tests: vault tests (#2366)

This commit is contained in:
Mo
2023-07-25 07:40:28 -05:00
committed by GitHub
parent 80436cd0b9
commit 596e041c42
47 changed files with 479 additions and 289 deletions

View File

@@ -1,3 +1,5 @@
import { VaultUserServiceInterface, VaultInviteServiceInterface } from '@standardnotes/services'
import { VaultLockServiceInterface } from './../VaultLock/VaultLockServiceInterface'
import { HistoryServiceInterface } from './../History/HistoryServiceInterface' import { HistoryServiceInterface } from './../History/HistoryServiceInterface'
import { InternalEventBusInterface } from './../Internal/InternalEventBusInterface' import { InternalEventBusInterface } from './../Internal/InternalEventBusInterface'
import { PreferenceServiceInterface } from './../Preferences/PreferenceServiceInterface' import { PreferenceServiceInterface } from './../Preferences/PreferenceServiceInterface'
@@ -5,7 +7,7 @@ import { AsymmetricMessageServiceInterface } from './../AsymmetricMessage/Asymme
import { SyncOptions } from './../Sync/SyncOptions' import { SyncOptions } from './../Sync/SyncOptions'
import { ImportDataReturnType } from './../Mutator/ImportDataUseCase' import { ImportDataReturnType } from './../Mutator/ImportDataUseCase'
import { ChallengeServiceInterface } from './../Challenge/ChallengeServiceInterface' import { ChallengeServiceInterface } from './../Challenge/ChallengeServiceInterface'
import { VaultServiceInterface } from './../Vaults/VaultServiceInterface' import { VaultServiceInterface } from '../Vault/VaultServiceInterface'
import { ApplicationIdentifier } from '@standardnotes/common' import { ApplicationIdentifier } from '@standardnotes/common'
import { import {
BackupFile, BackupFile,
@@ -95,24 +97,27 @@ export interface ApplicationInterface {
syncOptions?: SyncOptions, syncOptions?: SyncOptions,
): Promise<void> ): Promise<void>
get features(): FeaturesClientInterface
get componentManager(): ComponentManagerInterface
get items(): ItemManagerInterface
get mutator(): MutatorClientInterface
get user(): UserClientInterface
get files(): FilesClientInterface
get subscriptions(): SubscriptionManagerInterface
get fileBackups(): BackupServiceInterface | undefined
get sessions(): SessionsClientInterface
get homeServer(): HomeServerServiceInterface | undefined
get vaults(): VaultServiceInterface
get challenges(): ChallengeServiceInterface
get alerts(): AlertService get alerts(): AlertService
get asymmetric(): AsymmetricMessageServiceInterface get asymmetric(): AsymmetricMessageServiceInterface
get preferences(): PreferenceServiceInterface get challenges(): ChallengeServiceInterface
get events(): InternalEventBusInterface get componentManager(): ComponentManagerInterface
get history(): HistoryServiceInterface
get encryption(): EncryptionProviderInterface get encryption(): EncryptionProviderInterface
get events(): InternalEventBusInterface
get features(): FeaturesClientInterface
get fileBackups(): BackupServiceInterface | undefined
get files(): FilesClientInterface
get history(): HistoryServiceInterface
get homeServer(): HomeServerServiceInterface | undefined
get items(): ItemManagerInterface
get mutator(): MutatorClientInterface
get preferences(): PreferenceServiceInterface
get sessions(): SessionsClientInterface
get subscriptions(): SubscriptionManagerInterface
get user(): UserClientInterface
get vaults(): VaultServiceInterface
get vaultLocks(): VaultLockServiceInterface
get vaultUsers(): VaultUserServiceInterface
get vaultInvites(): VaultInviteServiceInterface
readonly identifier: ApplicationIdentifier readonly identifier: ApplicationIdentifier
readonly platform: Platform readonly platform: Platform

View File

@@ -1,3 +1,4 @@
import { GetVault } from './../Vault/UseCase/GetVault'
import { SessionsClientInterface } from './../Session/SessionsClientInterface' import { SessionsClientInterface } from './../Session/SessionsClientInterface'
import { EncryptionProviderInterface } from './../Encryption/EncryptionProviderInterface' import { EncryptionProviderInterface } from './../Encryption/EncryptionProviderInterface'
import { GetUntrustedPayload } from './UseCase/GetUntrustedPayload' import { GetUntrustedPayload } from './UseCase/GetUntrustedPayload'
@@ -5,7 +6,6 @@ import { GetInboundMessages } from './UseCase/GetInboundMessages'
import { GetOutboundMessages } from './UseCase/GetOutboundMessages' import { GetOutboundMessages } from './UseCase/GetOutboundMessages'
import { SendOwnContactChangeMessage } from './UseCase/SendOwnContactChangeMessage' import { SendOwnContactChangeMessage } from './UseCase/SendOwnContactChangeMessage'
import { HandleRootKeyChangedMessage } from './UseCase/HandleRootKeyChangedMessage' import { HandleRootKeyChangedMessage } from './UseCase/HandleRootKeyChangedMessage'
import { GetVault } from './../Vaults/UseCase/GetVault'
import { GetTrustedPayload } from './UseCase/GetTrustedPayload' import { GetTrustedPayload } from './UseCase/GetTrustedPayload'
import { ReplaceContactData } from './../Contacts/UseCase/ReplaceContactData' import { ReplaceContactData } from './../Contacts/UseCase/ReplaceContactData'
import { GetAllContacts } from './../Contacts/UseCase/GetAllContacts' import { GetAllContacts } from './../Contacts/UseCase/GetAllContacts'

View File

@@ -26,7 +26,7 @@ import { UserKeyPairChangedEventData } from '../Session/UserKeyPairChangedEventD
import { SendOwnContactChangeMessage } from './UseCase/SendOwnContactChangeMessage' import { SendOwnContactChangeMessage } from './UseCase/SendOwnContactChangeMessage'
import { GetOutboundMessages } from './UseCase/GetOutboundMessages' import { GetOutboundMessages } from './UseCase/GetOutboundMessages'
import { GetInboundMessages } from './UseCase/GetInboundMessages' import { GetInboundMessages } from './UseCase/GetInboundMessages'
import { GetVault } from '../Vaults/UseCase/GetVault' import { GetVault } from '../Vault/UseCase/GetVault'
import { AsymmetricMessageServiceInterface } from './AsymmetricMessageServiceInterface' import { AsymmetricMessageServiceInterface } from './AsymmetricMessageServiceInterface'
import { GetUntrustedPayload } from './UseCase/GetUntrustedPayload' import { GetUntrustedPayload } from './UseCase/GetUntrustedPayload'
import { FindContact } from '../Contacts/UseCase/FindContact' import { FindContact } from '../Contacts/UseCase/FindContact'

View File

@@ -10,7 +10,7 @@ import {
} from '@standardnotes/models' } from '@standardnotes/models'
import { ContentType } from '@standardnotes/domain-core' import { ContentType } from '@standardnotes/domain-core'
import { GetVault } from '../../Vaults/UseCase/GetVault' import { GetVault } from '../../Vault/UseCase/GetVault'
import { EncryptionProviderInterface } from '../../Encryption/EncryptionProviderInterface' import { EncryptionProviderInterface } from '../../Encryption/EncryptionProviderInterface'
export class HandleRootKeyChangedMessage { export class HandleRootKeyChangedMessage {

View File

@@ -11,7 +11,7 @@ import { SendVaultDataChangedMessage } from './UseCase/SendVaultDataChangedMessa
import { NotifyVaultUsersOfKeyRotation } from './UseCase/NotifyVaultUsersOfKeyRotation' import { NotifyVaultUsersOfKeyRotation } from './UseCase/NotifyVaultUsersOfKeyRotation'
import { HandleKeyPairChange } from './../Contacts/UseCase/HandleKeyPairChange' import { HandleKeyPairChange } from './../Contacts/UseCase/HandleKeyPairChange'
import { CreateSharedVault } from './UseCase/CreateSharedVault' import { CreateSharedVault } from './UseCase/CreateSharedVault'
import { GetVault } from './../Vaults/UseCase/GetVault' import { GetVault } from './../Vault/UseCase/GetVault'
import { SharedVaultService } from './SharedVaultService' import { SharedVaultService } from './SharedVaultService'
import { SyncServiceInterface } from '../Sync/SyncServiceInterface' import { SyncServiceInterface } from '../Sync/SyncServiceInterface'
import { ItemManagerInterface } from '../Item/ItemManagerInterface' import { ItemManagerInterface } from '../Item/ItemManagerInterface'

View File

@@ -21,13 +21,13 @@ import { InternalEventInterface } from '../Internal/InternalEventInterface'
import { UserEventServiceEvent, UserEventServiceEventPayload } from '../UserEvent/UserEventServiceEvent' import { UserEventServiceEvent, UserEventServiceEventPayload } from '../UserEvent/UserEventServiceEvent'
import { DeleteThirdPartyVault } from './UseCase/DeleteExternalSharedVault' import { DeleteThirdPartyVault } from './UseCase/DeleteExternalSharedVault'
import { DeleteSharedVault } from './UseCase/DeleteSharedVault' import { DeleteSharedVault } from './UseCase/DeleteSharedVault'
import { VaultServiceEvent, VaultServiceEventPayload } from '../Vaults/VaultServiceEvent' import { VaultServiceEvent, VaultServiceEventPayload } from '../Vault/VaultServiceEvent'
import { ShareContactWithVault } from './UseCase/ShareContactWithVault' import { ShareContactWithVault } from './UseCase/ShareContactWithVault'
import { NotifyVaultUsersOfKeyRotation } from './UseCase/NotifyVaultUsersOfKeyRotation' import { NotifyVaultUsersOfKeyRotation } from './UseCase/NotifyVaultUsersOfKeyRotation'
import { CreateSharedVault } from './UseCase/CreateSharedVault' import { CreateSharedVault } from './UseCase/CreateSharedVault'
import { SendVaultDataChangedMessage } from './UseCase/SendVaultDataChangedMessage' import { SendVaultDataChangedMessage } from './UseCase/SendVaultDataChangedMessage'
import { ConvertToSharedVault } from './UseCase/ConvertToSharedVault' import { ConvertToSharedVault } from './UseCase/ConvertToSharedVault'
import { GetVault } from '../Vaults/UseCase/GetVault' import { GetVault } from '../Vault/UseCase/GetVault'
import { ContentType } from '@standardnotes/domain-core' import { ContentType } from '@standardnotes/domain-core'
import { HandleKeyPairChange } from '../Contacts/UseCase/HandleKeyPairChange' import { HandleKeyPairChange } from '../Contacts/UseCase/HandleKeyPairChange'
import { FindContact } from '../Contacts/UseCase/FindContact' import { FindContact } from '../Contacts/UseCase/FindContact'

View File

@@ -2,7 +2,7 @@ import { SharedVaultListingInterface, VaultListingInterface, VaultListingMutator
import { ClientDisplayableError, isErrorResponse } from '@standardnotes/responses' import { ClientDisplayableError, isErrorResponse } from '@standardnotes/responses'
import { SharedVaultServerInterface } from '@standardnotes/api' import { SharedVaultServerInterface } from '@standardnotes/api'
import { ItemManagerInterface } from '../../Item/ItemManagerInterface' import { ItemManagerInterface } from '../../Item/ItemManagerInterface'
import { MoveItemsToVault } from '../../Vaults/UseCase/MoveItemsToVault' import { MoveItemsToVault } from '../../Vault/UseCase/MoveItemsToVault'
import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface' import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface'
export class ConvertToSharedVault { export class ConvertToSharedVault {

View File

@@ -7,8 +7,8 @@ import {
import { ClientDisplayableError, isErrorResponse } from '@standardnotes/responses' import { ClientDisplayableError, isErrorResponse } from '@standardnotes/responses'
import { SharedVaultServerInterface } from '@standardnotes/api' import { SharedVaultServerInterface } from '@standardnotes/api'
import { ItemManagerInterface } from '../../Item/ItemManagerInterface' import { ItemManagerInterface } from '../../Item/ItemManagerInterface'
import { CreateVault } from '../../Vaults/UseCase/CreateVault' import { CreateVault } from '../../Vault/UseCase/CreateVault'
import { MoveItemsToVault } from '../../Vaults/UseCase/MoveItemsToVault' import { MoveItemsToVault } from '../../Vault/UseCase/MoveItemsToVault'
import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface' import { MutatorClientInterface } from '../../Mutator/MutatorClientInterface'
export class CreateSharedVault { export class CreateSharedVault {

View File

@@ -2,7 +2,7 @@ import { ClientDisplayableError, isErrorResponse } from '@standardnotes/response
import { SharedVaultServerInterface } from '@standardnotes/api' import { SharedVaultServerInterface } from '@standardnotes/api'
import { SharedVaultListingInterface } from '@standardnotes/models' import { SharedVaultListingInterface } from '@standardnotes/models'
import { SyncServiceInterface } from '../../Sync/SyncServiceInterface' import { SyncServiceInterface } from '../../Sync/SyncServiceInterface'
import { DeleteVault } from '../../Vaults/UseCase/DeleteVault' import { DeleteVault } from '../../Vault/UseCase/DeleteVault'
export class DeleteSharedVault { export class DeleteSharedVault {
constructor( constructor(

View File

@@ -1,4 +1,4 @@
import { GetVaults } from './../../Vaults/UseCase/GetVaults' import { GetVaults } from '../../Vault/UseCase/GetVaults'
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core' import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
import { SharedVaultListingInterface } from '@standardnotes/models' import { SharedVaultListingInterface } from '@standardnotes/models'

View File

@@ -5,7 +5,7 @@ import {
VaultListingInterface, VaultListingInterface,
VaultListingMutator, VaultListingMutator,
} from '@standardnotes/models' } from '@standardnotes/models'
import { ChangeVaultOptionsDTO } from '../ChangeVaultOptionsDTO' import { ChangeVaultKeyOptionsDTO } from './ChangeVaultKeyOptionsDTO'
import { GetVault } from './GetVault' import { GetVault } from './GetVault'
import { EncryptionProviderInterface } from '../../Encryption/EncryptionProviderInterface' import { EncryptionProviderInterface } from '../../Encryption/EncryptionProviderInterface'
import { KeySystemKeyManagerInterface } from '../../KeySystem/KeySystemKeyManagerInterface' import { KeySystemKeyManagerInterface } from '../../KeySystem/KeySystemKeyManagerInterface'
@@ -19,7 +19,7 @@ export class ChangeVaultKeyOptions {
private getVault: GetVault, private getVault: GetVault,
) {} ) {}
async execute(dto: ChangeVaultOptionsDTO): Promise<void> { async execute(dto: ChangeVaultKeyOptionsDTO): Promise<void> {
const useStorageMode = dto.newKeyStorageMode ?? dto.vault.keyStorageMode const useStorageMode = dto.newKeyStorageMode ?? dto.vault.keyStorageMode
if (dto.newPasswordType) { if (dto.newPasswordType) {

View File

@@ -1,6 +1,6 @@
import { KeySystemRootKeyPasswordType, KeySystemRootKeyStorageMode, VaultListingInterface } from '@standardnotes/models' import { KeySystemRootKeyPasswordType, KeySystemRootKeyStorageMode, VaultListingInterface } from '@standardnotes/models'
export type ChangeVaultOptionsDTO = { export type ChangeVaultKeyOptionsDTO = {
vault: VaultListingInterface vault: VaultListingInterface
newPasswordType: newPasswordType:
| { passwordType: KeySystemRootKeyPasswordType.Randomized } | { passwordType: KeySystemRootKeyPasswordType.Randomized }

View File

@@ -1,4 +1,4 @@
import { SyncServiceInterface } from './../../Sync/SyncServiceInterface' import { SyncServiceInterface } from '../../Sync/SyncServiceInterface'
import { UuidGenerator } from '@standardnotes/utils' import { UuidGenerator } from '@standardnotes/utils'
import { import {
KeySystemRootKeyParamsInterface, KeySystemRootKeyParamsInterface,

View File

@@ -1,5 +1,5 @@
import { VaultListingInterface } from '@standardnotes/models' import { VaultListingInterface } from '@standardnotes/models'
import { ItemManagerInterface } from './../../Item/ItemManagerInterface' import { ItemManagerInterface } from '../../Item/ItemManagerInterface'
import { ContentType, Result, SyncUseCaseInterface } from '@standardnotes/domain-core' import { ContentType, Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
export class GetVault implements SyncUseCaseInterface<VaultListingInterface> { export class GetVault implements SyncUseCaseInterface<VaultListingInterface> {

View File

@@ -3,14 +3,13 @@ import {
DecryptedItemInterface, DecryptedItemInterface,
FileItem, FileItem,
KeySystemIdentifier, KeySystemIdentifier,
KeySystemRootKeyPasswordType,
KeySystemRootKeyStorageMode, KeySystemRootKeyStorageMode,
VaultListingInterface, VaultListingInterface,
VaultListingMutator, VaultListingMutator,
isNote, isNote,
} from '@standardnotes/models' } from '@standardnotes/models'
import { VaultServiceInterface } from './VaultServiceInterface' import { VaultServiceInterface } from './VaultServiceInterface'
import { ChangeVaultOptionsDTO } from './ChangeVaultOptionsDTO' import { ChangeVaultKeyOptionsDTO } from './UseCase/ChangeVaultKeyOptionsDTO'
import { VaultServiceEvent, VaultServiceEventPayload } from './VaultServiceEvent' import { VaultServiceEvent, VaultServiceEventPayload } from './VaultServiceEvent'
import { CreateVault } from './UseCase/CreateVault' import { CreateVault } from './UseCase/CreateVault'
import { AbstractService } from '../Service/AbstractService' import { AbstractService } from '../Service/AbstractService'
@@ -25,23 +24,18 @@ import { GetVault } from './UseCase/GetVault'
import { ChangeVaultKeyOptions } from './UseCase/ChangeVaultKeyOptions' import { ChangeVaultKeyOptions } from './UseCase/ChangeVaultKeyOptions'
import { MutatorClientInterface } from '../Mutator/MutatorClientInterface' import { MutatorClientInterface } from '../Mutator/MutatorClientInterface'
import { AlertService } from '../Alert/AlertService' import { AlertService } from '../Alert/AlertService'
import { ContentType } from '@standardnotes/domain-core'
import { EncryptionProviderInterface } from '../Encryption/EncryptionProviderInterface'
import { KeySystemKeyManagerInterface } from '../KeySystem/KeySystemKeyManagerInterface'
import { GetVaults } from './UseCase/GetVaults' import { GetVaults } from './UseCase/GetVaults'
import { VaultLockServiceInterface } from '../VaultLock/VaultLockServiceInterface'
export class VaultService export class VaultService
extends AbstractService<VaultServiceEvent, VaultServiceEventPayload[VaultServiceEvent]> extends AbstractService<VaultServiceEvent, VaultServiceEventPayload[VaultServiceEvent]>
implements VaultServiceInterface implements VaultServiceInterface
{ {
private lockMap = new Map<VaultListingInterface['uuid'], boolean>()
constructor( constructor(
private sync: SyncServiceInterface, private sync: SyncServiceInterface,
private items: ItemManagerInterface, private items: ItemManagerInterface,
private mutator: MutatorClientInterface, private mutator: MutatorClientInterface,
private encryption: EncryptionProviderInterface, private vaultLocks: VaultLockServiceInterface,
private keys: KeySystemKeyManagerInterface,
private alerts: AlertService, private alerts: AlertService,
private _getVault: GetVault, private _getVault: GetVault,
private _getVaults: GetVaults, private _getVaults: GetVaults,
@@ -49,29 +43,34 @@ export class VaultService
private _moveItemsToVault: MoveItemsToVault, private _moveItemsToVault: MoveItemsToVault,
private _createVault: CreateVault, private _createVault: CreateVault,
private _removeItemFromVault: RemoveItemFromVault, private _removeItemFromVault: RemoveItemFromVault,
private _deleteVaultUse: DeleteVault, private _deleteVault: DeleteVault,
private _rotateVaultKey: RotateVaultKey, private _rotateVaultKey: RotateVaultKey,
eventBus: InternalEventBusInterface, eventBus: InternalEventBusInterface,
) { ) {
super(eventBus) super(eventBus)
}
items.addObserver( override deinit(): void {
[ContentType.TYPES.KeySystemItemsKey, ContentType.TYPES.KeySystemRootKey, ContentType.TYPES.VaultListing], super.deinit()
() => { ;(this.sync as unknown) = undefined
void this.recomputeAllVaultsLockingState() ;(this.items as unknown) = undefined
}, ;(this.mutator as unknown) = undefined
) ;(this.vaultLocks as unknown) = undefined
;(this.alerts as unknown) = undefined
;(this._getVault as unknown) = undefined
;(this._getVaults as unknown) = undefined
;(this._changeVaultKeyOptions as unknown) = undefined
;(this._moveItemsToVault as unknown) = undefined
;(this._createVault as unknown) = undefined
;(this._removeItemFromVault as unknown) = undefined
;(this._deleteVault as unknown) = undefined
;(this._rotateVaultKey as unknown) = undefined
} }
getVaults(): VaultListingInterface[] { getVaults(): VaultListingInterface[] {
return this._getVaults.execute().getValue() return this._getVaults.execute().getValue()
} }
getLockedvaults(): VaultListingInterface[] {
const vaults = this.getVaults()
return vaults.filter((vault) => this.isVaultLocked(vault))
}
public getVault(dto: { keySystemIdentifier: KeySystemIdentifier }): VaultListingInterface | undefined { public getVault(dto: { keySystemIdentifier: KeySystemIdentifier }): VaultListingInterface | undefined {
const result = this._getVault.execute(dto) const result = this._getVault.execute(dto)
if (result.isFailed()) { if (result.isFailed()) {
@@ -90,16 +89,12 @@ export class VaultService
return vault return vault
} }
async createRandomizedVault(dto: { async createRandomizedVault(dto: { name: string; description?: string }): Promise<VaultListingInterface> {
name: string
description?: string
storagePreference: KeySystemRootKeyStorageMode
}): Promise<VaultListingInterface> {
return this.createVaultWithParameters({ return this.createVaultWithParameters({
name: dto.name, name: dto.name,
description: dto.description, description: dto.description,
userInputtedPassword: undefined, userInputtedPassword: undefined,
storagePreference: dto.storagePreference, storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
} }
@@ -132,7 +127,7 @@ export class VaultService
vault: VaultListingInterface, vault: VaultListingInterface,
item: DecryptedItemInterface, item: DecryptedItemInterface,
): Promise<DecryptedItemInterface | undefined> { ): Promise<DecryptedItemInterface | undefined> {
if (this.isVaultLocked(vault)) { if (this.vaultLocks.isVaultLocked(vault)) {
throw new Error('Attempting to add item to locked vault') throw new Error('Attempting to add item to locked vault')
} }
@@ -162,7 +157,7 @@ export class VaultService
throw new Error('Cannot find vault to remove item from') throw new Error('Cannot find vault to remove item from')
} }
if (this.isVaultLocked(vault)) { if (this.vaultLocks.isVaultLocked(vault)) {
throw new Error('Attempting to remove item from locked vault') throw new Error('Attempting to remove item from locked vault')
} }
@@ -175,7 +170,7 @@ export class VaultService
throw new Error('Shared vault must be deleted through SharedVaultService') throw new Error('Shared vault must be deleted through SharedVaultService')
} }
const error = await this._deleteVaultUse.execute(vault) const error = await this._deleteVault.execute(vault)
if (isClientDisplayableError(error)) { if (isClientDisplayableError(error)) {
return false return false
@@ -200,7 +195,7 @@ export class VaultService
} }
async rotateVaultRootKey(vault: VaultListingInterface): Promise<void> { async rotateVaultRootKey(vault: VaultListingInterface): Promise<void> {
if (this.computeVaultLockState(vault) === 'locked') { if (this.vaultLocks.isVaultLocked(vault)) {
throw new Error('Cannot rotate root key of locked vault') throw new Error('Cannot rotate root key of locked vault')
} }
@@ -232,8 +227,8 @@ export class VaultService
return this.getVault({ keySystemIdentifier: latestItem.key_system_identifier }) return this.getVault({ keySystemIdentifier: latestItem.key_system_identifier })
} }
async changeVaultOptions(dto: ChangeVaultOptionsDTO): Promise<void> { async changeVaultOptions(dto: ChangeVaultKeyOptionsDTO): Promise<void> {
if (this.isVaultLocked(dto.vault)) { if (this.vaultLocks.isVaultLocked(dto.vault)) {
throw new Error('Attempting to change vault options on a locked vault') throw new Error('Attempting to change vault options on a locked vault')
} }
@@ -243,99 +238,4 @@ export class VaultService
await this.notifyEventSync(VaultServiceEvent.VaultRootKeyRotated, { vault: dto.vault }) await this.notifyEventSync(VaultServiceEvent.VaultRootKeyRotated, { vault: dto.vault })
} }
} }
public isVaultLocked(vault: VaultListingInterface): boolean {
return this.lockMap.get(vault.uuid) === true
}
public async lockNonPersistentVault(vault: VaultListingInterface): Promise<void> {
if (vault.keyStorageMode === KeySystemRootKeyStorageMode.Synced) {
throw new Error('Vault uses synced root key and cannot be locked')
}
this.keys.clearMemoryOfKeysRelatedToVault(vault)
this.lockMap.set(vault.uuid, true)
void this.notifyEventSync(VaultServiceEvent.VaultLocked, { vault })
}
public async unlockNonPersistentVault(vault: VaultListingInterface, password: string): Promise<boolean> {
if (vault.keyPasswordType !== KeySystemRootKeyPasswordType.UserInputted) {
throw new Error('Vault uses randomized password and cannot be unlocked with user inputted password')
}
if (vault.keyStorageMode === KeySystemRootKeyStorageMode.Synced) {
throw new Error('Vault uses synced root key and cannot be unlocked with user inputted password')
}
const derivedRootKey = this.encryption.deriveUserInputtedKeySystemRootKey({
keyParams: vault.rootKeyParams,
userInputtedPassword: password,
})
this.keys.intakeNonPersistentKeySystemRootKey(derivedRootKey, vault.keyStorageMode)
await this.encryption.decryptErroredPayloads()
if (this.computeVaultLockState(vault) === 'locked') {
this.keys.undoIntakeNonPersistentKeySystemRootKey(vault.systemIdentifier)
return false
}
this.lockMap.set(vault.uuid, false)
void this.notifyEventSync(VaultServiceEvent.VaultUnlocked, { vault })
return true
}
private recomputeAllVaultsLockingState = async (): Promise<void> => {
const vaults = this.getVaults()
for (const vault of vaults) {
const locked = this.computeVaultLockState(vault) === 'locked'
if (this.lockMap.get(vault.uuid) !== locked) {
this.lockMap.set(vault.uuid, locked)
if (locked) {
void this.notifyEvent(VaultServiceEvent.VaultLocked, { vault })
} else {
void this.notifyEvent(VaultServiceEvent.VaultUnlocked, { vault })
}
}
}
}
private computeVaultLockState(vault: VaultListingInterface): 'locked' | 'unlocked' {
const rootKey = this.keys.getPrimaryKeySystemRootKey(vault.systemIdentifier)
if (!rootKey) {
return 'locked'
}
const itemsKey = this.keys.getPrimaryKeySystemItemsKey(vault.systemIdentifier)
if (!itemsKey) {
return 'locked'
}
return 'unlocked'
}
override deinit(): void {
super.deinit()
;(this.sync as unknown) = undefined
;(this.items as unknown) = undefined
;(this.mutator as unknown) = undefined
;(this.encryption as unknown) = undefined
;(this.alerts as unknown) = undefined
;(this._getVault as unknown) = undefined
;(this._getVaults as unknown) = undefined
;(this._changeVaultKeyOptions as unknown) = undefined
;(this._moveItemsToVault as unknown) = undefined
;(this._createVault as unknown) = undefined
;(this._removeItemFromVault as unknown) = undefined
;(this._deleteVaultUse as unknown) = undefined
;(this._rotateVaultKey as unknown) = undefined
this.lockMap.clear()
}
} }

View File

@@ -2,18 +2,10 @@ import { VaultListingInterface } from '@standardnotes/models'
export enum VaultServiceEvent { export enum VaultServiceEvent {
VaultRootKeyRotated = 'VaultRootKeyRotated', VaultRootKeyRotated = 'VaultRootKeyRotated',
VaultUnlocked = 'VaultUnlocked',
VaultLocked = 'VaultLocked',
} }
export type VaultServiceEventPayload = { export type VaultServiceEventPayload = {
[VaultServiceEvent.VaultRootKeyRotated]: { [VaultServiceEvent.VaultRootKeyRotated]: {
vault: VaultListingInterface vault: VaultListingInterface
} }
[VaultServiceEvent.VaultUnlocked]: {
vault: VaultListingInterface
}
[VaultServiceEvent.VaultLocked]: {
vault: VaultListingInterface
}
} }

View File

@@ -6,15 +6,11 @@ import {
} from '@standardnotes/models' } from '@standardnotes/models'
import { AbstractService } from '../Service/AbstractService' import { AbstractService } from '../Service/AbstractService'
import { VaultServiceEvent, VaultServiceEventPayload } from './VaultServiceEvent' import { VaultServiceEvent, VaultServiceEventPayload } from './VaultServiceEvent'
import { ChangeVaultOptionsDTO } from './ChangeVaultOptionsDTO' import { ChangeVaultKeyOptionsDTO } from './UseCase/ChangeVaultKeyOptionsDTO'
export interface VaultServiceInterface export interface VaultServiceInterface
extends AbstractService<VaultServiceEvent, VaultServiceEventPayload[VaultServiceEvent]> { extends AbstractService<VaultServiceEvent, VaultServiceEventPayload[VaultServiceEvent]> {
createRandomizedVault(dto: { createRandomizedVault(dto: { name: string; description?: string }): Promise<VaultListingInterface>
name: string
description?: string
storagePreference: KeySystemRootKeyStorageMode
}): Promise<VaultListingInterface>
createUserInputtedPasswordVault(dto: { createUserInputtedPasswordVault(dto: {
name: string name: string
description?: string description?: string
@@ -24,7 +20,6 @@ export interface VaultServiceInterface
getVaults(): VaultListingInterface[] getVaults(): VaultListingInterface[]
getVault(dto: { keySystemIdentifier: KeySystemIdentifier }): VaultListingInterface | undefined getVault(dto: { keySystemIdentifier: KeySystemIdentifier }): VaultListingInterface | undefined
getLockedvaults(): VaultListingInterface[]
deleteVault(vault: VaultListingInterface): Promise<boolean> deleteVault(vault: VaultListingInterface): Promise<boolean>
moveItemToVault( moveItemToVault(
@@ -40,8 +35,5 @@ export interface VaultServiceInterface
params: { name: string; description: string }, params: { name: string; description: string },
): Promise<VaultListingInterface> ): Promise<VaultListingInterface>
rotateVaultRootKey(vault: VaultListingInterface): Promise<void> rotateVaultRootKey(vault: VaultListingInterface): Promise<void>
changeVaultOptions(dto: ChangeVaultOptionsDTO): Promise<void> changeVaultOptions(dto: ChangeVaultKeyOptionsDTO): Promise<void>
isVaultLocked(vault: VaultListingInterface): boolean
unlockNonPersistentVault(vault: VaultListingInterface, password: string): Promise<boolean>
} }

View File

@@ -9,7 +9,7 @@ import { GetUntrustedPayload } from './../AsymmetricMessage/UseCase/GetUntrusted
import { GetTrustedPayload } from './../AsymmetricMessage/UseCase/GetTrustedPayload' import { GetTrustedPayload } from './../AsymmetricMessage/UseCase/GetTrustedPayload'
import { InviteRecord } from './InviteRecord' import { InviteRecord } from './InviteRecord'
import { VaultUserServiceInterface } from './../VaultUser/VaultUserServiceInterface' import { VaultUserServiceInterface } from './../VaultUser/VaultUserServiceInterface'
import { GetVault } from './../Vaults/UseCase/GetVault' import { GetVault } from '../Vault/UseCase/GetVault'
import { InviteToVault } from './UseCase/InviteToVault' import { InviteToVault } from './UseCase/InviteToVault'
import { GetVaultContacts } from '../VaultUser/UseCase/GetVaultContacts' import { GetVaultContacts } from '../VaultUser/UseCase/GetVaultContacts'
import { SyncServiceInterface } from './../Sync/SyncServiceInterface' import { SyncServiceInterface } from './../Sync/SyncServiceInterface'

View File

@@ -0,0 +1,133 @@
import { GetVaults } from '../Vault/UseCase/GetVaults'
import { KeySystemRootKeyPasswordType, KeySystemRootKeyStorageMode, VaultListingInterface } from '@standardnotes/models'
import { VaultLockServiceInterface } from './VaultLockServiceInterface'
import { VaultLockServiceEvent, VaultLockServiceEventPayload } from './VaultLockServiceEvent'
import { AbstractService } from '../Service/AbstractService'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface'
import { ContentType } from '@standardnotes/domain-core'
import { EncryptionProviderInterface } from '../Encryption/EncryptionProviderInterface'
import { KeySystemKeyManagerInterface } from '../KeySystem/KeySystemKeyManagerInterface'
export class VaultLockService
extends AbstractService<VaultLockServiceEvent, VaultLockServiceEventPayload[VaultLockServiceEvent]>
implements VaultLockServiceInterface
{
private lockMap = new Map<VaultListingInterface['uuid'], boolean>()
constructor(
items: ItemManagerInterface,
private encryption: EncryptionProviderInterface,
private keys: KeySystemKeyManagerInterface,
private _getVaults: GetVaults,
eventBus: InternalEventBusInterface,
) {
super(eventBus)
items.addObserver(
[ContentType.TYPES.KeySystemItemsKey, ContentType.TYPES.KeySystemRootKey, ContentType.TYPES.VaultListing],
() => {
void this.recomputeAllVaultsLockingState()
},
)
}
override deinit(): void {
super.deinit()
;(this.encryption as unknown) = undefined
;(this.keys as unknown) = undefined
;(this._getVaults as unknown) = undefined
this.lockMap.clear()
}
public getLockedvaults(): VaultListingInterface[] {
const vaults = this._getVaults.execute().getValue()
return vaults.filter((vault) => this.isVaultLocked(vault))
}
public isVaultLocked(vault: VaultListingInterface): boolean {
return this.lockMap.get(vault.uuid) === true
}
public isVaultLockable(vault: VaultListingInterface): boolean {
return vault.keyPasswordType === KeySystemRootKeyPasswordType.UserInputted
}
public lockNonPersistentVault(vault: VaultListingInterface): void {
if (vault.keyStorageMode === KeySystemRootKeyStorageMode.Synced) {
throw new Error('Vault uses synced key storage and cannot be locked')
}
if (vault.keyPasswordType !== KeySystemRootKeyPasswordType.UserInputted) {
throw new Error('Vault uses randomized password and cannot be locked')
}
this.keys.clearMemoryOfKeysRelatedToVault(vault)
this.lockMap.set(vault.uuid, true)
void this.notifyEventSync(VaultLockServiceEvent.VaultLocked, { vault })
}
public async unlockNonPersistentVault(vault: VaultListingInterface, password: string): Promise<boolean> {
if (vault.keyPasswordType !== KeySystemRootKeyPasswordType.UserInputted) {
throw new Error('Vault uses randomized password and cannot be unlocked with user inputted password')
}
if (vault.keyStorageMode === KeySystemRootKeyStorageMode.Synced) {
throw new Error('Vault uses synced root key and cannot be unlocked with user inputted password')
}
const derivedRootKey = this.encryption.deriveUserInputtedKeySystemRootKey({
keyParams: vault.rootKeyParams,
userInputtedPassword: password,
})
this.keys.intakeNonPersistentKeySystemRootKey(derivedRootKey, vault.keyStorageMode)
await this.encryption.decryptErroredPayloads()
if (this.computeVaultLockState(vault) === 'locked') {
this.keys.undoIntakeNonPersistentKeySystemRootKey(vault.systemIdentifier)
return false
}
this.lockMap.set(vault.uuid, false)
void this.notifyEventSync(VaultLockServiceEvent.VaultUnlocked, { vault })
return true
}
private recomputeAllVaultsLockingState = async (): Promise<void> => {
const vaults = this._getVaults.execute().getValue()
for (const vault of vaults) {
const locked = this.computeVaultLockState(vault) === 'locked'
if (this.lockMap.get(vault.uuid) !== locked) {
this.lockMap.set(vault.uuid, locked)
if (locked) {
void this.notifyEvent(VaultLockServiceEvent.VaultLocked, { vault })
} else {
void this.notifyEvent(VaultLockServiceEvent.VaultUnlocked, { vault })
}
}
}
}
private computeVaultLockState(vault: VaultListingInterface): 'locked' | 'unlocked' {
const rootKey = this.keys.getPrimaryKeySystemRootKey(vault.systemIdentifier)
if (!rootKey) {
return 'locked'
}
const itemsKey = this.keys.getPrimaryKeySystemItemsKey(vault.systemIdentifier)
if (!itemsKey) {
return 'locked'
}
return 'unlocked'
}
}

View File

@@ -0,0 +1,15 @@
import { VaultListingInterface } from '@standardnotes/models'
export enum VaultLockServiceEvent {
VaultUnlocked = 'VaultUnlocked',
VaultLocked = 'VaultLocked',
}
export type VaultLockServiceEventPayload = {
[VaultLockServiceEvent.VaultUnlocked]: {
vault: VaultListingInterface
}
[VaultLockServiceEvent.VaultLocked]: {
vault: VaultListingInterface
}
}

View File

@@ -0,0 +1,12 @@
import { VaultListingInterface } from '@standardnotes/models'
import { AbstractService } from '../Service/AbstractService'
import { VaultLockServiceEvent, VaultLockServiceEventPayload } from './VaultLockServiceEvent'
export interface VaultLockServiceInterface
extends AbstractService<VaultLockServiceEvent, VaultLockServiceEventPayload[VaultLockServiceEvent]> {
getLockedvaults(): VaultListingInterface[]
isVaultLocked(vault: VaultListingInterface): boolean
isVaultLockable(vault: VaultListingInterface): boolean
lockNonPersistentVault(vault: VaultListingInterface): void
unlockNonPersistentVault(vault: VaultListingInterface, password: string): Promise<boolean>
}

View File

@@ -1,8 +1,9 @@
import { VaultLockServiceInterface } from './../VaultLock/VaultLockServiceInterface'
import { LeaveVault } from './UseCase/LeaveSharedVault' import { LeaveVault } from './UseCase/LeaveSharedVault'
import { GetVault } from './../Vaults/UseCase/GetVault' import { GetVault } from '../Vault/UseCase/GetVault'
import { InternalEventBusInterface } from './../Internal/InternalEventBusInterface' import { InternalEventBusInterface } from './../Internal/InternalEventBusInterface'
import { RemoveVaultMember } from './UseCase/RemoveSharedVaultMember' import { RemoveVaultMember } from './UseCase/RemoveSharedVaultMember'
import { VaultServiceInterface } from './../Vaults/VaultServiceInterface' import { VaultServiceInterface } from '../Vault/VaultServiceInterface'
import { SessionsClientInterface } from './../Session/SessionsClientInterface' import { SessionsClientInterface } from './../Session/SessionsClientInterface'
import { GetVaultUsers } from './UseCase/GetVaultUsers' import { GetVaultUsers } from './UseCase/GetVaultUsers'
import { SharedVaultListingInterface } from '@standardnotes/models' import { SharedVaultListingInterface } from '@standardnotes/models'
@@ -17,6 +18,7 @@ export class VaultUserService extends AbstractService<VaultUserServiceEvent> imp
constructor( constructor(
private session: SessionsClientInterface, private session: SessionsClientInterface,
private vaults: VaultServiceInterface, private vaults: VaultServiceInterface,
private vaultLocks: VaultLockServiceInterface,
private _getVaultUsers: GetVaultUsers, private _getVaultUsers: GetVaultUsers,
private _removeVaultMember: RemoveVaultMember, private _removeVaultMember: RemoveVaultMember,
private _isVaultOwner: IsVaultOwner, private _isVaultOwner: IsVaultOwner,
@@ -66,7 +68,7 @@ export class VaultUserService extends AbstractService<VaultUserServiceEvent> imp
throw new Error('Only vault admins can remove users') throw new Error('Only vault admins can remove users')
} }
if (this.vaults.isVaultLocked(sharedVault)) { if (this.vaultLocks.isVaultLocked(sharedVault)) {
throw new Error('Cannot remove user from locked vault') throw new Error('Cannot remove user from locked vault')
} }

View File

@@ -187,18 +187,21 @@ export * from './VaultInvite/UseCase/SendVaultInvite'
export * from './VaultInvite/VaultInviteService' export * from './VaultInvite/VaultInviteService'
export * from './VaultInvite/VaultInviteServiceEvent' export * from './VaultInvite/VaultInviteServiceEvent'
export * from './VaultInvite/VaultInviteServiceInterface' export * from './VaultInvite/VaultInviteServiceInterface'
export * from './Vaults/ChangeVaultOptionsDTO' export * from './Vault/UseCase/ChangeVaultKeyOptionsDTO'
export * from './Vaults/UseCase/ChangeVaultKeyOptions' export * from './Vault/UseCase/ChangeVaultKeyOptions'
export * from './Vaults/UseCase/CreateVault' export * from './Vault/UseCase/CreateVault'
export * from './Vaults/UseCase/DeleteVault' export * from './Vault/UseCase/DeleteVault'
export * from './Vaults/UseCase/GetVault' export * from './Vault/UseCase/GetVault'
export * from './Vaults/UseCase/GetVaults' export * from './Vault/UseCase/GetVaults'
export * from './Vaults/UseCase/MoveItemsToVault' export * from './Vault/UseCase/MoveItemsToVault'
export * from './Vaults/UseCase/RemoveItemFromVault' export * from './Vault/UseCase/RemoveItemFromVault'
export * from './Vaults/UseCase/RotateVaultKey' export * from './Vault/UseCase/RotateVaultKey'
export * from './Vaults/VaultService' export * from './Vault/VaultService'
export * from './Vaults/VaultServiceEvent' export * from './Vault/VaultServiceEvent'
export * from './Vaults/VaultServiceInterface' export * from './Vault/VaultServiceInterface'
export * from './VaultLock/VaultLockService'
export * from './VaultLock/VaultLockServiceEvent'
export * from './VaultLock/VaultLockServiceInterface'
export * from './VaultUser/UseCase/GetVaultContacts' export * from './VaultUser/UseCase/GetVaultContacts'
export * from './VaultUser/UseCase/GetVaultContacts' export * from './VaultUser/UseCase/GetVaultContacts'
export * from './VaultUser/UseCase/GetVaultUsers' export * from './VaultUser/UseCase/GetVaultUsers'

View File

@@ -75,6 +75,7 @@ import {
VaultInviteServiceInterface, VaultInviteServiceInterface,
UserEventServiceEvent, UserEventServiceEvent,
VaultServiceEvent, VaultServiceEvent,
VaultLockServiceInterface,
} from '@standardnotes/services' } from '@standardnotes/services'
import { import {
PayloadEmitSource, PayloadEmitSource,
@@ -1324,18 +1325,6 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.dependencies.get<HomeServerServiceInterface | undefined>(TYPES.HomeServerService) return this.dependencies.get<HomeServerServiceInterface | undefined>(TYPES.HomeServerService)
} }
public get vaults(): VaultServiceInterface {
return this.dependencies.get<VaultServiceInterface>(TYPES.VaultService)
}
public get contacts(): ContactServiceInterface {
return this.dependencies.get<ContactServiceInterface>(TYPES.ContactService)
}
public get sharedVaults(): SharedVaultServiceInterface {
return this.dependencies.get<SharedVaultServiceInterface>(TYPES.SharedVaultService)
}
public get preferences(): PreferenceServiceInterface { public get preferences(): PreferenceServiceInterface {
return this.dependencies.get<PreferenceServiceInterface>(TYPES.PreferencesService) return this.dependencies.get<PreferenceServiceInterface>(TYPES.PreferencesService)
} }
@@ -1352,6 +1341,14 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.dependencies.get<InternalEventBusInterface>(TYPES.InternalEventBus) return this.dependencies.get<InternalEventBusInterface>(TYPES.InternalEventBus)
} }
public get vaults(): VaultServiceInterface {
return this.dependencies.get<VaultServiceInterface>(TYPES.VaultService)
}
public get vaultLocks(): VaultLockServiceInterface {
return this.dependencies.get<VaultLockServiceInterface>(TYPES.VaultLockService)
}
public get vaultUsers(): VaultUserServiceInterface { public get vaultUsers(): VaultUserServiceInterface {
return this.dependencies.get<VaultUserServiceInterface>(TYPES.VaultUserService) return this.dependencies.get<VaultUserServiceInterface>(TYPES.VaultUserService)
} }
@@ -1360,6 +1357,14 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
return this.dependencies.get<VaultInviteServiceInterface>(TYPES.VaultInviteService) return this.dependencies.get<VaultInviteServiceInterface>(TYPES.VaultInviteService)
} }
public get contacts(): ContactServiceInterface {
return this.dependencies.get<ContactServiceInterface>(TYPES.ContactService)
}
public get sharedVaults(): SharedVaultServiceInterface {
return this.dependencies.get<SharedVaultServiceInterface>(TYPES.SharedVaultService)
}
private get migrations(): MigrationService { private get migrations(): MigrationService {
return this.dependencies.get<MigrationService>(TYPES.MigrationService) return this.dependencies.get<MigrationService>(TYPES.MigrationService)
} }

View File

@@ -117,6 +117,7 @@ import {
GetOwnedSharedVaults, GetOwnedSharedVaults,
ContactBelongsToVault, ContactBelongsToVault,
DeleteContact, DeleteContact,
VaultLockService,
} from '@standardnotes/services' } from '@standardnotes/services'
import { ItemManager } from '../../Services/Items/ItemManager' import { ItemManager } from '../../Services/Items/ItemManager'
import { PayloadManager } from '../../Services/Payloads/PayloadManager' import { PayloadManager } from '../../Services/Payloads/PayloadManager'
@@ -650,6 +651,7 @@ export class Dependencies {
return new VaultUserService( return new VaultUserService(
this.get(TYPES.SessionManager), this.get(TYPES.SessionManager),
this.get(TYPES.VaultService), this.get(TYPES.VaultService),
this.get(TYPES.VaultLockService),
this.get(TYPES.GetVaultUsers), this.get(TYPES.GetVaultUsers),
this.get(TYPES.RemoveVaultMember), this.get(TYPES.RemoveVaultMember),
this.get(TYPES.IsVaultOwner), this.get(TYPES.IsVaultOwner),
@@ -725,13 +727,22 @@ export class Dependencies {
) )
}) })
this.factory.set(TYPES.VaultLockService, () => {
return new VaultLockService(
this.get(TYPES.ItemManager),
this.get(TYPES.EncryptionService),
this.get(TYPES.KeySystemKeyManager),
this.get(TYPES.GetVaults),
this.get(TYPES.InternalEventBus),
)
})
this.factory.set(TYPES.VaultService, () => { this.factory.set(TYPES.VaultService, () => {
return new VaultService( return new VaultService(
this.get(TYPES.SyncService), this.get(TYPES.SyncService),
this.get(TYPES.ItemManager), this.get(TYPES.ItemManager),
this.get(TYPES.MutatorService), this.get(TYPES.MutatorService),
this.get(TYPES.EncryptionService), this.get(TYPES.VaultLockService),
this.get(TYPES.KeySystemKeyManager),
this.get(TYPES.AlertService), this.get(TYPES.AlertService),
this.get(TYPES.GetVault), this.get(TYPES.GetVault),
this.get(TYPES.GetVaults), this.get(TYPES.GetVaults),

View File

@@ -62,6 +62,7 @@ export const TYPES = {
VaultUserService: Symbol.for('VaultUserService'), VaultUserService: Symbol.for('VaultUserService'),
VaultInviteService: Symbol.for('VaultInviteService'), VaultInviteService: Symbol.for('VaultInviteService'),
VaultUserCache: Symbol.for('VaultUserCache'), VaultUserCache: Symbol.for('VaultUserCache'),
VaultLockService: Symbol.for('VaultLockService'),
// Servers // Servers
RevisionServer: Symbol.for('RevisionServer'), RevisionServer: Symbol.for('RevisionServer'),

View File

@@ -11,6 +11,7 @@ export const VaultTests = {
'vaults/signatures.test.js', 'vaults/signatures.test.js',
'vaults/shared_vaults.test.js', 'vaults/shared_vaults.test.js',
'vaults/invites.test.js', 'vaults/invites.test.js',
'vaults/locking.test.js',
'vaults/items.test.js', 'vaults/items.test.js',
'vaults/conflicts.test.js', 'vaults/conflicts.test.js',
'vaults/deletion.test.js', 'vaults/deletion.test.js',

View File

@@ -49,10 +49,6 @@ export class AppContext {
) )
} }
get vaults() {
return this.application.vaults
}
get sessions() { get sessions() {
return this.application.sessions return this.application.sessions
} }
@@ -97,6 +93,14 @@ export class AppContext {
return this.application.sharedVaults return this.application.sharedVaults
} }
get vaults() {
return this.application.vaults
}
get vaultLocks() {
return this.application.vaultLocks
}
get vaultUsers() { get vaultUsers() {
return this.application.vaultUsers return this.application.vaultUsers
} }

View File

@@ -82,6 +82,27 @@ export const createSharedVaultWithUnacceptedButTrustedInvite = async (
return { sharedVault, contact, contactContext, deinitContactContext, invite } return { sharedVault, contact, contactContext, deinitContactContext, invite }
} }
export const createSharedVaultAndInviteContact = async (
createInContext,
inviteContext,
inviteContact,
permissions = SharedVaultPermission.Write,
) => {
const sharedVault = await createSharedVault(createInContext)
await createInContext.vaultInvites.inviteContactToSharedVault(sharedVault, inviteContact, permissions)
const promise = inviteContext.awaitNextSyncSharedVaultFromScratchEvent()
await inviteContext.sync()
await acceptAllInvites(inviteContext)
await promise
return { sharedVault }
}
export const createSharedVaultWithUnacceptedAndUntrustedInvite = async ( export const createSharedVaultWithUnacceptedAndUntrustedInvite = async (
context, context,
permissions = SharedVaultPermission.Write, permissions = SharedVaultPermission.Write,
@@ -113,7 +134,6 @@ export const inviteNewPartyToSharedVault = async (context, sharedVault, permissi
export const createPrivateVault = async (context) => { export const createPrivateVault = async (context) => {
const privateVault = await context.vaults.createRandomizedVault({ const privateVault = await context.vaults.createRandomizedVault({
name: 'My Private Vault', name: 'My Private Vault',
storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
return privateVault return privateVault

View File

@@ -61,7 +61,7 @@
await loadTests(MainRegistry.VaultTests.files); await loadTests(MainRegistry.VaultTests.files);
} }
if (!MainRegistry.VaultTests.enabled.exclusive) { if (!MainRegistry.VaultTests.enabled || !MainRegistry.VaultTests.enabled.exclusive) {
await loadTests(MainRegistry.BaseTests); await loadTests(MainRegistry.BaseTests);
} }

View File

@@ -153,6 +153,16 @@ describe('shared vault deletion', function () {
}) })
it('being removed from a shared vault should delete respective vault listing', async () => { it('being removed from a shared vault should delete respective vault listing', async () => {
console.error('TODO: implement test') const { sharedVault, contactContext, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInvite(context)
await context.vaultUsers.removeUserFromSharedVault(sharedVault, contactContext.userUuid)
await contactContext.sync()
const vault = contactContext.vaults.getVault({ keySystemIdentifier: sharedVault.systemIdentifier })
expect(vault).to.be.undefined
await deinitContactContext()
}) })
}) })

View File

@@ -200,10 +200,6 @@ describe('shared vault invites', function () {
await deinitContactContext() await deinitContactContext()
}) })
it('when inviter keypair changes, recipient should still be able to trust and decrypt previous invite', async () => {
console.error('TODO: implement test')
})
it('should delete all inbound invites after changing user password', async () => { it('should delete all inbound invites after changing user password', async () => {
/** Invites to user are encrypted with old keypair and are no longer decryptable */ /** Invites to user are encrypted with old keypair and are no longer decryptable */
const { contactContext, deinitContactContext } = const { contactContext, deinitContactContext } =

View File

@@ -117,6 +117,28 @@ describe('shared vault items', function () {
}) })
it('adding item to vault while belonging to other vault should move the item to new vault', async () => { it('adding item to vault while belonging to other vault should move the item to new vault', async () => {
console.error('TODO: implement test') const { note, sharedVault, contactContext, contact, deinitContactContext } =
await Collaboration.createSharedVaultWithAcceptedInviteAndNote(context)
const { sharedVault: otherSharedVault } = await Collaboration.createSharedVaultAndInviteContact(
context,
contactContext,
contact,
)
const updatedNote = await Collaboration.moveItemToVault(context, otherSharedVault, note)
expect(updatedNote.key_system_identifier).to.equal(otherSharedVault.systemIdentifier)
expect(updatedNote.shared_vault_uuid).to.equal(otherSharedVault.sharing.sharedVaultUuid)
await contactContext.sync()
const receivedNote = contactContext.items.findItem(note.uuid)
expect(receivedNote).to.not.be.undefined
expect(receivedNote.title).to.equal(note.title)
expect(receivedNote.key_system_identifier).to.equal(otherSharedVault.systemIdentifier)
expect(receivedNote.shared_vault_uuid).to.equal(otherSharedVault.sharing.sharedVaultUuid)
await deinitContactContext()
}) })
}) })

View File

@@ -229,36 +229,4 @@ describe('shared vault key rotation', function () {
await deinitContactContext() await deinitContactContext()
}) })
it('should throw if attempting to change password of locked vault', async () => {
console.error('TODO: implement')
})
it('should respect storage preference when rotating key system root key', async () => {
console.error('TODO: implement')
})
it('should change storage preference from synced to local', async () => {
console.error('TODO: implement')
})
it('should change storage preference from local to synced', async () => {
console.error('TODO: implement')
})
it('should resync key system items key if it is encrypted with noncurrent key system root key', async () => {
console.error('TODO: implement')
})
it('should change password type from user inputted to randomized', async () => {
console.error('TODO: implement')
})
it('should change password type from randomized to user inputted', async () => {
console.error('TODO: implement')
})
it('should not be able to change storage mode of third party vault', async () => {
console.error('TODO: implement')
})
}) })

View File

@@ -0,0 +1,108 @@
import * as Factory from '../lib/factory.js'
import * as Collaboration from '../lib/Collaboration.js'
chai.use(chaiAsPromised)
const expect = chai.expect
describe('vault locking', function () {
this.timeout(Factory.TwentySecondTimeout)
let context
afterEach(async function () {
await context.deinit()
localStorage.clear()
})
beforeEach(async function () {
localStorage.clear()
context = await Factory.createAppContextWithRealCrypto()
await context.launch()
await context.register()
})
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,
})
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,
})
expect(() => context.vaultLocks.lockNonPersistentVault(vault)).to.throw(
Error,
'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',
})
expect(() => context.vaultLocks.lockNonPersistentVault(vault)).to.throw(
Error,
'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,
})
context.vaultLocks.lockNonPersistentVault(vault)
await Factory.expectThrowsAsync(
() => context.vaults.changeVaultOptions({ vault }),
'Attempting to change vault options on a locked vault',
)
})
it('should respect storage preference when rotating key system root key', async () => {
console.error('TODO: implement')
})
it('should change storage preference from synced to local', async () => {
console.error('TODO: implement')
})
it('should change storage preference from local to synced', async () => {
console.error('TODO: implement')
})
it('should resync key system items key if it is encrypted with noncurrent key system root key', async () => {
console.error('TODO: implement')
})
it('should change password type from user inputted to randomized', async () => {
console.error('TODO: implement')
})
it('should change password type from randomized to user inputted', async () => {
console.error('TODO: implement')
})
it('should not be able to change storage mode of third party vault', async () => {
console.error('TODO: implement')
})
})

View File

@@ -42,7 +42,6 @@ describe('vaults', function () {
it('should be able to create an offline vault', async () => { it('should be able to create an offline vault', async () => {
const vault = await vaults.createRandomizedVault({ const vault = await vaults.createRandomizedVault({
name: 'My Vault', name: 'My Vault',
storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
expect(vault.systemIdentifier).to.not.be.undefined expect(vault.systemIdentifier).to.not.be.undefined
@@ -59,7 +58,6 @@ describe('vaults', function () {
await context.application.addPasscode('123') await context.application.addPasscode('123')
const vault = await vaults.createRandomizedVault({ const vault = await vaults.createRandomizedVault({
name: 'My Vault', name: 'My Vault',
storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
expect(vault.systemIdentifier).to.not.be.undefined expect(vault.systemIdentifier).to.not.be.undefined
@@ -75,7 +73,6 @@ describe('vaults', function () {
it('should add item to offline vault', async () => { it('should add item to offline vault', async () => {
const vault = await vaults.createRandomizedVault({ const vault = await vaults.createRandomizedVault({
name: 'My Vault', name: 'My Vault',
storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
const item = await context.createSyncedNote() const item = await context.createSyncedNote()
@@ -89,7 +86,6 @@ describe('vaults', function () {
const appIdentifier = context.identifier const appIdentifier = context.identifier
const vault = await vaults.createRandomizedVault({ const vault = await vaults.createRandomizedVault({
name: 'My Vault', name: 'My Vault',
storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
const note = await context.createSyncedNote('foo', 'bar') const note = await context.createSyncedNote('foo', 'bar')
await vaults.moveItemToVault(vault, note) await vaults.moveItemToVault(vault, note)
@@ -110,7 +106,6 @@ describe('vaults', function () {
const appIdentifier = context.identifier const appIdentifier = context.identifier
const vault = await vaults.createRandomizedVault({ const vault = await vaults.createRandomizedVault({
name: 'My Vault', name: 'My Vault',
storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
const note = await context.createSyncedNote('foo', 'bar') const note = await context.createSyncedNote('foo', 'bar')
await vaults.moveItemToVault(vault, note) await vaults.moveItemToVault(vault, note)
@@ -138,7 +133,6 @@ describe('vaults', function () {
const appIdentifier = context.identifier const appIdentifier = context.identifier
const vault = await vaults.createRandomizedVault({ const vault = await vaults.createRandomizedVault({
name: 'My Vault', name: 'My Vault',
storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
const note = await context.createSyncedNote('foo', 'bar') const note = await context.createSyncedNote('foo', 'bar')
await vaults.moveItemToVault(vault, note) await vaults.moveItemToVault(vault, note)
@@ -168,7 +162,6 @@ describe('vaults', function () {
it('should create a vault', async () => { it('should create a vault', async () => {
const vault = await vaults.createRandomizedVault({ const vault = await vaults.createRandomizedVault({
name: 'My Vault', name: 'My Vault',
storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
expect(vault).to.not.be.undefined expect(vault).to.not.be.undefined
@@ -184,7 +177,6 @@ describe('vaults', function () {
const note = await context.createSyncedNote('foo', 'bar') const note = await context.createSyncedNote('foo', 'bar')
const vault = await vaults.createRandomizedVault({ const vault = await vaults.createRandomizedVault({
name: 'My Vault', name: 'My Vault',
storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
await vaults.moveItemToVault(vault, note) await vaults.moveItemToVault(vault, note)
@@ -198,7 +190,6 @@ describe('vaults', function () {
const appIdentifier = context.identifier const appIdentifier = context.identifier
const vault = await vaults.createRandomizedVault({ const vault = await vaults.createRandomizedVault({
name: 'My Vault', name: 'My Vault',
storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
const note = await context.createSyncedNote('foo', 'bar') const note = await context.createSyncedNote('foo', 'bar')
await vaults.moveItemToVault(vault, note) await vaults.moveItemToVault(vault, note)
@@ -219,7 +210,6 @@ describe('vaults', function () {
it('rotating a key system root key should create a new vault items key', async () => { it('rotating a key system root key should create a new vault items key', async () => {
const vault = await vaults.createRandomizedVault({ const vault = await vaults.createRandomizedVault({
name: 'My Vault', name: 'My Vault',
storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
const keySystemItemsKey = context.keys.getKeySystemItemsKeys(vault.systemIdentifier)[0] const keySystemItemsKey = context.keys.getKeySystemItemsKeys(vault.systemIdentifier)[0]
@@ -235,7 +225,6 @@ describe('vaults', function () {
it('deleting a vault should delete all its items', async () => { it('deleting a vault should delete all its items', async () => {
const vault = await vaults.createRandomizedVault({ const vault = await vaults.createRandomizedVault({
name: 'My Vault', name: 'My Vault',
storagePreference: KeySystemRootKeyStorageMode.Synced,
}) })
const note = await context.createSyncedNote('foo', 'bar') const note = await context.createSyncedNote('foo', 'bar')
await vaults.moveItemToVault(vault, note) await vaults.moveItemToVault(vault, note)

View File

@@ -11,7 +11,7 @@ import {
InternalEventHandlerInterface, InternalEventHandlerInterface,
InternalEventInterface, InternalEventInterface,
StorageKey, StorageKey,
VaultServiceEvent, VaultLockServiceEvent,
} from '@standardnotes/services' } from '@standardnotes/services'
import { VaultDisplayOptions, VaultDisplayOptionsPersistable, VaultListingInterface } from '@standardnotes/models' import { VaultDisplayOptions, VaultDisplayOptionsPersistable, VaultListingInterface } from '@standardnotes/models'
import { VaultDisplayServiceEvent } from './VaultDisplayServiceEvent' import { VaultDisplayServiceEvent } from './VaultDisplayServiceEvent'
@@ -33,8 +33,8 @@ export class VaultDisplayService
this.options = new VaultDisplayOptions({ exclude: [], locked: [] }) this.options = new VaultDisplayOptions({ exclude: [], locked: [] })
internalEventBus.addEventHandler(this, VaultServiceEvent.VaultLocked) internalEventBus.addEventHandler(this, VaultLockServiceEvent.VaultLocked)
internalEventBus.addEventHandler(this, VaultServiceEvent.VaultUnlocked) internalEventBus.addEventHandler(this, VaultLockServiceEvent.VaultUnlocked)
internalEventBus.addEventHandler(this, ApplicationEvent.ApplicationStageChanged) internalEventBus.addEventHandler(this, ApplicationEvent.ApplicationStageChanged)
makeObservable(this, { makeObservable(this, {
@@ -51,7 +51,7 @@ export class VaultDisplayService
} }
async handleEvent(event: InternalEventInterface): Promise<void> { async handleEvent(event: InternalEventInterface): Promise<void> {
if (event.type === VaultServiceEvent.VaultLocked || event.type === VaultServiceEvent.VaultUnlocked) { if (event.type === VaultLockServiceEvent.VaultLocked || event.type === VaultLockServiceEvent.VaultUnlocked) {
this.handleVaultLockingStatusChanged() this.handleVaultLockingStatusChanged()
} else if (event.type === ApplicationEvent.ApplicationStageChanged) { } else if (event.type === ApplicationEvent.ApplicationStageChanged) {
const stage = (event.payload as ApplicationStageChangedEventPayload).stage const stage = (event.payload as ApplicationStageChangedEventPayload).stage
@@ -62,7 +62,7 @@ export class VaultDisplayService
} }
private handleVaultLockingStatusChanged(): void { private handleVaultLockingStatusChanged(): void {
const lockedVaults = this.application.vaults.getLockedvaults() const lockedVaults = this.application.vaultLocks.getLockedvaults()
const options = this.options.newOptionsByIntakingLockedVaults(lockedVaults) const options = this.options.newOptionsByIntakingLockedVaults(lockedVaults)
this.setVaultSelectionOptions(options) this.setVaultSelectionOptions(options)
@@ -90,7 +90,7 @@ export class VaultDisplayService
changeToMultipleVaultDisplayMode(): void { changeToMultipleVaultDisplayMode(): void {
const vaults = this.application.vaults.getVaults() const vaults = this.application.vaults.getVaults()
const lockedVaults = this.application.vaults.getLockedvaults() const lockedVaults = this.application.vaultLocks.getLockedvaults()
const newOptions = new VaultDisplayOptions({ const newOptions = new VaultDisplayOptions({
exclude: vaults exclude: vaults
@@ -103,26 +103,26 @@ export class VaultDisplayService
} }
hideVault = (vault: VaultListingInterface) => { hideVault = (vault: VaultListingInterface) => {
const lockedVaults = this.application.vaults.getLockedvaults() const lockedVaults = this.application.vaultLocks.getLockedvaults()
const newOptions = this.options.newOptionsByExcludingVault(vault, lockedVaults) const newOptions = this.options.newOptionsByExcludingVault(vault, lockedVaults)
this.setVaultSelectionOptions(newOptions) this.setVaultSelectionOptions(newOptions)
} }
unhideVault = async (vault: VaultListingInterface) => { unhideVault = async (vault: VaultListingInterface) => {
if (this.application.vaults.isVaultLocked(vault)) { if (this.application.vaultLocks.isVaultLocked(vault)) {
const unlocked = await this.unlockVault(vault) const unlocked = await this.unlockVault(vault)
if (!unlocked) { if (!unlocked) {
return return
} }
} }
const lockedVaults = this.application.vaults.getLockedvaults() const lockedVaults = this.application.vaultLocks.getLockedvaults()
const newOptions = this.options.newOptionsByUnexcludingVault(vault, lockedVaults) const newOptions = this.options.newOptionsByUnexcludingVault(vault, lockedVaults)
this.setVaultSelectionOptions(newOptions) this.setVaultSelectionOptions(newOptions)
} }
showOnlyVault = async (vault: VaultListingInterface) => { showOnlyVault = async (vault: VaultListingInterface) => {
if (this.application.vaults.isVaultLocked(vault)) { if (this.application.vaultLocks.isVaultLocked(vault)) {
const unlocked = await this.unlockVault(vault) const unlocked = await this.unlockVault(vault)
if (!unlocked) { if (!unlocked) {
return return
@@ -134,7 +134,7 @@ export class VaultDisplayService
} }
async unlockVault(vault: VaultListingInterface): Promise<boolean> { async unlockVault(vault: VaultListingInterface): Promise<boolean> {
if (!this.application.vaults.isVaultLocked(vault)) { if (!this.application.vaultLocks.isVaultLocked(vault)) {
throw new Error('Attempting to unlock a vault that is not locked.') throw new Error('Attempting to unlock a vault that is not locked.')
} }
@@ -161,7 +161,7 @@ export class VaultDisplayService
const password = value.value as string const password = value.value as string
const unlocked = await this.application.vaults.unlockNonPersistentVault(vault, password) const unlocked = await this.application.vaultLocks.unlockNonPersistentVault(vault, password)
if (!unlocked) { if (!unlocked) {
this.application.challenges.setValidationStatusForChallenge(challenge, value, false) this.application.challenges.setValidationStatusForChallenge(challenge, value, false)
resolve(false) resolve(false)

View File

@@ -72,12 +72,12 @@ const VaultItem = ({ vault }: Props) => {
}, [application.sharedVaults, vault]) }, [application.sharedVaults, vault])
const ensureVaultIsUnlocked = useCallback(async () => { const ensureVaultIsUnlocked = useCallback(async () => {
if (!application.vaults.isVaultLocked(vault)) { if (!application.vaultLocks.isVaultLocked(vault)) {
return true return true
} }
const unlocked = await application.vaultDisplayService.unlockVault(vault) const unlocked = await application.vaultDisplayService.unlockVault(vault)
return unlocked return unlocked
}, [application.vaultDisplayService, application.vaults, vault]) }, [application, vault])
const openEditModal = useCallback(async () => { const openEditModal = useCallback(async () => {
if (!(await ensureVaultIsUnlocked())) { if (!(await ensureVaultIsUnlocked())) {

View File

@@ -3,7 +3,7 @@ import Modal, { ModalAction } from '@/Components/Modal/Modal'
import DecoratedInput from '@/Components/Input/DecoratedInput' import DecoratedInput from '@/Components/Input/DecoratedInput'
import { useApplication } from '@/Components/ApplicationProvider' import { useApplication } from '@/Components/ApplicationProvider'
import { import {
ChangeVaultOptionsDTO, ChangeVaultKeyOptionsDTO,
KeySystemRootKeyPasswordType, KeySystemRootKeyPasswordType,
KeySystemRootKeyStorageMode, KeySystemRootKeyStorageMode,
SharedVaultInviteServerHash, SharedVaultInviteServerHash,
@@ -89,7 +89,7 @@ const EditVaultModal: FunctionComponent<Props> = ({ onCloseDialog, existingVault
const isChangingPasswordType = vault.keyPasswordType !== passwordType const isChangingPasswordType = vault.keyPasswordType !== passwordType
const isChangingKeyStorageMode = vault.keyStorageMode !== keyStorageMode const isChangingKeyStorageMode = vault.keyStorageMode !== keyStorageMode
const getPasswordTypeParams = (): ChangeVaultOptionsDTO['newPasswordType'] => { const getPasswordTypeParams = (): ChangeVaultKeyOptionsDTO['newPasswordType'] => {
if (!isChangingPasswordType) { if (!isChangingPasswordType) {
throw new Error('Password type is not changing') throw new Error('Password type is not changing')
} }
@@ -135,7 +135,6 @@ const EditVaultModal: FunctionComponent<Props> = ({ onCloseDialog, existingVault
await application.vaults.createRandomizedVault({ await application.vaults.createRandomizedVault({
name, name,
description, description,
storagePreference: keyStorageMode,
}) })
} }
@@ -169,7 +168,7 @@ const EditVaultModal: FunctionComponent<Props> = ({ onCloseDialog, existingVault
[existingVault, handleDialogClose, handleSubmit], [existingVault, handleDialogClose, handleSubmit],
) )
if (existingVault && application.vaults.isVaultLocked(existingVault)) { if (existingVault && application.vaultLocks.isVaultLocked(existingVault)) {
return <div>Vault is locked.</div> return <div>Vault is locked.</div>
} }

View File

@@ -41,7 +41,7 @@ const ManyVaultSelectionMenu: FunctionComponent = () => {
<Icon type="safe-square" className="mr-2 text-neutral" /> <Icon type="safe-square" className="mr-2 text-neutral" />
<div className="flex w-full items-center gap-1"> <div className="flex w-full items-center gap-1">
{vault.name} {vault.name}
{application.vaults.isVaultLocked(vault) && <Icon className="ml-1" type="lock" size={'small'} />} {application.vaultLocks.isVaultLocked(vault) && <Icon className="ml-1" type="lock" size={'small'} />}
</div> </div>
</MenuSwitchButtonItem> </MenuSwitchButtonItem>
))} ))}

View File

@@ -30,7 +30,7 @@ const SingleVaultSelectionMenu: FunctionComponent = () => {
<MenuRadioButtonItem key={vault.uuid} checked={isVaultVisible(vault)} onClick={() => selectVault(vault)}> <MenuRadioButtonItem key={vault.uuid} checked={isVaultVisible(vault)} onClick={() => selectVault(vault)}>
<div className="flex w-full items-center gap-1"> <div className="flex w-full items-center gap-1">
{vault.name} {vault.name}
{application.vaults.isVaultLocked(vault) && <Icon className="ml-1" type="lock" size={'small'} />} {application.vaultLocks.isVaultLocked(vault) && <Icon className="ml-1" type="lock" size={'small'} />}
</div> </div>
</MenuRadioButtonItem> </MenuRadioButtonItem>
))} ))}

View File

@@ -29,7 +29,7 @@ const AddToVaultMenuOption: FunctionComponent<Props> = ({ iconClassName, items }
const addItemsToVault = useCallback( const addItemsToVault = useCallback(
async (vault: VaultListingInterface) => { async (vault: VaultListingInterface) => {
if (application.vaults.isVaultLocked(vault)) { if (application.vaultLocks.isVaultLocked(vault)) {
const unlocked = await application.vaultDisplayService.unlockVault(vault) const unlocked = await application.vaultDisplayService.unlockVault(vault)
if (!unlocked) { if (!unlocked) {
return return
@@ -40,7 +40,7 @@ const AddToVaultMenuOption: FunctionComponent<Props> = ({ iconClassName, items }
await application.vaults.moveItemToVault(vault, item) await application.vaults.moveItemToVault(vault, item)
} }
}, },
[application.vaultDisplayService, application.vaults, items], [application, items],
) )
const removeItemsFromVault = useCallback(async () => { const removeItemsFromVault = useCallback(async () => {
@@ -50,7 +50,7 @@ const AddToVaultMenuOption: FunctionComponent<Props> = ({ iconClassName, items }
continue continue
} }
if (application.vaults.isVaultLocked(vault)) { if (application.vaultLocks.isVaultLocked(vault)) {
const unlocked = await application.vaultDisplayService.unlockVault(vault) const unlocked = await application.vaultDisplayService.unlockVault(vault)
if (!unlocked) { if (!unlocked) {
return return
@@ -58,7 +58,7 @@ const AddToVaultMenuOption: FunctionComponent<Props> = ({ iconClassName, items }
} }
await application.vaults.removeItemFromVault(item) await application.vaults.removeItemFromVault(item)
} }
}, [application.vaultDisplayService, application.vaults, items]) }, [application, items])
const doesVaultContainItems = (vault: VaultListingInterface) => { const doesVaultContainItems = (vault: VaultListingInterface) => {
return items.every((item) => item.key_system_identifier === vault.systemIdentifier) return items.every((item) => item.key_system_identifier === vault.systemIdentifier)
@@ -140,7 +140,9 @@ const AddToVaultMenuOption: FunctionComponent<Props> = ({ iconClassName, items }
/> />
<div className="flex w-full items-center"> <div className="flex w-full items-center">
{vault.name} {vault.name}
{application.vaults.isVaultLocked(vault) && <Icon className="ml-1" type="lock" size={'small'} />} {application.vaultLocks.isVaultLocked(vault) && (
<Icon className="ml-1" type="lock" size={'small'} />
)}
</div> </div>
</span> </span>
</MenuItem> </MenuItem>