* chore: fix getting vault method * remove only attribute * fix checking for vaults * fix expected missing vaults
504 lines
19 KiB
JavaScript
504 lines
19 KiB
JavaScript
import * as Factory from '../lib/factory.js'
|
|
import * as Collaboration from '../lib/Collaboration.js'
|
|
|
|
chai.use(chaiAsPromised)
|
|
const expect = chai.expect
|
|
|
|
describe('vault key management', function () {
|
|
this.timeout(Factory.TwentySecondTimeout)
|
|
|
|
let context
|
|
|
|
beforeEach(async function () {
|
|
localStorage.clear()
|
|
|
|
context = await Factory.createVaultsContextWithRealCrypto()
|
|
|
|
await context.launch()
|
|
})
|
|
|
|
afterEach(async function () {
|
|
await context.deinit()
|
|
localStorage.clear()
|
|
sinon.restore()
|
|
context = undefined
|
|
})
|
|
|
|
describe('locking', () => {
|
|
it('should throw if attempting to add item to locked vault', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Ephemeral,
|
|
})
|
|
|
|
await context.vaultLocks.lockNonPersistentVault(vault)
|
|
|
|
const item = await context.createSyncedNote('test note', 'test note text')
|
|
|
|
await Factory.expectThrowsAsync(
|
|
() => context.vaults.moveItemToVault(vault, item),
|
|
'Attempting to add item to locked vault',
|
|
)
|
|
})
|
|
|
|
it('should throw if attempting to remove item from locked vault', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Ephemeral,
|
|
})
|
|
|
|
const item = await context.createSyncedNote('test note', 'test note text')
|
|
|
|
await context.vaults.moveItemToVault(vault, item)
|
|
|
|
await context.vaultLocks.lockNonPersistentVault(vault)
|
|
|
|
await Factory.expectThrowsAsync(
|
|
() => context.vaults.removeItemFromVault(item),
|
|
'Cannot find vault to remove item from',
|
|
)
|
|
})
|
|
|
|
it('should lock non-persistent vault', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Ephemeral,
|
|
})
|
|
|
|
await context.vaultLocks.lockNonPersistentVault(vault)
|
|
|
|
expect(context.vaultLocks.isVaultLocked(vault)).to.be.true
|
|
})
|
|
|
|
it('should not be able to lock user-inputted vault with synced key', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Synced,
|
|
})
|
|
|
|
await Factory.expectThrowsAsync(
|
|
() => context.vaultLocks.lockNonPersistentVault(vault),
|
|
'Vault uses synced key storage and cannot be locked',
|
|
)
|
|
})
|
|
|
|
it('should not be able to lock randomized vault', async () => {
|
|
const vault = await context.vaults.createRandomizedVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
})
|
|
|
|
await Factory.expectThrowsAsync(
|
|
() => context.vaultLocks.lockNonPersistentVault(vault),
|
|
'Vault uses synced key storage and cannot be locked',
|
|
)
|
|
})
|
|
|
|
it('should throw if attempting to change password of locked vault', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Ephemeral,
|
|
})
|
|
|
|
await context.vaultLocks.lockNonPersistentVault(vault)
|
|
|
|
await Factory.expectThrowsAsync(
|
|
() => context.vaults.changeVaultKeyOptions({ vault }),
|
|
'Attempting to change vault options on a locked vault',
|
|
)
|
|
})
|
|
})
|
|
|
|
describe('locking memory management', () => {
|
|
it('locking a vault should clear decrypted items keys from memory', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Ephemeral,
|
|
})
|
|
|
|
const itemsKeys = context.keys.getKeySystemItemsKeys(vault.systemIdentifier)
|
|
expect(itemsKeys.length).to.equal(1)
|
|
|
|
await context.vaultLocks.lockNonPersistentVault(vault)
|
|
|
|
const itemsKeysAfterLock = context.keys.getKeySystemItemsKeys(vault.systemIdentifier)
|
|
expect(itemsKeysAfterLock.length).to.equal(0)
|
|
})
|
|
|
|
it('locking then unlocking a vault should bring items keys back into memory', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Ephemeral,
|
|
})
|
|
|
|
await context.vaultLocks.lockNonPersistentVault(vault)
|
|
await context.vaultLocks.unlockNonPersistentVault(vault, 'test password')
|
|
|
|
const itemsKeys = context.keys.getKeySystemItemsKeys(vault.systemIdentifier)
|
|
expect(itemsKeys.length).to.equal(1)
|
|
|
|
const rootKeys = context.keys.getAllKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(rootKeys.length).to.equal(1)
|
|
})
|
|
|
|
it('locking should clear vault items from memory', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Ephemeral,
|
|
})
|
|
|
|
const note = await context.createSyncedNote()
|
|
await Collaboration.moveItemToVault(context, vault, note)
|
|
|
|
await context.vaultLocks.lockNonPersistentVault(vault)
|
|
|
|
const decryptedNote = context.items.findItem(note.uuid)
|
|
expect(decryptedNote).to.be.undefined
|
|
|
|
const encryptedNote = context.items.findAnyItem(note.uuid)
|
|
expect(encryptedNote).to.not.be.undefined
|
|
expect(isEncryptedItem(encryptedNote)).to.be.true
|
|
})
|
|
})
|
|
|
|
describe('key rotation and persistence', () => {
|
|
it('rotating ephemeral vault should not persist keys', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Ephemeral,
|
|
})
|
|
|
|
await context.vaults.rotateVaultRootKey(vault, 'test password')
|
|
|
|
const syncedKeys = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(syncedKeys.length).to.equal(0)
|
|
|
|
const storedKey = context.keys.getRootKeyFromStorageForVault(vault.systemIdentifier)
|
|
expect(storedKey).to.be.undefined
|
|
})
|
|
|
|
it('rotating local vault should not sync keys', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Local,
|
|
})
|
|
|
|
await context.vaults.rotateVaultRootKey(vault, 'test password')
|
|
|
|
const syncedKeys = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(syncedKeys.length).to.equal(0)
|
|
|
|
const storedKey = context.keys.getRootKeyFromStorageForVault(vault.systemIdentifier)
|
|
expect(storedKey).to.not.be.undefined
|
|
})
|
|
|
|
it('rotating synced vault should sync new key', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Synced,
|
|
})
|
|
|
|
await context.vaults.rotateVaultRootKey(vault, 'test password')
|
|
|
|
const syncedKeys = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(syncedKeys.length).to.equal(2)
|
|
|
|
const storedKey = context.keys.getRootKeyFromStorageForVault(vault.systemIdentifier)
|
|
expect(storedKey).to.be.undefined
|
|
})
|
|
})
|
|
|
|
describe('changeVaultKeyOptions', () => {
|
|
describe('change storage type', () => {
|
|
it('should not be able to change randomized vault from synced to local', async () => {
|
|
const vault = await context.vaults.createRandomizedVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
})
|
|
|
|
const result = await context.vaults.changeVaultKeyOptions({
|
|
vault,
|
|
newStorageMode: KeySystemRootKeyStorageMode.Local,
|
|
})
|
|
|
|
expect(result.isFailed()).to.be.true
|
|
expect(result.getError()).to.equal('Cannot change storage mode to non-synced for randomized vault')
|
|
})
|
|
|
|
it('should not be able to change randomized vault from synced to ephemeral', async () => {
|
|
const vault = await context.vaults.createRandomizedVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
})
|
|
|
|
const result = await context.vaults.changeVaultKeyOptions({
|
|
vault,
|
|
newStorageMode: KeySystemRootKeyStorageMode.Ephemeral,
|
|
})
|
|
|
|
expect(result.isFailed()).to.be.true
|
|
expect(result.getError()).to.equal('Cannot change storage mode to non-synced for randomized vault')
|
|
})
|
|
|
|
it('should change user password vault from synced to local', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Synced,
|
|
})
|
|
|
|
let syncedKeys = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
|
|
const result = await context.vaults.changeVaultKeyOptions({
|
|
vault,
|
|
newStorageMode: KeySystemRootKeyStorageMode.Local,
|
|
})
|
|
|
|
expect(result.isFailed()).to.be.false
|
|
|
|
syncedKeys = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(syncedKeys.length).to.equal(0)
|
|
|
|
const storedKey = context.keys.getRootKeyFromStorageForVault(vault.systemIdentifier)
|
|
expect(storedKey).to.not.be.undefined
|
|
})
|
|
|
|
it('should change user password vault from synced to ephemeral', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Synced,
|
|
})
|
|
|
|
let syncedKeys = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
|
|
const result = await context.vaults.changeVaultKeyOptions({
|
|
vault,
|
|
newStorageMode: KeySystemRootKeyStorageMode.Ephemeral,
|
|
})
|
|
|
|
expect(result.isFailed()).to.be.false
|
|
|
|
syncedKeys = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(syncedKeys.length).to.equal(0)
|
|
|
|
const storedKey = context.keys.getRootKeyFromStorageForVault(vault.systemIdentifier)
|
|
expect(storedKey).to.be.undefined
|
|
|
|
const memKeys = context.keys.getAllKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(memKeys.length).to.equal(1)
|
|
})
|
|
|
|
it('should change user password vault from local to synced', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Local,
|
|
})
|
|
|
|
let syncedKeys = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
|
|
const result = await context.vaults.changeVaultKeyOptions({
|
|
vault,
|
|
newStorageMode: KeySystemRootKeyStorageMode.Synced,
|
|
})
|
|
|
|
expect(result.isFailed()).to.be.false
|
|
|
|
syncedKeys = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(syncedKeys.length).to.equal(1)
|
|
|
|
const storedKey = context.keys.getRootKeyFromStorageForVault(vault.systemIdentifier)
|
|
expect(storedKey).to.be.undefined
|
|
|
|
const memKeys = context.keys.getAllKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(memKeys.length).to.equal(1)
|
|
})
|
|
|
|
it('should change user password vault from local to ephemeral', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Local,
|
|
})
|
|
|
|
let syncedKeys = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
|
|
const result = await context.vaults.changeVaultKeyOptions({
|
|
vault,
|
|
newStorageMode: KeySystemRootKeyStorageMode.Ephemeral,
|
|
})
|
|
|
|
expect(result.isFailed()).to.be.false
|
|
|
|
syncedKeys = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(syncedKeys.length).to.equal(0)
|
|
|
|
const storedKey = context.keys.getRootKeyFromStorageForVault(vault.systemIdentifier)
|
|
expect(storedKey).to.be.undefined
|
|
|
|
const memKeys = context.keys.getAllKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(memKeys.length).to.equal(1)
|
|
})
|
|
|
|
it('should throw if attempting to change key options of third party vault', async () => {
|
|
await context.register()
|
|
|
|
const { contactVault, contactContext, deinitContactContext } =
|
|
await Collaboration.createSharedVaultWithAcceptedInvite(context)
|
|
|
|
await Factory.expectThrowsAsync(
|
|
() => contactContext.vaults.changeVaultKeyOptions({ vault: contactVault }),
|
|
'Third party vault options should be changed via changeThirdPartyVaultStorageOptions',
|
|
)
|
|
|
|
await deinitContactContext()
|
|
})
|
|
|
|
it('changing storage options for third party vault should validate password', async () => {
|
|
await context.register()
|
|
|
|
const { contactVault, thirdPartyContext, deinitThirdPartyContext } = await context.createSharedPasswordVault(
|
|
'test password',
|
|
)
|
|
|
|
const invalidResult = await thirdPartyContext.vaults.changeThirdPartyVaultStorageOptions({
|
|
vault: contactVault,
|
|
vaultPassword: 'wrong password',
|
|
newStorageMode: KeySystemRootKeyStorageMode.Synced,
|
|
})
|
|
|
|
expect(invalidResult.isFailed()).to.be.true
|
|
expect(invalidResult.getError()).to.equal('Invalid vault password')
|
|
|
|
const validResult = await thirdPartyContext.vaults.changeThirdPartyVaultStorageOptions({
|
|
vault: contactVault,
|
|
vaultPassword: 'test password',
|
|
newStorageMode: KeySystemRootKeyStorageMode.Local,
|
|
})
|
|
|
|
expect(validResult.isFailed()).to.be.false
|
|
|
|
await deinitThirdPartyContext()
|
|
})
|
|
})
|
|
|
|
describe('change password type', () => {
|
|
it('should fail to change password type from randomized to user inputted if password is not supplied', async () => {
|
|
const vault = await context.vaults.createRandomizedVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
})
|
|
|
|
const result = await context.vaults.changeVaultKeyOptions({
|
|
vault,
|
|
newPasswordOptions: {
|
|
passwordType: KeySystemPasswordType.UserInputted,
|
|
},
|
|
})
|
|
|
|
expect(result.isFailed()).to.be.true
|
|
})
|
|
|
|
it('should change password type from randomized to user inputted', async () => {
|
|
const vault = await context.vaults.createRandomizedVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
})
|
|
|
|
const rootKeysBeforeChange = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(rootKeysBeforeChange.length).to.equal(1)
|
|
|
|
const result = await context.vaults.changeVaultKeyOptions({
|
|
vault,
|
|
newPasswordOptions: {
|
|
passwordType: KeySystemPasswordType.UserInputted,
|
|
userInputtedPassword: 'test password',
|
|
},
|
|
})
|
|
|
|
expect(result.isFailed()).to.be.false
|
|
|
|
const rootKeysAfterChange = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(rootKeysAfterChange.length).to.equal(2)
|
|
|
|
expect(rootKeysAfterChange[0].itemsKey).to.not.equal(rootKeysAfterChange[1].itemsKey)
|
|
})
|
|
|
|
it('should change password type from user inputted to randomized', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Local,
|
|
})
|
|
|
|
const result = await context.vaults.changeVaultKeyOptions({
|
|
vault,
|
|
newPasswordOptions: {
|
|
passwordType: KeySystemPasswordType.Randomized,
|
|
},
|
|
})
|
|
|
|
expect(result.isFailed()).to.be.false
|
|
|
|
const rootKeysAfterChange = context.keys.getSyncedKeySystemRootKeysForVault(vault.systemIdentifier)
|
|
expect(rootKeysAfterChange.length).to.equal(2)
|
|
|
|
const storedKey = context.keys.getRootKeyFromStorageForVault(vault.systemIdentifier)
|
|
expect(storedKey).to.be.undefined
|
|
|
|
const updatedVault = context.vaults.getVault({ keySystemIdentifier: vault.systemIdentifier }).getValue()
|
|
expect(updatedVault.keyStorageMode).to.equal(KeySystemRootKeyStorageMode.Synced)
|
|
})
|
|
|
|
it('should fail to change password type from user inputted to randomized if storage mode is not synced', async () => {
|
|
const vault = await context.vaults.createUserInputtedPasswordVault({
|
|
name: 'test vault',
|
|
description: 'test vault description',
|
|
userInputtedPassword: 'test password',
|
|
storagePreference: KeySystemRootKeyStorageMode.Local,
|
|
})
|
|
|
|
const result = await context.vaults.changeVaultKeyOptions({
|
|
vault,
|
|
newPasswordOptions: {
|
|
passwordType: KeySystemPasswordType.Randomized,
|
|
},
|
|
newStorageMode: KeySystemRootKeyStorageMode.Local,
|
|
})
|
|
|
|
expect(result.isFailed()).to.be.true
|
|
|
|
expect(result.getError()).to.equal('Cannot change storage mode to non-synced for randomized vault')
|
|
})
|
|
})
|
|
})
|
|
})
|