Skip to content

Commit 247e1e1

Browse files
committed
fix: call exec_auth async
1 parent e25ad51 commit 247e1e1

File tree

2 files changed

+178
-64
lines changed

2 files changed

+178
-64
lines changed

src/exec_auth.ts

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@ export interface Credential {
2020
export class ExecAuth implements Authenticator {
2121
private readonly tokenCache: { [key: string]: Credential | null } = {};
2222
private execFn: (
23-
cmd: string,
24-
args: string[],
25-
opts: child_process.SpawnOptions,
26-
) => child_process.SpawnSyncReturns<Buffer> = child_process.spawnSync;
23+
command: string,
24+
args?: readonly string[],
25+
options?: child_process.SpawnOptionsWithoutStdio,
26+
) => child_process.ChildProcessWithoutNullStreams = child_process.spawn;
2727

2828
public isAuthProvider(user: User): boolean {
2929
if (!user) {
@@ -41,7 +41,7 @@ export class ExecAuth implements Authenticator {
4141
}
4242

4343
public async applyAuthentication(user: User, opts: https.RequestOptions): Promise<void> {
44-
const credential = this.getCredential(user);
44+
const credential = await this.getCredential(user);
4545
if (!credential) {
4646
return;
4747
}
@@ -70,7 +70,7 @@ export class ExecAuth implements Authenticator {
7070
return null;
7171
}
7272

73-
private getCredential(user: User): Credential | null {
73+
private async getCredential(user: User): Promise<Credential | null> {
7474
// TODO: Add a unit test for token caching.
7575
const cachedToken = this.tokenCache[user.name];
7676
if (cachedToken) {
@@ -99,15 +99,33 @@ export class ExecAuth implements Authenticator {
9999
exec.env.forEach((elt) => (env[elt.name] = elt.value));
100100
opts = { ...opts, env };
101101
}
102-
const result = this.execFn(exec.command, exec.args, opts);
103-
if (result.error) {
104-
throw result.error;
105-
}
106-
if (result.status === 0) {
107-
const obj = JSON.parse(result.stdout.toString('utf8')) as Credential;
108-
this.tokenCache[user.name] = obj;
109-
return obj;
110-
}
111-
throw new Error(result.stderr.toString('utf8'));
102+
103+
return new Promise((accept, reject) => {
104+
let stdoutData: string = '';
105+
let stderrData: string = '';
106+
107+
const subprocess = this.execFn(exec.command, exec.args, opts);
108+
109+
subprocess.stdout.on('data', (data: Buffer | string) => {
110+
stdoutData += data.toString('utf8');
111+
});
112+
113+
subprocess.stderr.on('data', (data) => {
114+
stderrData += data.toString('utf8');
115+
});
116+
117+
subprocess.on('error', (error) => {
118+
throw error;
119+
});
120+
121+
subprocess.on('close', (code) => {
122+
if (code !== 0) {
123+
throw new Error(stderrData);
124+
}
125+
const obj = JSON.parse(stdoutData) as Credential;
126+
this.tokenCache[user.name] = obj;
127+
accept(obj);
128+
});
129+
});
112130
}
113131
}

src/exec_auth_test.ts

Lines changed: 144 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,28 @@ describe('ExecAuth', () => {
6060
const auth = new ExecAuth();
6161
(auth as any).execFn = (
6262
command: string,
63-
args: string[],
64-
opts: child_process.SpawnOptions,
65-
): child_process.SpawnSyncReturns<Buffer> => {
63+
args?: readonly string[],
64+
options?: child_process.SpawnOptionsWithoutStdio,
65+
): child_process.ChildProcessWithoutNullStreams => {
6666
return {
67-
status: 0,
68-
stdout: Buffer.from(JSON.stringify({ status: { token: 'foo' } })),
69-
} as child_process.SpawnSyncReturns<Buffer>;
67+
stdout: {
68+
on: (_data: string, f: (data: Buffer | string) => void) => {
69+
f(Buffer.from(JSON.stringify({ status: { token: 'foo' } })));
70+
},
71+
},
72+
stderr: {
73+
on: () => {},
74+
},
75+
on: (op: string, f: any) => {
76+
if (op === 'close') {
77+
f(0);
78+
}
79+
},
80+
} as unknown as child_process.ChildProcessWithoutNullStreams;
7081
};
7182
const opts = {} as https.RequestOptions;
7283
opts.headers = {} as OutgoingHttpHeaders;
73-
auth.applyAuthentication(
84+
await auth.applyAuthentication(
7485
{
7586
name: 'user',
7687
authProvider: {
@@ -94,15 +105,30 @@ describe('ExecAuth', () => {
94105
const auth = new ExecAuth();
95106
(auth as any).execFn = (
96107
command: string,
97-
args: string[],
98-
opts: child_process.SpawnOptions,
99-
): child_process.SpawnSyncReturns<Buffer> => {
108+
args?: readonly string[],
109+
options?: child_process.SpawnOptionsWithoutStdio,
110+
): child_process.ChildProcessWithoutNullStreams => {
100111
return {
101-
status: 0,
102-
stdout: Buffer.from(
103-
JSON.stringify({ status: { clientCertificateData: 'foo', clientKeyData: 'bar' } }),
104-
),
105-
} as child_process.SpawnSyncReturns<Buffer>;
112+
stdout: {
113+
on: (_data: string, f: (data: Buffer | string) => void) => {
114+
f(
115+
Buffer.from(
116+
JSON.stringify({
117+
status: { clientCertificateData: 'foo', clientKeyData: 'bar' },
118+
}),
119+
),
120+
);
121+
},
122+
},
123+
stderr: {
124+
on: () => {},
125+
},
126+
on: (op: string, f: any) => {
127+
if (op === 'close') {
128+
f(0);
129+
}
130+
},
131+
} as unknown as child_process.ChildProcessWithoutNullStreams;
106132
};
107133

108134
const user = {
@@ -119,7 +145,7 @@ describe('ExecAuth', () => {
119145
opts.headers = {} as OutgoingHttpHeaders;
120146
opts.headers = {} as OutgoingHttpHeaders;
121147

122-
auth.applyAuthentication(user, opts);
148+
await auth.applyAuthentication(user, opts);
123149
expect(opts.headers.Authorization).to.be.undefined;
124150
expect(opts.cert).to.equal('foo');
125151
expect(opts.key).to.equal('bar');
@@ -136,18 +162,31 @@ describe('ExecAuth', () => {
136162
var tokenValue = 'foo';
137163
(auth as any).execFn = (
138164
command: string,
139-
args: string[],
140-
opts: child_process.SpawnOptions,
141-
): child_process.SpawnSyncReturns<Buffer> => {
165+
args?: readonly string[],
166+
options?: child_process.SpawnOptionsWithoutStdio,
167+
): child_process.ChildProcessWithoutNullStreams => {
142168
execCount++;
143169
return {
144-
status: 0,
145-
stdout: Buffer.from(
146-
JSON.stringify({
147-
status: { token: tokenValue, expirationTimestamp: expire },
148-
}),
149-
),
150-
} as child_process.SpawnSyncReturns<Buffer>;
170+
stdout: {
171+
on: (_data: string, f: (data: Buffer | string) => void) => {
172+
f(
173+
Buffer.from(
174+
JSON.stringify({
175+
status: { token: tokenValue, expirationTimestamp: expire },
176+
}),
177+
),
178+
);
179+
},
180+
},
181+
stderr: {
182+
on: () => {},
183+
},
184+
on: (op: string, f: any) => {
185+
if (op === 'close') {
186+
f(0);
187+
}
188+
},
189+
} as unknown as child_process.ChildProcessWithoutNullStreams;
151190
};
152191

153192
const user = {
@@ -207,6 +246,26 @@ describe('ExecAuth', () => {
207246
} as child_process.SpawnSyncReturns<Buffer>;
208247
};
209248

249+
(auth as any).execFn = (
250+
command: string,
251+
args?: readonly string[],
252+
options?: child_process.SpawnOptionsWithoutStdio,
253+
): child_process.ChildProcessWithoutNullStreams => {
254+
return {
255+
stdout: {
256+
on: (_data: string, f: (data: Buffer | string) => void) => {},
257+
},
258+
stderr: {
259+
on: () => {},
260+
},
261+
on: (op: string, f: any) => {
262+
if (op === 'error') {
263+
throw new Error('Error: spawnSync /path/to/bin ENOENT');
264+
}
265+
},
266+
} as unknown as child_process.ChildProcessWithoutNullStreams;
267+
};
268+
210269
const user = {
211270
name: 'user',
212271
authProvider: {
@@ -230,16 +289,29 @@ describe('ExecAuth', () => {
230289
return;
231290
}
232291
const auth = new ExecAuth();
292+
233293
(auth as any).execFn = (
234294
command: string,
235-
args: string[],
236-
opts: child_process.SpawnOptions,
237-
): child_process.SpawnSyncReturns<Buffer> => {
295+
args?: readonly string[],
296+
options?: child_process.SpawnOptionsWithoutStdio,
297+
): child_process.ChildProcessWithoutNullStreams => {
238298
return {
239-
status: 100,
240-
stdout: Buffer.from(JSON.stringify({ status: { token: 'foo' } })),
241-
stderr: Buffer.from('Some error!'),
242-
} as child_process.SpawnSyncReturns<Buffer>;
299+
stdout: {
300+
on: (_data: string, f: (data: Buffer | string) => void) => {
301+
f(Buffer.from(JSON.stringify({ status: { token: 'foo' } })));
302+
},
303+
},
304+
stderr: {
305+
on: (_data: string, f: (data: Buffer | string) => void) => {
306+
f(Buffer.from('Some error!'));
307+
},
308+
},
309+
on: (op: string, f: any) => {
310+
if (op === 'close') {
311+
f(100);
312+
}
313+
},
314+
} as unknown as child_process.ChildProcessWithoutNullStreams;
243315
};
244316

245317
const user = {
@@ -265,18 +337,30 @@ describe('ExecAuth', () => {
265337
return;
266338
}
267339
const auth = new ExecAuth();
268-
let optsOut: child_process.SpawnOptions = {};
340+
let optsOut: child_process.SpawnOptions | undefined = {};
269341
(auth as any).execFn = (
270342
command: string,
271-
args: string[],
272-
opts: child_process.SpawnOptions,
273-
): child_process.SpawnSyncReturns<Buffer> => {
274-
optsOut = opts;
343+
args?: readonly string[],
344+
options?: child_process.SpawnOptionsWithoutStdio,
345+
): child_process.ChildProcessWithoutNullStreams => {
346+
optsOut = options;
275347
return {
276-
status: 0,
277-
stdout: Buffer.from(JSON.stringify({ status: { token: 'foo' } })),
278-
} as child_process.SpawnSyncReturns<Buffer>;
348+
stdout: {
349+
on: (_data: string, f: (data: Buffer | string) => void) => {
350+
f(Buffer.from(JSON.stringify({ status: { token: 'foo' } })));
351+
},
352+
},
353+
stderr: {
354+
on: () => {},
355+
},
356+
on: (op: string, f: any) => {
357+
if (op === 'close') {
358+
f(0);
359+
}
360+
},
361+
} as unknown as child_process.ChildProcessWithoutNullStreams;
279362
};
363+
280364
process.env.BLABBLE = 'flubble';
281365
const opts = {} as https.RequestOptions;
282366
opts.headers = {} as OutgoingHttpHeaders;
@@ -313,16 +397,28 @@ describe('ExecAuth', () => {
313397
const auth = new ExecAuth();
314398
(auth as any).execFn = (
315399
command: string,
316-
args: string[],
317-
opts: child_process.SpawnOptions,
318-
): child_process.SpawnSyncReturns<Buffer> => {
400+
args?: readonly string[],
401+
options?: child_process.SpawnOptionsWithoutStdio,
402+
): child_process.ChildProcessWithoutNullStreams => {
319403
return {
320-
status: 0,
321-
stdout: Buffer.from(JSON.stringify({ status: { token: 'foo' } })),
322-
} as child_process.SpawnSyncReturns<Buffer>;
404+
stdout: {
405+
on: (_data: string, f: (data: Buffer | string) => void) => {
406+
f(Buffer.from(JSON.stringify({ status: { token: 'foo' } })));
407+
},
408+
},
409+
stderr: {
410+
on: () => {},
411+
},
412+
on: (op: string, f: any) => {
413+
if (op === 'close') {
414+
f(0);
415+
}
416+
},
417+
} as unknown as child_process.ChildProcessWithoutNullStreams;
323418
};
419+
324420
const opts = {} as https.RequestOptions;
325-
auth.applyAuthentication(
421+
await auth.applyAuthentication(
326422
{
327423
name: 'user',
328424
authProvider: {

0 commit comments

Comments
 (0)