@@ -282,11 +282,171 @@ describe('Value Encryption', () => {
282
282
} ) ;
283
283
} ) ;
284
284
285
+ describe ( 'per environment encryption E2E' , ( ) => {
286
+ it ( 'sets up, trusts and untrusts users correctly' , ( ) => {
287
+ const cwd = process . cwd ( ) ;
288
+
289
+ return withTempFiles ( { } , async ( inDir ) => {
290
+ // run environmentless
291
+ delete process . env . NODE_ENV ;
292
+
293
+ process . chdir ( inDir ( '.' ) ) ;
294
+ process . env . APP_CONFIG_SECRETS_KEYCHAIN_FOLDER = inDir ( 'keychain' ) ;
295
+
296
+ const keys = await initializeKeysManually ( {
297
+ name : 'Tester' ,
298
+
299
+ } ) ;
300
+
301
+ const dirs = {
302
+ keychain : inDir ( 'keychain' ) ,
303
+ privateKey : inDir ( 'keychain/private-key.asc' ) ,
304
+ publicKey : inDir ( 'keychain/public-key.asc' ) ,
305
+ revocationCert : inDir ( 'keychain/revocation.asc' ) ,
306
+ } ;
307
+
308
+ expect ( await initializeLocalKeys ( keys , dirs ) ) . toEqual ( {
309
+ publicKeyArmored : keys . publicKeyArmored ,
310
+ } ) ;
311
+
312
+ const publicKey = await loadPublicKey ( ) ;
313
+ const privateKey = await loadPrivateKey ( ) ;
314
+
315
+ // this is what init-repo does
316
+ await trustTeamMember ( publicKey , privateKey ) ;
317
+
318
+ // at this point, we should have ourselves trusted, and 1 symmetric key
319
+ const { value : meta } = await loadMetaConfig ( ) ;
320
+
321
+ expect ( meta . teamMembers ) . toHaveProperty ( 'default' ) ;
322
+ expect ( meta . encryptionKeys ) . toHaveProperty ( 'default' ) ;
323
+ expect ( ( meta . teamMembers ! as any ) . default ) . toHaveLength ( 1 ) ;
324
+ expect ( ( meta . encryptionKeys ! as any ) . default ) . toHaveLength ( 1 ) ;
325
+
326
+ const encryptionKey = await loadLatestSymmetricKey ( privateKey ) ;
327
+ const encrypted = await encryptValue ( 'a secret value' , encryptionKey ) ;
328
+ await expect ( decryptValue ( encrypted , encryptionKey ) ) . resolves . toBe ( 'a secret value' ) ;
329
+
330
+ // now lets create a new environment
331
+ await trustTeamMember ( publicKey , privateKey , { ...defaultEnvOptions , override : 'prod' } ) ;
332
+
333
+ // at this point, we should have a default and a prod env with 1 trusted member and 1 key each
334
+ const { value : prodEnvMeta } = await loadMetaConfig ( ) ;
335
+
336
+ expect ( prodEnvMeta . teamMembers ) . toHaveProperty ( 'default' ) ;
337
+ expect ( prodEnvMeta . encryptionKeys ) . toHaveProperty ( 'default' ) ;
338
+ expect ( ( prodEnvMeta . teamMembers ! as any ) . default ) . toHaveLength ( 1 ) ;
339
+ expect ( ( prodEnvMeta . encryptionKeys ! as any ) . default ) . toHaveLength ( 1 ) ;
340
+ expect ( prodEnvMeta . teamMembers ) . toHaveProperty ( 'production' ) ;
341
+ expect ( prodEnvMeta . encryptionKeys ) . toHaveProperty ( 'production' ) ;
342
+ expect ( ( prodEnvMeta . teamMembers ! as any ) . production ) . toHaveLength ( 1 ) ;
343
+ expect ( ( prodEnvMeta . encryptionKeys ! as any ) . production ) . toHaveLength ( 1 ) ;
344
+
345
+ const prodEncryptionKey = await loadLatestSymmetricKey ( privateKey ) ;
346
+ const prodEncrypted = await encryptValue ( 'a secret value' , prodEncryptionKey ) ;
347
+ await expect ( decryptValue ( prodEncrypted , prodEncryptionKey ) ) . resolves . toBe ( 'a secret value' ) ;
348
+
349
+ const teammateKeys = await initializeKeysManually ( {
350
+ name : 'A Teammate' ,
351
+
352
+ } ) ;
353
+
354
+ const teammatePublicKey = await loadPublicKey ( teammateKeys . publicKeyArmored ) ;
355
+ const teammatePrivateKey = await loadPrivateKey ( teammateKeys . privateKeyArmored ) ;
356
+
357
+ await trustTeamMember ( teammatePublicKey , privateKey ) ;
358
+
359
+ // at this point, we should have 2 team members, but still 1 symmetric key
360
+ const { value : metaAfterTrustingTeammate } = await loadMetaConfig ( ) ;
361
+
362
+ expect ( metaAfterTrustingTeammate . teamMembers ) . toHaveProperty ( 'default' ) ;
363
+ expect ( metaAfterTrustingTeammate . encryptionKeys ) . toHaveProperty ( 'default' ) ;
364
+ expect ( ( metaAfterTrustingTeammate . teamMembers ! as any ) . default ) . toHaveLength ( 2 ) ;
365
+ expect ( ( metaAfterTrustingTeammate . encryptionKeys ! as any ) . default ) . toHaveLength ( 1 ) ;
366
+ expect ( metaAfterTrustingTeammate . teamMembers ) . toHaveProperty ( 'production' ) ;
367
+ expect ( metaAfterTrustingTeammate . encryptionKeys ) . toHaveProperty ( 'production' ) ;
368
+ expect ( ( metaAfterTrustingTeammate . teamMembers ! as any ) . production ) . toHaveLength ( 1 ) ;
369
+ expect ( ( metaAfterTrustingTeammate . encryptionKeys ! as any ) . production ) . toHaveLength ( 1 ) ;
370
+
371
+ // ensures that the teammate can now encrypt/decrypt values
372
+ const encryptedByTeammate = await encryptValue (
373
+ 'a secret value' ,
374
+ await loadLatestSymmetricKey ( teammatePrivateKey ) ,
375
+ ) ;
376
+ await expect (
377
+ decryptValue ( encryptedByTeammate , await loadLatestSymmetricKey ( teammatePrivateKey ) ) ,
378
+ ) . resolves . toBe ( 'a secret value' ) ;
379
+
380
+ // ensures that we can still encrypt/decrypt values
381
+ const encryptedByUs = await encryptValue (
382
+ 'a secret value' ,
383
+ await loadLatestSymmetricKey ( privateKey ) ,
384
+ ) ;
385
+ await expect (
386
+ decryptValue ( encryptedByUs , await loadLatestSymmetricKey ( privateKey ) ) ,
387
+ ) . resolves . toBe ( 'a secret value' ) ;
388
+
389
+ await untrustTeamMember ( '[email protected] ' , privateKey ) ;
390
+
391
+ // at this point, we should have 1 team members, and a newly generated symmetric key
392
+ const { value : metaAfterUntrustingTeammate } = await loadMetaConfig ( ) ;
393
+
394
+ expect ( metaAfterUntrustingTeammate . teamMembers ) . toHaveProperty ( 'default' ) ;
395
+ expect ( metaAfterUntrustingTeammate . encryptionKeys ) . toHaveProperty ( 'default' ) ;
396
+ expect ( ( metaAfterUntrustingTeammate . teamMembers ! as any ) . default ) . toHaveLength ( 1 ) ;
397
+ expect ( ( metaAfterUntrustingTeammate . encryptionKeys ! as any ) . default ) . toHaveLength ( 2 ) ;
398
+ expect ( metaAfterUntrustingTeammate . teamMembers ) . toHaveProperty ( 'production' ) ;
399
+ expect ( metaAfterUntrustingTeammate . encryptionKeys ) . toHaveProperty ( 'production' ) ;
400
+ expect ( ( metaAfterUntrustingTeammate . teamMembers ! as any ) . production ) . toHaveLength ( 1 ) ;
401
+ expect ( ( metaAfterUntrustingTeammate . encryptionKeys ! as any ) . production ) . toHaveLength ( 1 ) ;
402
+
403
+ // ensures that we can still encrypt/decrypt values
404
+ const newlyEncryptedByUs = await encryptValue (
405
+ 'a secret value' ,
406
+ await loadLatestSymmetricKey ( privateKey ) ,
407
+ ) ;
408
+ await expect (
409
+ decryptValue ( newlyEncryptedByUs , await loadLatestSymmetricKey ( privateKey ) ) ,
410
+ ) . resolves . toBe ( 'a secret value' ) ;
411
+
412
+ // now, the teammate should have no access
413
+ await expect ( loadLatestSymmetricKey ( teammatePrivateKey ) ) . rejects . toThrow ( ) ;
414
+
415
+ // just for test coverage, create a new symmetric key
416
+ const latestSymmetricKey = await loadLatestSymmetricKey ( privateKey ) ;
417
+
418
+ const newRevisionNumber = getRevisionNumber ( latestSymmetricKey . revision ) + 1 ;
419
+
420
+ await saveNewSymmetricKey (
421
+ await generateSymmetricKey ( newRevisionNumber . toString ( ) ) ,
422
+ await loadTeamMembers ( ) ,
423
+ ) ;
424
+
425
+ const { value : metaAfterNewSymmetricKey } = await loadMetaConfig ( ) ;
426
+
427
+ expect ( metaAfterNewSymmetricKey . teamMembers ) . toHaveProperty ( 'default' ) ;
428
+ expect ( metaAfterNewSymmetricKey . encryptionKeys ) . toHaveProperty ( 'default' ) ;
429
+ expect ( ( metaAfterNewSymmetricKey . teamMembers ! as any ) . default ) . toHaveLength ( 1 ) ;
430
+ expect ( ( metaAfterNewSymmetricKey . encryptionKeys ! as any ) . default ) . toHaveLength ( 3 ) ;
431
+ expect ( metaAfterNewSymmetricKey . teamMembers ) . toHaveProperty ( 'production' ) ;
432
+ expect ( metaAfterNewSymmetricKey . encryptionKeys ) . toHaveProperty ( 'production' ) ;
433
+ expect ( ( metaAfterNewSymmetricKey . teamMembers ! as any ) . production ) . toHaveLength ( 1 ) ;
434
+ expect ( ( metaAfterNewSymmetricKey . encryptionKeys ! as any ) . production ) . toHaveLength ( 1 ) ;
435
+
436
+ // get out of the directory, Windows doesn't like unlink while cwd
437
+ process . chdir ( cwd ) ;
438
+ } ) ;
439
+ } ) ;
440
+ } ) ;
441
+
285
442
describe ( 'E2E Encrypted Repo' , ( ) => {
286
443
it ( 'sets up, trusts and untrusts users correctly' , ( ) => {
287
444
const cwd = process . cwd ( ) ;
288
445
289
446
return withTempFiles ( { } , async ( inDir ) => {
447
+ // run environmentless
448
+ delete process . env . NODE_ENV ;
449
+
290
450
process . chdir ( inDir ( '.' ) ) ;
291
451
process . env . APP_CONFIG_SECRETS_KEYCHAIN_FOLDER = inDir ( 'keychain' ) ;
292
452
@@ -315,8 +475,10 @@ describe('E2E Encrypted Repo', () => {
315
475
// at this point, we should have ourselves trusted, and 1 symmetric key
316
476
const { value : meta } = await loadMetaConfig ( ) ;
317
477
318
- expect ( meta . teamMembers ) . toHaveLength ( 1 ) ;
319
- expect ( meta . encryptionKeys ) . toHaveLength ( 1 ) ;
478
+ expect ( meta . teamMembers ) . toHaveProperty ( 'default' ) ;
479
+ expect ( meta . encryptionKeys ) . toHaveProperty ( 'default' ) ;
480
+ expect ( ( meta . teamMembers ! as any ) . default ) . toHaveLength ( 1 ) ;
481
+ expect ( ( meta . encryptionKeys ! as any ) . default ) . toHaveLength ( 1 ) ;
320
482
321
483
const encryptionKey = await loadLatestSymmetricKey ( privateKey ) ;
322
484
const encrypted = await encryptValue ( 'a secret value' , encryptionKey ) ;
@@ -335,8 +497,10 @@ describe('E2E Encrypted Repo', () => {
335
497
// at this point, we should have 2 team members, but still 1 symmetric key
336
498
const { value : metaAfterTrustingTeammate } = await loadMetaConfig ( ) ;
337
499
338
- expect ( metaAfterTrustingTeammate . teamMembers ) . toHaveLength ( 2 ) ;
339
- expect ( metaAfterTrustingTeammate . encryptionKeys ) . toHaveLength ( 1 ) ;
500
+ expect ( metaAfterTrustingTeammate . teamMembers ) . toHaveProperty ( 'default' ) ;
501
+ expect ( metaAfterTrustingTeammate . encryptionKeys ) . toHaveProperty ( 'default' ) ;
502
+ expect ( ( metaAfterTrustingTeammate . teamMembers ! as any ) . default ) . toHaveLength ( 2 ) ;
503
+ expect ( ( metaAfterTrustingTeammate . encryptionKeys ! as any ) . default ) . toHaveLength ( 1 ) ;
340
504
341
505
// ensures that the teammate can now encrypt/decrypt values
342
506
const encryptedByTeammate = await encryptValue (
@@ -361,8 +525,10 @@ describe('E2E Encrypted Repo', () => {
361
525
// at this point, we should have 1 team members, and a newly generated symmetric key
362
526
const { value : metaAfterUntrustingTeammate } = await loadMetaConfig ( ) ;
363
527
364
- expect ( metaAfterUntrustingTeammate . teamMembers ) . toHaveLength ( 1 ) ;
365
- expect ( metaAfterUntrustingTeammate . encryptionKeys ) . toHaveLength ( 2 ) ;
528
+ expect ( metaAfterUntrustingTeammate . teamMembers ) . toHaveProperty ( 'default' ) ;
529
+ expect ( metaAfterUntrustingTeammate . encryptionKeys ) . toHaveProperty ( 'default' ) ;
530
+ expect ( ( metaAfterUntrustingTeammate . teamMembers ! as any ) . default ) . toHaveLength ( 1 ) ;
531
+ expect ( ( metaAfterUntrustingTeammate . encryptionKeys ! as any ) . default ) . toHaveLength ( 2 ) ;
366
532
367
533
// ensures that we can still encrypt/decrypt values
368
534
const newlyEncryptedByUs = await encryptValue (
@@ -388,8 +554,10 @@ describe('E2E Encrypted Repo', () => {
388
554
389
555
const { value : metaAfterNewSymmetricKey } = await loadMetaConfig ( ) ;
390
556
391
- expect ( metaAfterNewSymmetricKey . teamMembers ) . toHaveLength ( 1 ) ;
392
- expect ( metaAfterNewSymmetricKey . encryptionKeys ) . toHaveLength ( 3 ) ;
557
+ expect ( metaAfterNewSymmetricKey . teamMembers ) . toHaveProperty ( 'default' ) ;
558
+ expect ( metaAfterNewSymmetricKey . encryptionKeys ) . toHaveProperty ( 'default' ) ;
559
+ expect ( ( metaAfterNewSymmetricKey . teamMembers ! as any ) . default ) . toHaveLength ( 1 ) ;
560
+ expect ( ( metaAfterNewSymmetricKey . encryptionKeys ! as any ) . default ) . toHaveLength ( 3 ) ;
393
561
394
562
// get out of the directory, Windows doesn't like unlink while cwd
395
563
process . chdir ( cwd ) ;
0 commit comments