Skip to content

Commit 35df1ad

Browse files
committed
Merge branch 'master' into fix/refresh-token-if-empty
2 parents 8e3ba7f + a4f9d70 commit 35df1ad

File tree

5 files changed

+117
-11
lines changed

5 files changed

+117
-11
lines changed

src/config.ts

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ import { CloudAuth } from './cloud_auth';
1313
import { Cluster, Context, newClusters, newContexts, newUsers, User } from './config_types';
1414
import { ExecAuth } from './exec_auth';
1515

16+
// fs.existsSync was removed in node 10
17+
function fileExists(filepath: string): boolean {
18+
try {
19+
fs.accessSync(filepath);
20+
return true;
21+
// tslint:disable-next-line:no-empty
22+
} catch (ignore) { }
23+
return false;
24+
}
25+
1626
export class KubeConfig {
1727
private static authenticators: Authenticator[] = [
1828
new CloudAuth(),
@@ -197,12 +207,10 @@ export class KubeConfig {
197207
const home = findHomeDir();
198208
if (home) {
199209
const config = path.join(home, '.kube', 'config');
200-
try {
201-
fs.accessSync(config);
210+
if (fileExists(config)) {
202211
this.loadFromFile(config);
203212
return;
204-
// tslint:disable-next-line:no-empty
205-
} catch (ignore) {}
213+
}
206214
}
207215
if (process.platform === 'win32' && shelljs.which('wsl.exe')) {
208216
// TODO: Handle if someome set $KUBECONFIG in wsl here...
@@ -213,12 +221,10 @@ export class KubeConfig {
213221
}
214222
}
215223

216-
try {
217-
fs.accessSync(Config.SERVICEACCOUNT_TOKEN_PATH);
224+
if (fileExists(Config.SERVICEACCOUNT_TOKEN_PATH)) {
218225
this.loadFromCluster();
219226
return;
220-
// tslint:disable-next-line:no-empty
221-
} catch (ignore) {}
227+
}
222228

223229
this.loadFromClusterAndUser(
224230
{ name: 'cluster', server: 'http://localhost:8080' } as Cluster,

src/config_test.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -702,7 +702,7 @@ describe('KubeConfig', () => {
702702
it('should exec with exec auth and env vars', () => {
703703
const config = new KubeConfig();
704704
const token = 'token';
705-
const responseStr = `'{ "token": "${token}" }'`;
705+
const responseStr = `'{"status": { "token": "${token}" }}'`;
706706
config.loadFromClusterAndUser(
707707
{ skipTLSVerify: false } as Cluster,
708708
{
@@ -733,7 +733,13 @@ describe('KubeConfig', () => {
733733
it('should exec with exec auth', () => {
734734
const config = new KubeConfig();
735735
const token = 'token';
736-
const responseStr = `'{ "token": "${token}" }'`;
736+
const responseStr = `'{
737+
"apiVersion": "client.authentication.k8s.io/v1beta1",
738+
"kind": "ExecCredential",
739+
"status": {
740+
"token": "${token}"
741+
}
742+
}'`;
737743
config.loadFromClusterAndUser(
738744
{ skipTLSVerify: false } as Cluster,
739745
{

src/exec_auth.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,26 @@ import { Authenticator } from './auth';
44
import { User } from './config_types';
55

66
export class ExecAuth implements Authenticator {
7+
private readonly tokenCache: { [key: string]: any } = {};
8+
79
public isAuthProvider(user: User) {
810
return user.authProvider.name === 'exec' ||
911
(user.authProvider.config && user.authProvider.config.exec);
1012
}
1113

1214
public getToken(user: User): string | null {
15+
// TODO: Handle client cert auth here, requires auth refactor.
16+
// See https://kubernetes.io/docs/reference/access-authn-authz/authentication/#input-and-output-formats
17+
// for details on this protocol.
18+
// TODO: Add a unit test for token caching.
19+
const cachedToken = this.tokenCache[user.name];
20+
if (cachedToken) {
21+
const date = Date.parse(cachedToken.status.expirationTimestamp);
22+
if (date < Date.now()) {
23+
return `Bearer ${cachedToken.status.token}`;
24+
}
25+
this.tokenCache[user.name] = null;
26+
}
1327
const config = user.authProvider.config;
1428
if (!config.exec.command) {
1529
throw new Error('No command was specified for exec authProvider!');
@@ -27,7 +41,8 @@ export class ExecAuth implements Authenticator {
2741
const result = shell.exec(cmd, opts);
2842
if (result.code === 0) {
2943
const obj = JSON.parse(result.stdout);
30-
return `Bearer ${obj.token}`;
44+
this.tokenCache[user.name] = obj;
45+
return `Bearer ${obj.status.token}`;
3146
}
3247
throw new Error(result.stderr);
3348
}

src/oidc_auth.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { Authenticator } from './auth';
2+
import { User } from './config_types';
3+
4+
export class OpenIDConnectAuth implements Authenticator {
5+
public isAuthProvider(user: User): boolean {
6+
if (!user.authProvider) {
7+
return false;
8+
}
9+
return user.authProvider.name === 'oidc';
10+
}
11+
12+
public getToken(user: User): string | null {
13+
if (!user.authProvider.config || !user.authProvider.config['id-token']) {
14+
return null;
15+
}
16+
// TODO: Handle expiration and refresh here...
17+
// TODO: Extract the 'Bearer ' to config.ts?
18+
return `Bearer ${user.authProvider.config['id-token']}`;
19+
}
20+
}

src/oidc_auth_test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import { expect } from 'chai';
2+
3+
import { User } from './config_types';
4+
import { OpenIDConnectAuth } from './oidc_auth';
5+
6+
describe('OIDCAuth', () => {
7+
const auth = new OpenIDConnectAuth();
8+
it('should be true for oidc user', () => {
9+
const user = {
10+
authProvider: {
11+
name: 'oidc',
12+
},
13+
} as User;
14+
15+
expect(auth.isAuthProvider(user)).to.equal(true);
16+
});
17+
18+
it('should be false for other user', () => {
19+
const user = {
20+
authProvider: {
21+
name: 'azure',
22+
},
23+
} as User;
24+
25+
expect(auth.isAuthProvider(user)).to.equal(false);
26+
});
27+
28+
it('should be false for null user.authProvider', () => {
29+
const user = {} as User;
30+
31+
expect(auth.isAuthProvider(user)).to.equal(false);
32+
});
33+
34+
it('get a token if present', () => {
35+
const token = 'some token';
36+
const user = {
37+
authProvider: {
38+
name: 'oidc',
39+
config: {
40+
'id-token': token,
41+
},
42+
},
43+
} as User;
44+
45+
expect(auth.getToken(user)).to.equal(`Bearer ${token}`);
46+
});
47+
48+
it('get null if token missing', () => {
49+
const user = {
50+
authProvider: {
51+
name: 'oidc',
52+
config: {
53+
},
54+
},
55+
} as User;
56+
57+
expect(auth.getToken(user)).to.equal(null);
58+
});
59+
});

0 commit comments

Comments
 (0)