vvauth 2.3.0 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +6 -12
- package/package.json +1 -1
- package/test/token-cache.js +60 -4
package/index.js
CHANGED
|
@@ -9,7 +9,6 @@ const {createHash} = require('crypto');
|
|
|
9
9
|
const {encrypt, decrypt} = require('ssh-agent-crypt');
|
|
10
10
|
const {spawn, execFileSync} = require('child_process');
|
|
11
11
|
const passthru = require('nyks/child_process/passthru');
|
|
12
|
-
const wait = require('nyks/child_process/wait');
|
|
13
12
|
const boolPrompt = require('cnyks/prompt/bool');
|
|
14
13
|
|
|
15
14
|
const {parse} = require('yaml');
|
|
@@ -160,6 +159,11 @@ class vvauth {
|
|
|
160
159
|
}
|
|
161
160
|
|
|
162
161
|
async connect(renew = false) {
|
|
162
|
+
const {ssh_auth, jwt_auth} = this.rc;
|
|
163
|
+
if(!ssh_auth && !jwt_auth) {
|
|
164
|
+
this.VAULT_TOKEN = undefined;
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
163
167
|
if(this._connected && !renew)
|
|
164
168
|
return this.VAULT_TOKEN;
|
|
165
169
|
this._connected = false;
|
|
@@ -199,7 +203,6 @@ class vvauth {
|
|
|
199
203
|
}
|
|
200
204
|
|
|
201
205
|
if(!token) {
|
|
202
|
-
const {ssh_auth, jwt_auth} = this.rc;
|
|
203
206
|
const agent_path = process.env.SSH_AUTH_SOCK || (process.platform === 'win32' ? 'pageant' : null);
|
|
204
207
|
if(ssh_auth && agent_path) {
|
|
205
208
|
metadata = await this._login_vault_ssh({...ssh_auth}, agent_path);
|
|
@@ -303,16 +306,7 @@ class vvauth {
|
|
|
303
306
|
let {'ssh-agent-crypt' : agent } = this.rc;
|
|
304
307
|
if(agent) {
|
|
305
308
|
const {path, identity} = agent;
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
child.stdin.end(fs.readFileSync(path));
|
|
309
|
-
child.stderr.pipe(process.stderr);
|
|
310
|
-
|
|
311
|
-
const [exit, body] = await Promise.all([wait(child, false), drain(child.stdout)]);
|
|
312
|
-
if(exit !== 0) {
|
|
313
|
-
console.error("Could not expand armored %s using %s", path, identity);
|
|
314
|
-
process.exit();
|
|
315
|
-
}
|
|
309
|
+
const body = await decrypt(fs.readFileSync(path), identity);
|
|
316
310
|
const result = JSON.parse(body);
|
|
317
311
|
secrets = {...secrets, ...result};
|
|
318
312
|
}
|
package/package.json
CHANGED
package/test/token-cache.js
CHANGED
|
@@ -256,13 +256,15 @@ test('an unwritable cache does not prevent login', async t => {
|
|
|
256
256
|
assert.equal(await client().connect(), 'fresh-token');
|
|
257
257
|
});
|
|
258
258
|
|
|
259
|
-
test('
|
|
259
|
+
test('without an auth method inherited tokens and caches are ignored', async t => {
|
|
260
260
|
const {client, seed} = await fixture(t, {lookup_status : 403});
|
|
261
261
|
const instance = client();
|
|
262
262
|
instance.rc = {};
|
|
263
263
|
await seed(instance);
|
|
264
264
|
instance.VAULT_TOKEN = 'uncached-token';
|
|
265
|
-
|
|
265
|
+
instance._read_token_cache = async () => { throw new Error('No auth: no cache lookup'); };
|
|
266
|
+
assert.equal(await instance.connect(), undefined);
|
|
267
|
+
assert.equal(instance.VAULT_TOKEN, undefined);
|
|
266
268
|
});
|
|
267
269
|
|
|
268
270
|
test('--renew ignores both environment and cached tokens and saves a new token', async t => {
|
|
@@ -294,11 +296,11 @@ test('each successive --renew call forces a new login', async t => {
|
|
|
294
296
|
assert.equal(requests.length, 2);
|
|
295
297
|
});
|
|
296
298
|
|
|
297
|
-
test('--renew without an authentication method
|
|
299
|
+
test('--renew without an authentication method remains a no-op', async t => {
|
|
298
300
|
const {client} = await fixture(t);
|
|
299
301
|
const instance = client();
|
|
300
302
|
instance.rc = {};
|
|
301
|
-
|
|
303
|
+
assert.equal(await instance.connect(true), undefined);
|
|
302
304
|
});
|
|
303
305
|
|
|
304
306
|
for(const flag of ['renew']) {
|
|
@@ -736,3 +738,57 @@ for(const selected of ['identity', 'mail', 'default']) {
|
|
|
736
738
|
}
|
|
737
739
|
});
|
|
738
740
|
}
|
|
741
|
+
|
|
742
|
+
for(const explicit of [false, true]) {
|
|
743
|
+
test(`.creds uses the JS module without a CLI (explicit identity=${explicit})`, async t => {
|
|
744
|
+
const {client, file} = await fixture(t);
|
|
745
|
+
const instance = client();
|
|
746
|
+
const credentials = path.join(path.dirname(path.dirname(path.dirname(file))), '.creds');
|
|
747
|
+
const identity = spawnSync('ssh-add', ['-l'], {encoding : 'utf8'}).stdout.split(/\s+/)[1];
|
|
748
|
+
fs.writeFileSync(credentials, await encrypt(JSON.stringify({TEST_SECRET : 'module-secret'})));
|
|
749
|
+
instance.rc['ssh-agent-crypt'] = {path : credentials};
|
|
750
|
+
if(explicit)
|
|
751
|
+
instance.rc['ssh-agent-crypt'].identity = identity;
|
|
752
|
+
instance.rc.env = {map : {EXPORTED_SECRET : '$${secrets.TEST_SECRET}'}};
|
|
753
|
+
instance._vault_get_profile = async () => ({profile : {}, database : {}});
|
|
754
|
+
const previousPath = process.env.PATH;
|
|
755
|
+
try {
|
|
756
|
+
process.env.PATH = path.dirname(credentials);
|
|
757
|
+
const env = await instance.env();
|
|
758
|
+
assert.equal(env.EXPORTED_SECRET, 'module-secret');
|
|
759
|
+
} finally {
|
|
760
|
+
process.env.PATH = previousPath;
|
|
761
|
+
}
|
|
762
|
+
});
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
test('.creds decryption failures reject instead of exiting successfully', async t => {
|
|
766
|
+
const {client, file} = await fixture(t);
|
|
767
|
+
const instance = client();
|
|
768
|
+
const credentials = path.join(path.dirname(path.dirname(path.dirname(file))), '.creds');
|
|
769
|
+
fs.writeFileSync(credentials, 'not an armored payload');
|
|
770
|
+
instance.rc['ssh-agent-crypt'] = {path : credentials};
|
|
771
|
+
instance._vault_get_profile = async () => ({profile : {}, database : {}});
|
|
772
|
+
await assert.rejects(instance.env(), /Unsupported format header/);
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
for(const inherited of ['', 'inherited-token']) {
|
|
776
|
+
test(`local .creds environment works offline with inherited token=${!!inherited}`, async t => {
|
|
777
|
+
const {client, file, requests} = await fixture(t, {lookup_status : 503});
|
|
778
|
+
const instance = client();
|
|
779
|
+
const credentials = path.join(path.dirname(path.dirname(path.dirname(file))), '.creds');
|
|
780
|
+
fs.writeFileSync(credentials, await encrypt(JSON.stringify({ARM_CLIENT_SECRET : 'local-secret', VAULT_TOKEN : 'local-token'})));
|
|
781
|
+
instance.rc = {
|
|
782
|
+
'ssh-agent-crypt' : {path : credentials},
|
|
783
|
+
env : {map : {ARM_CLIENT_SECRET : '$${secrets.ARM_CLIENT_SECRET}', VAULT_TOKEN : '$${secrets.VAULT_TOKEN}'}},
|
|
784
|
+
};
|
|
785
|
+
instance.VAULT_TOKEN = inherited;
|
|
786
|
+
instance._lookup_token = async () => { throw new Error('Local environment must not contact Vault'); };
|
|
787
|
+
instance._read_token_cache = async () => { throw new Error('Local environment must not read the token cache'); };
|
|
788
|
+
const env = await instance.env();
|
|
789
|
+
assert.equal(env.ARM_CLIENT_SECRET, 'local-secret');
|
|
790
|
+
assert.equal(env.VAULT_TOKEN, 'local-token');
|
|
791
|
+
assert.equal(env.VAULT_ADDR, instance.VAULT_ADDR);
|
|
792
|
+
assert.deepEqual(requests, []);
|
|
793
|
+
});
|
|
794
|
+
}
|