diff --git a/src/cloud_auth.ts b/src/azure_auth.ts similarity index 95% rename from src/cloud_auth.ts rename to src/azure_auth.ts index 5fb36abe83..df2f7b70c1 100644 --- a/src/cloud_auth.ts +++ b/src/azure_auth.ts @@ -18,12 +18,12 @@ interface Config { ['expiry-key']: string; ['access-token']?: string; } -export class CloudAuth implements Authenticator { +export class AzureAuth implements Authenticator { public isAuthProvider(user: User): boolean { if (!user || !user.authProvider) { return false; } - return user.authProvider.name === 'azure' || user.authProvider.name === 'gcp'; + return user.authProvider.name === 'azure'; } public async applyAuthentication( diff --git a/src/azure_auth_test.ts b/src/azure_auth_test.ts new file mode 100644 index 0000000000..97a5d95232 --- /dev/null +++ b/src/azure_auth_test.ts @@ -0,0 +1,288 @@ +import { expect } from 'chai'; +import * as requestlib from 'request'; +import { join } from 'path'; + +import { User, Cluster } from './config_types'; +import { AzureAuth } from './azure_auth'; +import { KubeConfig } from './config'; + +describe('AzureAuth', () => { + var auth: AzureAuth; + beforeEach(() => { + auth = new AzureAuth(); + }); + + it('should be true for azure user', () => { + const user = { + authProvider: { + name: 'azure', + }, + } as User; + + expect(auth.isAuthProvider(user)).to.equal(true); + }); + + it('should be false for other user', () => { + const user = { + authProvider: { + name: 'gcp', + }, + } as User; + + expect(auth.isAuthProvider(user)).to.equal(false); + }); + + it('should be false for null user.authProvider', () => { + const user = {} as User; + + expect(auth.isAuthProvider(user)).to.equal(false); + }); + + it('should populate from auth provider', async () => { + const config = new KubeConfig(); + const token = 'token'; + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'azure', + config: { + 'access-token': token, + expiry: 'Fri Aug 24 07:32:05 PDT 3018', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + + await config.applyToRequest(opts); + expect(opts.headers).to.not.be.undefined; + if (opts.headers) { + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + } + opts.headers = []; + opts.headers.Host = 'foo.com'; + await config.applyToRequest(opts); + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + }); + + it('should populate from auth provider without expirty', async () => { + const config = new KubeConfig(); + const token = 'token'; + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'azure', + config: { + 'access-token': token, + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + + await config.applyToRequest(opts); + expect(opts.headers).to.not.be.undefined; + if (opts.headers) { + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + } + }); + + it('should populate rejectUnauthorized=false when skipTLSVerify is set', async () => { + const config = new KubeConfig(); + const token = 'token'; + config.loadFromClusterAndUser( + { skipTLSVerify: true } as Cluster, + { + authProvider: { + name: 'azure', + config: { + 'access-token': token, + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + + await config.applyToRequest(opts); + expect(opts.rejectUnauthorized).to.equal(false); + }); + + it('should not set rejectUnauthorized if skipTLSVerify is not set', async () => { + // This test is just making 100% sure we validate certs unless we explictly set + // skipTLSVerify = true + const config = new KubeConfig(); + const token = 'token'; + config.loadFromClusterAndUser( + {} as Cluster, + { + authProvider: { + name: 'azure', + config: { + 'access-token': token, + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + + await config.applyToRequest(opts); + expect(opts.rejectUnauthorized).to.equal(undefined); + }); + + it('should throw with expired token and no cmd', () => { + const config = new KubeConfig(); + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'azure', + config: { + expiry: 'Aug 24 07:32:05 PDT 2017', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + + return expect(config.applyToRequest(opts)).to.eventually.be.rejectedWith('Token is expired!'); + }); + + it('should throw with bad command', () => { + const config = new KubeConfig(); + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'azure', + config: { + 'access-token': 'token', + expiry: 'Aug 24 07:32:05 PDT 2017', + 'cmd-path': 'non-existent-command', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + return expect(config.applyToRequest(opts)).to.eventually.be.rejectedWith(/Failed to refresh token/); + }); + + it('should exec with expired token', async () => { + // TODO: fix this test for Windows + if (process.platform === 'win32') { + return; + } + const config = new KubeConfig(); + const token = 'token'; + const responseStr = `{"token":{"accessToken":"${token}"}}`; + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'azure', + config: { + expiry: 'Aug 24 07:32:05 PDT 2017', + 'cmd-path': 'echo', + 'cmd-args': `'${responseStr}'`, + 'token-key': '{.token.accessToken}', + 'expiry-key': '{.token.token_expiry}', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + await config.applyToRequest(opts); + expect(opts.headers).to.not.be.undefined; + if (opts.headers) { + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + } + }); + it('should exec without access-token', async () => { + // TODO: fix this test for Windows + if (process.platform === 'win32') { + return; + } + const config = new KubeConfig(); + const token = 'token'; + const responseStr = `{"token":{"accessToken":"${token}"}}`; + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'azure', + config: { + 'cmd-path': 'echo', + 'cmd-args': `'${responseStr}'`, + 'token-key': '{.token.accessToken}', + 'expiry-key': '{.token.token_expiry}', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + await config.applyToRequest(opts); + expect(opts.headers).to.not.be.undefined; + if (opts.headers) { + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + } + }); + it('should exec without access-token', async () => { + // TODO: fix this test for Windows + if (process.platform === 'win32') { + return; + } + const config = new KubeConfig(); + const token = 'token'; + const responseStr = `{"token":{"accessToken":"${token}"}}`; + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'azure', + config: { + 'cmd-path': 'echo', + 'cmd-args': `'${responseStr}'`, + 'token-key': '{.token.accessToken}', + 'expiry-key': '{.token.token_expiry}', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + await config.applyToRequest(opts); + expect(opts.headers).to.not.be.undefined; + if (opts.headers) { + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + } + }); + it('should exec succesfully with spaces in cmd', async () => { + // TODO: fix this test for Windows + if (process.platform === 'win32') { + return; + } + const config = new KubeConfig(); + const token = 'token'; + const responseStr = `{"token":{"accessToken":"${token}"}}`; + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'azure', + config: { + 'cmd-path': join(__dirname, '..', 'test', 'echo space.js'), + 'cmd-args': `'${responseStr}'`, + 'token-key': '{.token.accessToken}', + 'expiry-key': '{.token.token_expiry}', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + await config.applyToRequest(opts); + expect(opts.headers).to.not.be.undefined; + if (opts.headers) { + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + } + }); +}); diff --git a/src/config.ts b/src/config.ts index b9b941d484..9dfee8e729 100644 --- a/src/config.ts +++ b/src/config.ts @@ -10,7 +10,7 @@ import shelljs = require('shelljs'); import * as api from './api'; import { Authenticator } from './auth'; -import { CloudAuth } from './cloud_auth'; +import { AzureAuth } from './azure_auth'; import { Cluster, ConfigOptions, @@ -25,6 +25,7 @@ import { } from './config_types'; import { ExecAuth } from './exec_auth'; import { FileAuth } from './file_auth'; +import { GoogleCloudPlatformAuth } from './gcp_auth'; import { OpenIDConnectAuth } from './oidc_auth'; // fs.existsSync was removed in node 10 @@ -39,7 +40,8 @@ function fileExists(filepath: string): boolean { export class KubeConfig { private static authenticators: Authenticator[] = [ - new CloudAuth(), + new AzureAuth(), + new GoogleCloudPlatformAuth(), new ExecAuth(), new FileAuth(), new OpenIDConnectAuth(), diff --git a/src/config_test.ts b/src/config_test.ts index 8798f71fde..73b3f7026f 100644 --- a/src/config_test.ts +++ b/src/config_test.ts @@ -1,19 +1,16 @@ import { readFileSync } from 'fs'; import * as https from 'https'; -import { dirname, join } from 'path'; +import { join } from 'path'; import { expect } from 'chai'; import mockfs = require('mock-fs'); import * as path from 'path'; import * as requestlib from 'request'; -import * as filesystem from 'fs'; -import { fs } from 'mock-fs'; -import * as os from 'os'; import { CoreV1Api } from './api'; import { bufferFromFileOrString, findHomeDir, findObject, KubeConfig, makeAbsolutePath } from './config'; import { Cluster, newClusters, newContexts, newUsers, User, ActionOnInvalid } from './config_types'; -import { isUndefined } from 'util'; +import { ExecAuth } from './exec_auth'; const kcFileName = 'testdata/kubeconfig.yaml'; const kc2FileName = 'testdata/kubeconfig-2.yaml'; @@ -619,255 +616,6 @@ describe('KubeConfig', () => { expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); } }); - it('should populate from auth provider', async () => { - const config = new KubeConfig(); - const token = 'token'; - config.loadFromClusterAndUser( - { skipTLSVerify: false } as Cluster, - { - authProvider: { - name: 'azure', - config: { - 'access-token': token, - expiry: 'Fri Aug 24 07:32:05 PDT 3018', - }, - }, - } as User, - ); - const opts = {} as requestlib.Options; - - await config.applyToRequest(opts); - expect(opts.headers).to.not.be.undefined; - if (opts.headers) { - expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); - } - opts.headers = []; - opts.headers.Host = 'foo.com'; - await config.applyToRequest(opts); - expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); - }); - - it('should populate from auth provider without expirty', async () => { - const config = new KubeConfig(); - const token = 'token'; - config.loadFromClusterAndUser( - { skipTLSVerify: false } as Cluster, - { - authProvider: { - name: 'azure', - config: { - 'access-token': token, - }, - }, - } as User, - ); - const opts = {} as requestlib.Options; - - await config.applyToRequest(opts); - expect(opts.headers).to.not.be.undefined; - if (opts.headers) { - expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); - } - }); - - it('should populate rejectUnauthorized=false when skipTLSVerify is set', async () => { - const config = new KubeConfig(); - const token = 'token'; - config.loadFromClusterAndUser( - { skipTLSVerify: true } as Cluster, - { - authProvider: { - name: 'azure', - config: { - 'access-token': token, - }, - }, - } as User, - ); - const opts = {} as requestlib.Options; - - await config.applyToRequest(opts); - expect(opts.rejectUnauthorized).to.equal(false); - }); - - it('should not set rejectUnauthorized if skipTLSVerify is not set', async () => { - // This test is just making 100% sure we validate certs unless we explictly set - // skipTLSVerify = true - const config = new KubeConfig(); - const token = 'token'; - config.loadFromClusterAndUser( - {} as Cluster, - { - authProvider: { - name: 'azure', - config: { - 'access-token': token, - }, - }, - } as User, - ); - const opts = {} as requestlib.Options; - - await config.applyToRequest(opts); - expect(opts.rejectUnauthorized).to.equal(undefined); - }); - - it('should throw with expired token and no cmd', () => { - const config = new KubeConfig(); - config.loadFromClusterAndUser( - { skipTLSVerify: false } as Cluster, - { - authProvider: { - name: 'azure', - config: { - expiry: 'Aug 24 07:32:05 PDT 2017', - }, - }, - } as User, - ); - const opts = {} as requestlib.Options; - - return expect(config.applyToRequest(opts)).to.eventually.be.rejectedWith('Token is expired!'); - }); - - it('should throw with bad command', () => { - const config = new KubeConfig(); - config.loadFromClusterAndUser( - { skipTLSVerify: false } as Cluster, - { - authProvider: { - name: 'azure', - config: { - 'access-token': 'token', - expiry: 'Aug 24 07:32:05 PDT 2017', - 'cmd-path': 'non-existent-command', - }, - }, - } as User, - ); - const opts = {} as requestlib.Options; - return expect(config.applyToRequest(opts)).to.eventually.be.rejectedWith( - /Failed to refresh token/, - ); - }); - - it('should exec with expired token', async () => { - // TODO: fix this test for Windows - if (process.platform === 'win32') { - return; - } - const config = new KubeConfig(); - const token = 'token'; - const responseStr = `{"token":{"accessToken":"${token}"}}`; - config.loadFromClusterAndUser( - { skipTLSVerify: false } as Cluster, - { - authProvider: { - name: 'azure', - config: { - expiry: 'Aug 24 07:32:05 PDT 2017', - 'cmd-path': 'echo', - 'cmd-args': `'${responseStr}'`, - 'token-key': '{.token.accessToken}', - 'expiry-key': '{.token.token_expiry}', - }, - }, - } as User, - ); - const opts = {} as requestlib.Options; - await config.applyToRequest(opts); - expect(opts.headers).to.not.be.undefined; - if (opts.headers) { - expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); - } - }); - it('should exec without access-token', async () => { - // TODO: fix this test for Windows - if (process.platform === 'win32') { - return; - } - const config = new KubeConfig(); - const token = 'token'; - const responseStr = `{"token":{"accessToken":"${token}"}}`; - config.loadFromClusterAndUser( - { skipTLSVerify: false } as Cluster, - { - authProvider: { - name: 'azure', - config: { - 'cmd-path': 'echo', - 'cmd-args': `'${responseStr}'`, - 'token-key': '{.token.accessToken}', - 'expiry-key': '{.token.token_expiry}', - }, - }, - } as User, - ); - const opts = {} as requestlib.Options; - await config.applyToRequest(opts); - expect(opts.headers).to.not.be.undefined; - if (opts.headers) { - expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); - } - }); - it('should exec without access-token', async () => { - // TODO: fix this test for Windows - if (process.platform === 'win32') { - return; - } - const config = new KubeConfig(); - const token = 'token'; - const responseStr = `{"token":{"accessToken":"${token}"}}`; - config.loadFromClusterAndUser( - { skipTLSVerify: false } as Cluster, - { - authProvider: { - name: 'azure', - config: { - 'cmd-path': 'echo', - 'cmd-args': `'${responseStr}'`, - 'token-key': '{.token.accessToken}', - 'expiry-key': '{.token.token_expiry}', - }, - }, - } as User, - ); - const opts = {} as requestlib.Options; - await config.applyToRequest(opts); - expect(opts.headers).to.not.be.undefined; - if (opts.headers) { - expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); - } - }); - it('should exec succesfully with spaces in cmd', async () => { - // TODO: fix this test for Windows - if (process.platform === 'win32') { - return; - } - const config = new KubeConfig(); - const token = 'token'; - const responseStr = `{"token":{"accessToken":"${token}"}}`; - config.loadFromClusterAndUser( - { skipTLSVerify: false } as Cluster, - { - authProvider: { - name: 'azure', // applies to gcp too as they are both handled by CloudAuth class - config: { - 'cmd-path': path.join(__dirname, '..', 'test', 'echo space.js'), - 'cmd-args': `'${responseStr}'`, - 'token-key': '{.token.accessToken}', - 'expiry-key': '{.token.token_expiry}', - }, - }, - } as User, - ); - const opts = {} as requestlib.Options; - await config.applyToRequest(opts); - expect(opts.headers).to.not.be.undefined; - if (opts.headers) { - expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); - } - }); it('should exec with exec auth and env vars', async () => { // TODO: fix this test for Windows if (process.platform === 'win32') { @@ -998,9 +746,10 @@ describe('KubeConfig', () => { // TODO: inject the exec command here? const opts = {} as requestlib.Options; await config.applyToRequest(opts); - expect((KubeConfig as any).authenticators[1].tokenCache['exec']).to.deep.equal( - JSON.parse(responseStr), + let execAuthenticator = (KubeConfig as any).authenticators.find( + (authenticator) => authenticator instanceof ExecAuth, ); + expect(execAuthenticator.tokenCache['exec']).to.deep.equal(JSON.parse(responseStr)); }); it('should throw with no command.', () => { diff --git a/src/gcp_auth.ts b/src/gcp_auth.ts new file mode 100644 index 0000000000..f2ee0792fa --- /dev/null +++ b/src/gcp_auth.ts @@ -0,0 +1,96 @@ +import * as proc from 'child_process'; +import https = require('https'); +import * as jsonpath from 'jsonpath-plus'; +import request = require('request'); + +import { Authenticator } from './auth'; +import { User } from './config_types'; + +/* FIXME: maybe we can extend the User and User.authProvider type to have a proper type. +Currently user.authProvider has `any` type and so we don't have a type for user.authProvider.config. +We therefore define its type here +*/ +interface Config { + expiry: string; + ['cmd-args']?: string; + ['cmd-path']?: string; + ['token-key']: string; + ['expiry-key']: string; + ['access-token']?: string; +} +export class GoogleCloudPlatformAuth implements Authenticator { + public isAuthProvider(user: User): boolean { + if (!user || !user.authProvider) { + return false; + } + return user.authProvider.name === 'gcp'; + } + + public async applyAuthentication( + user: User, + opts: request.Options | https.RequestOptions, + ): Promise { + const token = this.getToken(user); + if (token) { + opts.headers!.Authorization = `Bearer ${token}`; + } + } + + private getToken(user: User): string | null { + const config = user.authProvider.config; + if (this.isExpired(config)) { + this.updateAccessToken(config); + } + return config['access-token']; + } + + private isExpired(config: Config): boolean { + const token = config['access-token']; + const expiry = config.expiry; + if (!token) { + return true; + } + if (!expiry) { + return false; + } + + const expiration = Date.parse(expiry); + if (expiration < Date.now()) { + return true; + } + return false; + } + + private updateAccessToken(config: Config): void { + let cmd = config['cmd-path']; + if (!cmd) { + throw new Error('Token is expired!'); + } + // Wrap cmd in quotes to make it cope with spaces in path + cmd = `"${cmd}"`; + const args = config['cmd-args']; + if (args) { + cmd = cmd + ' ' + args; + } + // TODO: Cache to file? + // TODO: do this asynchronously + let output: any; + try { + output = proc.execSync(cmd); + } catch (err) { + throw new Error('Failed to refresh token: ' + err.message); + } + + const resultObj = JSON.parse(output); + + const tokenPathKeyInConfig = config['token-key']; + const expiryPathKeyInConfig = config['expiry-key']; + + // Format in file is {}, so slice it out and add '$' + const tokenPathKey = '$' + tokenPathKeyInConfig.slice(1, -1); + const expiryPathKey = '$' + expiryPathKeyInConfig.slice(1, -1); + + config['access-token'] = jsonpath.JSONPath(tokenPathKey, resultObj); + config.expiry = jsonpath.JSONPath(expiryPathKey, resultObj); + } +} diff --git a/src/gcp_auth_test.ts b/src/gcp_auth_test.ts new file mode 100644 index 0000000000..04f0a438cb --- /dev/null +++ b/src/gcp_auth_test.ts @@ -0,0 +1,288 @@ +import { expect } from 'chai'; +import * as requestlib from 'request'; +import { join } from 'path'; + +import { User, Cluster } from './config_types'; +import { GoogleCloudPlatformAuth } from './gcp_auth'; +import { KubeConfig } from './config'; + +describe('GoogleCloudPlatformAuth', () => { + var auth: GoogleCloudPlatformAuth; + beforeEach(() => { + auth = new GoogleCloudPlatformAuth(); + }); + + it('should be true for gcp user', () => { + const user = { + authProvider: { + name: 'gcp', + }, + } as User; + + expect(auth.isAuthProvider(user)).to.equal(true); + }); + + it('should be false for other user', () => { + const user = { + authProvider: { + name: 'azure', + }, + } as User; + + expect(auth.isAuthProvider(user)).to.equal(false); + }); + + it('should be false for null user.authProvider', () => { + const user = {} as User; + + expect(auth.isAuthProvider(user)).to.equal(false); + }); + + it('should populate from auth provider', async () => { + const config = new KubeConfig(); + const token = 'token'; + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'gcp', + config: { + 'access-token': token, + expiry: 'Fri Aug 24 07:32:05 PDT 3018', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + + await config.applyToRequest(opts); + expect(opts.headers).to.not.be.undefined; + if (opts.headers) { + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + } + opts.headers = []; + opts.headers.Host = 'foo.com'; + await config.applyToRequest(opts); + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + }); + + it('should populate from auth provider without expirty', async () => { + const config = new KubeConfig(); + const token = 'token'; + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'gcp', + config: { + 'access-token': token, + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + + await config.applyToRequest(opts); + expect(opts.headers).to.not.be.undefined; + if (opts.headers) { + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + } + }); + + it('should populate rejectUnauthorized=false when skipTLSVerify is set', async () => { + const config = new KubeConfig(); + const token = 'token'; + config.loadFromClusterAndUser( + { skipTLSVerify: true } as Cluster, + { + authProvider: { + name: 'gcp', + config: { + 'access-token': token, + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + + await config.applyToRequest(opts); + expect(opts.rejectUnauthorized).to.equal(false); + }); + + it('should not set rejectUnauthorized if skipTLSVerify is not set', async () => { + // This test is just making 100% sure we validate certs unless we explictly set + // skipTLSVerify = true + const config = new KubeConfig(); + const token = 'token'; + config.loadFromClusterAndUser( + {} as Cluster, + { + authProvider: { + name: 'gcp', + config: { + 'access-token': token, + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + + await config.applyToRequest(opts); + expect(opts.rejectUnauthorized).to.equal(undefined); + }); + + it('should throw with expired token and no cmd', () => { + const config = new KubeConfig(); + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'gcp', + config: { + expiry: 'Aug 24 07:32:05 PDT 2017', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + + return expect(config.applyToRequest(opts)).to.eventually.be.rejectedWith('Token is expired!'); + }); + + it('should throw with bad command', () => { + const config = new KubeConfig(); + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'gcp', + config: { + 'access-token': 'token', + expiry: 'Aug 24 07:32:05 PDT 2017', + 'cmd-path': 'non-existent-command', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + return expect(config.applyToRequest(opts)).to.eventually.be.rejectedWith(/Failed to refresh token/); + }); + + it('should exec with expired token', async () => { + // TODO: fix this test for Windows + if (process.platform === 'win32') { + return; + } + const config = new KubeConfig(); + const token = 'token'; + const responseStr = `{"token":{"accessToken":"${token}"}}`; + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'gcp', + config: { + expiry: 'Aug 24 07:32:05 PDT 2017', + 'cmd-path': 'echo', + 'cmd-args': `'${responseStr}'`, + 'token-key': '{.token.accessToken}', + 'expiry-key': '{.token.token_expiry}', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + await config.applyToRequest(opts); + expect(opts.headers).to.not.be.undefined; + if (opts.headers) { + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + } + }); + it('should exec without access-token', async () => { + // TODO: fix this test for Windows + if (process.platform === 'win32') { + return; + } + const config = new KubeConfig(); + const token = 'token'; + const responseStr = `{"token":{"accessToken":"${token}"}}`; + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'gcp', + config: { + 'cmd-path': 'echo', + 'cmd-args': `'${responseStr}'`, + 'token-key': '{.token.accessToken}', + 'expiry-key': '{.token.token_expiry}', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + await config.applyToRequest(opts); + expect(opts.headers).to.not.be.undefined; + if (opts.headers) { + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + } + }); + it('should exec without access-token', async () => { + // TODO: fix this test for Windows + if (process.platform === 'win32') { + return; + } + const config = new KubeConfig(); + const token = 'token'; + const responseStr = `{"token":{"accessToken":"${token}"}}`; + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'gcp', + config: { + 'cmd-path': 'echo', + 'cmd-args': `'${responseStr}'`, + 'token-key': '{.token.accessToken}', + 'expiry-key': '{.token.token_expiry}', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + await config.applyToRequest(opts); + expect(opts.headers).to.not.be.undefined; + if (opts.headers) { + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + } + }); + it('should exec succesfully with spaces in cmd', async () => { + // TODO: fix this test for Windows + if (process.platform === 'win32') { + return; + } + const config = new KubeConfig(); + const token = 'token'; + const responseStr = `{"token":{"accessToken":"${token}"}}`; + config.loadFromClusterAndUser( + { skipTLSVerify: false } as Cluster, + { + authProvider: { + name: 'gcp', + config: { + 'cmd-path': join(__dirname, '..', 'test', 'echo space.js'), + 'cmd-args': `'${responseStr}'`, + 'token-key': '{.token.accessToken}', + 'expiry-key': '{.token.token_expiry}', + }, + }, + } as User, + ); + const opts = {} as requestlib.Options; + await config.applyToRequest(opts); + expect(opts.headers).to.not.be.undefined; + if (opts.headers) { + expect(opts.headers.Authorization).to.equal(`Bearer ${token}`); + } + }); +});