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 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
- let child = spawn('ssh-agent-crypt', ["-decrypt", identity]);
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vvauth",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "Vault Auth helper",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -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('an invalid token without an auth method fails rather than exporting undefined', async t => {
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
- await assert.rejects(instance.connect(), /no authentication method/);
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 fails even with no cached token', async t => {
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
- await assert.rejects(instance.connect(true), /no authentication method/);
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
+ }