From 98c022813970ff816af43d6af65040729628c110 Mon Sep 17 00:00:00 2001 From: Mo Date: Mon, 24 Jul 2023 14:28:28 -0500 Subject: [PATCH] tests: vaults (#2365) * tests: signature tests * tests: asymmetric * feat: delete contact use case * chore: lint * chore: lint --- .../AsymmetricMessageService.spec.ts | 5 +- .../AsymmetricMessageService.ts | 9 +- .../UseCase/GetTrustedPayload.spec.ts | 63 +++++++++++ .../UseCase/GetTrustedPayload.ts | 11 ++ .../src/Domain/Contacts/ContactService.ts | 24 ++-- .../Contacts/ContactServiceInterface.ts | 11 +- .../Contacts/UseCase/DeleteContact.spec.ts | 87 +++++++++++++++ .../Domain/Contacts/UseCase/DeleteContact.ts | 45 ++++++++ .../SharedVaults/SharedVaultService.spec.ts | 12 +- .../Domain/SharedVaults/SharedVaultService.ts | 35 ++---- .../UseCase/ContactBelongsToVault.ts | 23 ++++ .../UseCase/GetOwnedSharedVaults.ts | 23 ++++ .../SharedVaults/UseCase/GetSharedVaults.ts | 16 +++ .../UseCase/NotifyVaultUsersOfKeyRotation.ts | 5 +- .../UseCase/SendVaultDataChangedMessage.ts | 9 +- .../UseCase/SendVaultKeyChangedMessage.ts | 6 +- .../UseCase/ShareContactWithVault.ts | 7 +- .../Domain/VaultInvite/VaultInviteService.ts | 6 +- .../VaultUser/UseCase/GetVaultContacts.ts | 10 +- .../Domain/VaultUser/UseCase/GetVaultUsers.ts | 27 ++++- .../{IsVaultAdmin.ts => IsVaultOwner.ts} | 2 +- .../src/Domain/VaultUser/VaultUserCache.ts | 15 +++ .../src/Domain/VaultUser/VaultUserService.ts | 18 ++- .../src/Domain/Vaults/UseCase/GetVault.ts | 2 +- .../src/Domain/Vaults/UseCase/GetVaults.ts | 15 +++ .../src/Domain/Vaults/VaultService.ts | 19 ++-- packages/services/src/Domain/index.ts | 8 +- .../Application/Dependencies/Dependencies.ts | 54 +++++++-- .../lib/Application/Dependencies/Types.ts | 8 +- .../snjs/mocha/TestRegistry/VaultTests.js | 39 ++++--- packages/snjs/mocha/lib/AppContext.js | 4 +- packages/snjs/mocha/test.html | 12 +- .../mocha/vaults/asymmetric-messages.test.js | 37 ++++++- packages/snjs/mocha/vaults/contacts.test.js | 10 +- packages/snjs/mocha/vaults/crypto.test.js | 6 +- packages/snjs/mocha/vaults/signatures.test.js | 103 +++++++++++++++++- .../Vaults/Contacts/EditContactModal.tsx | 2 +- .../Panes/Vaults/Invites/InviteItem.tsx | 2 +- .../Vaults/VaultModal/VaultModalInvites.tsx | 2 +- .../Vaults/VaultModal/VaultModalMembers.tsx | 2 +- 40 files changed, 649 insertions(+), 145 deletions(-) create mode 100644 packages/services/src/Domain/AsymmetricMessage/UseCase/GetTrustedPayload.spec.ts create mode 100644 packages/services/src/Domain/Contacts/UseCase/DeleteContact.spec.ts create mode 100644 packages/services/src/Domain/Contacts/UseCase/DeleteContact.ts create mode 100644 packages/services/src/Domain/SharedVaults/UseCase/ContactBelongsToVault.ts create mode 100644 packages/services/src/Domain/SharedVaults/UseCase/GetOwnedSharedVaults.ts create mode 100644 packages/services/src/Domain/SharedVaults/UseCase/GetSharedVaults.ts rename packages/services/src/Domain/VaultUser/UseCase/{IsVaultAdmin.ts => IsVaultOwner.ts} (88%) create mode 100644 packages/services/src/Domain/VaultUser/VaultUserCache.ts create mode 100644 packages/services/src/Domain/Vaults/UseCase/GetVaults.ts diff --git a/packages/services/src/Domain/AsymmetricMessage/AsymmetricMessageService.spec.ts b/packages/services/src/Domain/AsymmetricMessage/AsymmetricMessageService.spec.ts index 556a7e2d0..9f91cd3e4 100644 --- a/packages/services/src/Domain/AsymmetricMessage/AsymmetricMessageService.spec.ts +++ b/packages/services/src/Domain/AsymmetricMessage/AsymmetricMessageService.spec.ts @@ -1,3 +1,4 @@ +import { SessionsClientInterface } from './../Session/SessionsClientInterface' import { EncryptionProviderInterface } from './../Encryption/EncryptionProviderInterface' import { GetUntrustedPayload } from './UseCase/GetUntrustedPayload' import { GetInboundMessages } from './UseCase/GetInboundMessages' @@ -31,6 +32,7 @@ describe('AsymmetricMessageService', () => { let sync: jest.Mocked let mutator: jest.Mocked let encryption: jest.Mocked + let sessions: jest.Mocked let service: AsymmetricMessageService beforeEach(() => { @@ -60,9 +62,10 @@ describe('AsymmetricMessageService', () => { eventBus.addEventHandler = jest.fn() service = new AsymmetricMessageService( - messageServer, encryption, mutator, + sessions, + messageServer, createOrEditContact, findContact, getAllContacts, diff --git a/packages/services/src/Domain/AsymmetricMessage/AsymmetricMessageService.ts b/packages/services/src/Domain/AsymmetricMessage/AsymmetricMessageService.ts index 68c6218a2..c9cc013ca 100644 --- a/packages/services/src/Domain/AsymmetricMessage/AsymmetricMessageService.ts +++ b/packages/services/src/Domain/AsymmetricMessage/AsymmetricMessageService.ts @@ -1,3 +1,4 @@ +import { SessionsClientInterface } from './../Session/SessionsClientInterface' import { MutatorClientInterface } from './../Mutator/MutatorClientInterface' import { AsymmetricMessageServerHash, ClientDisplayableError, isClientDisplayableError } from '@standardnotes/responses' import { SyncEvent, SyncEventReceivedAsymmetricMessagesData } from '../Event/SyncEvent' @@ -39,9 +40,10 @@ export class AsymmetricMessageService implements AsymmetricMessageServiceInterface, InternalEventHandlerInterface { constructor( - private messageServer: AsymmetricMessageServer, private encryption: EncryptionProviderInterface, private mutator: MutatorClientInterface, + private sessions: SessionsClientInterface, + private messageServer: AsymmetricMessageServer, private _createOrEditContact: CreateOrEditContact, private _findContact: FindContact, private _getAllContacts: GetAllContacts, @@ -184,10 +186,6 @@ export class AsymmetricMessageService message: AsymmetricMessageServerHash, payload: AsymmetricMessagePayload, ): Promise { - if (payload.data.recipientUuid !== message.user_uuid) { - return - } - if (payload.type === AsymmetricMessagePayloadType.ContactShare) { await this.handleTrustedContactShareMessage(message, payload) } else if (payload.type === AsymmetricMessagePayloadType.SenderKeypairChanged) { @@ -225,6 +223,7 @@ export class AsymmetricMessageService const result = this._getTrustedPayload.execute({ privateKey: this.encryption.getKeyPair().privateKey, sender: contact.getValue(), + ownUserUuid: this.sessions.userUuid, message, }) diff --git a/packages/services/src/Domain/AsymmetricMessage/UseCase/GetTrustedPayload.spec.ts b/packages/services/src/Domain/AsymmetricMessage/UseCase/GetTrustedPayload.spec.ts new file mode 100644 index 000000000..195831d8b --- /dev/null +++ b/packages/services/src/Domain/AsymmetricMessage/UseCase/GetTrustedPayload.spec.ts @@ -0,0 +1,63 @@ +import { AsymmetricMessageServerHash } from '@standardnotes/responses' +import { AsymmetricMessagePayload, TrustedContactInterface } from '@standardnotes/models' +import { DecryptMessage } from '../../Encryption/UseCase/Asymmetric/DecryptMessage' +import { Result } from '@standardnotes/domain-core' +import { GetTrustedPayload } from './GetTrustedPayload' + +describe('GetTrustedPayload', () => { + let decryptMessage: jest.Mocked + let getTrustedPayload: GetTrustedPayload + + beforeEach(() => { + decryptMessage = {} as jest.Mocked + decryptMessage.execute = jest.fn() + + getTrustedPayload = new GetTrustedPayload(decryptMessage) + }) + + describe('execute', () => { + const mockDto = { + privateKey: 'test-private-key', + message: {} as AsymmetricMessageServerHash, + sender: {} as TrustedContactInterface, + ownUserUuid: 'test-user-uuid', + } + + it('should return failure when decryption fails', () => { + decryptMessage.execute = jest.fn().mockReturnValue(Result.fail('Decryption failed')) + + const result = getTrustedPayload.execute(mockDto) + + expect(result.isFailed()).toBe(true) + expect(result.getError()).toBe('Decryption failed') + }) + + it('should return failure when recipientUuid is not equal to ownUserUuid', () => { + const mockPayload: AsymmetricMessagePayload = { + data: { + recipientUuid: 'another-user-uuid', + }, + } as AsymmetricMessagePayload + decryptMessage.execute = jest.fn().mockReturnValue(Result.ok(mockPayload)) + + const result = getTrustedPayload.execute(mockDto) + + expect(result.isFailed()).toBe(true) + expect(result.getError()).toBe('Message is not for this user') + }) + + it('should return success when recipientUuid is equal to ownUserUuid', () => { + const mockPayload: AsymmetricMessagePayload = { + data: { + recipientUuid: 'test-user-uuid', + }, + } as AsymmetricMessagePayload + decryptMessage.execute = jest.fn().mockReturnValue(Result.ok(mockPayload)) + + const result = getTrustedPayload.execute(mockDto) + + expect(result.isFailed()).toBe(false) + expect(result.getValue()).toBe(mockPayload) + }) + }) +}) diff --git a/packages/services/src/Domain/AsymmetricMessage/UseCase/GetTrustedPayload.ts b/packages/services/src/Domain/AsymmetricMessage/UseCase/GetTrustedPayload.ts index 894760066..510c84a51 100644 --- a/packages/services/src/Domain/AsymmetricMessage/UseCase/GetTrustedPayload.ts +++ b/packages/services/src/Domain/AsymmetricMessage/UseCase/GetTrustedPayload.ts @@ -10,6 +10,7 @@ export class GetTrustedPayload implements SyncUseCaseInterface { const result = this.decryptMessage.execute({ message: dto.message.encrypted_message, @@ -17,6 +18,16 @@ export class GetTrustedPayload implements SyncUseCaseInterface @@ -34,6 +36,7 @@ export class ContactService private user: UserClientInterface, private selfContactManager: SelfContactManager, private encryption: EncryptionProviderInterface, + private _deleteContact: DeleteContact, private _findContact: FindContact, private _getAllContacts: GetAllContacts, private _createOrEditContact: CreateOrEditContact, @@ -167,20 +170,15 @@ export class ContactService return contact } - async deleteContact(contact: TrustedContactInterface): Promise { - if (contact.isMe) { - throw new Error('Cannot delete self') - } - - await this.mutator.setItemToBeDeleted(contact) - await this.sync.sync() + async deleteContact(contact: TrustedContactInterface): Promise> { + return this._deleteContact.execute({ contact, ownUserUuid: this.session.userUuid }) } getAllContacts(): TrustedContactInterface[] { return this._getAllContacts.execute().getValue() } - findTrustedContact(userUuid: string): TrustedContactInterface | undefined { + findContact(userUuid: string): TrustedContactInterface | undefined { const result = this._findContact.execute({ userUuid }) if (result.isFailed()) { return undefined @@ -188,12 +186,12 @@ export class ContactService return result.getValue() } - findTrustedContactForServerUser(user: SharedVaultUserServerHash): TrustedContactInterface | undefined { - return this.findTrustedContact(user.user_uuid) + findContactForServerUser(user: SharedVaultUserServerHash): TrustedContactInterface | undefined { + return this.findContact(user.user_uuid) } - findTrustedContactForInvite(invite: SharedVaultInviteServerHash): TrustedContactInterface | undefined { - return this.findTrustedContact(invite.sender_uuid) + findContactForInvite(invite: SharedVaultInviteServerHash): TrustedContactInterface | undefined { + return this.findContact(invite.sender_uuid) } getCollaborationIDForTrustedContact(contact: TrustedContactInterface): string { @@ -205,7 +203,7 @@ export class ContactService }) } - isItemAuthenticallySigned(item: DecryptedItemInterface): ItemSignatureValidationResult { + getItemSignatureStatus(item: DecryptedItemInterface): ItemSignatureValidationResult { return this._validateItemSigner.execute(item) } diff --git a/packages/services/src/Domain/Contacts/ContactServiceInterface.ts b/packages/services/src/Domain/Contacts/ContactServiceInterface.ts index ff3010ce0..ab3669853 100644 --- a/packages/services/src/Domain/Contacts/ContactServiceInterface.ts +++ b/packages/services/src/Domain/Contacts/ContactServiceInterface.ts @@ -2,6 +2,7 @@ import { DecryptedItemInterface, TrustedContactInterface } from '@standardnotes/ import { AbstractService } from '../Service/AbstractService' import { SharedVaultInviteServerHash, SharedVaultUserServerHash } from '@standardnotes/responses' import { ItemSignatureValidationResult } from './UseCase/Types/ItemSignatureValidationResult' +import { Result } from '@standardnotes/domain-core' export enum ContactServiceEvent {} @@ -26,13 +27,13 @@ export interface ContactServiceInterface extends AbstractService - deleteContact(contact: TrustedContactInterface): Promise + deleteContact(contact: TrustedContactInterface): Promise> getAllContacts(): TrustedContactInterface[] getSelfContact(): TrustedContactInterface | undefined - findTrustedContact(userUuid: string): TrustedContactInterface | undefined - findTrustedContactForServerUser(user: SharedVaultUserServerHash): TrustedContactInterface | undefined - findTrustedContactForInvite(invite: SharedVaultInviteServerHash): TrustedContactInterface | undefined + findContact(userUuid: string): TrustedContactInterface | undefined + findContactForServerUser(user: SharedVaultUserServerHash): TrustedContactInterface | undefined + findContactForInvite(invite: SharedVaultInviteServerHash): TrustedContactInterface | undefined - isItemAuthenticallySigned(item: DecryptedItemInterface): ItemSignatureValidationResult + getItemSignatureStatus(item: DecryptedItemInterface): ItemSignatureValidationResult } diff --git a/packages/services/src/Domain/Contacts/UseCase/DeleteContact.spec.ts b/packages/services/src/Domain/Contacts/UseCase/DeleteContact.spec.ts new file mode 100644 index 000000000..309dbfe67 --- /dev/null +++ b/packages/services/src/Domain/Contacts/UseCase/DeleteContact.spec.ts @@ -0,0 +1,87 @@ +import { ContactBelongsToVault } from './../../SharedVaults/UseCase/ContactBelongsToVault' +import { GetOwnedSharedVaults } from './../../SharedVaults/UseCase/GetOwnedSharedVaults' +import { SyncServiceInterface } from './../../Sync/SyncServiceInterface' +import { MutatorClientInterface } from './../../Mutator/MutatorClientInterface' +import { Result } from '@standardnotes/domain-core' +import { TrustedContactInterface } from '@standardnotes/models' +import { DeleteContact } from './DeleteContact' + +describe('DeleteContact', () => { + let mutator: jest.Mocked + let sync: jest.Mocked + let getOwnedVaults: jest.Mocked + let contactBelongsToVault: jest.Mocked + let deleteContact: DeleteContact + let mockDto: { + contact: TrustedContactInterface + ownUserUuid: string + } + let mockVaults: { id: string }[] + + beforeEach(() => { + mutator = {} as jest.Mocked + sync = {} as jest.Mocked + + getOwnedVaults = {} as jest.Mocked + getOwnedVaults.execute = jest.fn() + + contactBelongsToVault = {} as jest.Mocked + contactBelongsToVault.execute = jest.fn() + + deleteContact = new DeleteContact(mutator, sync, getOwnedVaults, contactBelongsToVault) + + mockDto = { + contact: { isMe: false } as TrustedContactInterface, + ownUserUuid: 'test-user-uuid', + } + + mockVaults = [{ id: 'vault1' }, { id: 'vault2' }] + }) + + describe('execute', () => { + it('should throw error when deleting self', async () => { + mockDto.contact.isMe = true + await expect(deleteContact.execute(mockDto)).rejects.toThrow('Cannot delete self') + }) + + it('should return failure when getting owned vaults fails', async () => { + getOwnedVaults.execute = jest.fn().mockReturnValue(Result.fail('Failed to get owned vaults')) + + const result = await deleteContact.execute(mockDto) + + expect(result.isFailed()).toBe(true) + expect(result.getError()).toBe('Failed to get owned vaults') + }) + + it('should return failure when checking contact membership fails', async () => { + getOwnedVaults.execute = jest.fn().mockReturnValue(Result.ok(mockVaults)) + contactBelongsToVault.execute = jest.fn().mockResolvedValue(Result.fail('Failed to check contact membership')) + + const result = await deleteContact.execute(mockDto) + + expect(result.isFailed()).toBe(true) + expect(result.getError()).toBe('Failed to check contact membership') + }) + + it('should return failure when contact belongs to an owned vault', async () => { + getOwnedVaults.execute = jest.fn().mockReturnValue(Result.ok(mockVaults)) + contactBelongsToVault.execute = jest.fn().mockResolvedValue(Result.ok(true)) + + const result = await deleteContact.execute(mockDto) + + expect(result.isFailed()).toBe(true) + expect(result.getError()).toBe('Cannot delete contact that belongs to an owned vault') + }) + + it('should return success when contact is successfully deleted', async () => { + getOwnedVaults.execute = jest.fn().mockReturnValue(Result.ok(mockVaults)) + contactBelongsToVault.execute = jest.fn().mockResolvedValue(Result.ok(false)) + mutator.setItemToBeDeleted = jest.fn().mockResolvedValue(undefined) + sync.sync = jest.fn().mockResolvedValue(undefined) + + const result = await deleteContact.execute(mockDto) + + expect(result.isFailed()).toBe(false) + }) + }) +}) diff --git a/packages/services/src/Domain/Contacts/UseCase/DeleteContact.ts b/packages/services/src/Domain/Contacts/UseCase/DeleteContact.ts new file mode 100644 index 000000000..2b4f8a6c5 --- /dev/null +++ b/packages/services/src/Domain/Contacts/UseCase/DeleteContact.ts @@ -0,0 +1,45 @@ +import { ContactBelongsToVault } from './../../SharedVaults/UseCase/ContactBelongsToVault' +import { GetOwnedSharedVaults } from './../../SharedVaults/UseCase/GetOwnedSharedVaults' +import { SyncServiceInterface } from './../../Sync/SyncServiceInterface' +import { MutatorClientInterface } from './../../Mutator/MutatorClientInterface' +import { Result, UseCaseInterface } from '@standardnotes/domain-core' +import { TrustedContactInterface } from '@standardnotes/models' + +export class DeleteContact implements UseCaseInterface { + constructor( + private mutator: MutatorClientInterface, + private sync: SyncServiceInterface, + private getOwnedVaults: GetOwnedSharedVaults, + private contactBelongsToVault: ContactBelongsToVault, + ) {} + + async execute(dto: { contact: TrustedContactInterface; ownUserUuid: string }): Promise> { + if (dto.contact.isMe) { + throw new Error('Cannot delete self') + } + + const vaults = this.getOwnedVaults.execute({ userUuid: dto.ownUserUuid }) + if (vaults.isFailed()) { + return Result.fail('Failed to get owned vaults') + } + + for (const vault of vaults.getValue()) { + const contactBelongsToVault = await this.contactBelongsToVault.execute({ + contact: dto.contact, + vault: vault, + }) + if (contactBelongsToVault.isFailed()) { + return Result.fail('Failed to check contact membership') + } + + if (contactBelongsToVault.getValue()) { + return Result.fail('Cannot delete contact that belongs to an owned vault') + } + } + + await this.mutator.setItemToBeDeleted(dto.contact) + await this.sync.sync() + + return Result.ok() + } +} diff --git a/packages/services/src/Domain/SharedVaults/SharedVaultService.spec.ts b/packages/services/src/Domain/SharedVaults/SharedVaultService.spec.ts index 4d7c021f3..c96bc8e17 100644 --- a/packages/services/src/Domain/SharedVaults/SharedVaultService.spec.ts +++ b/packages/services/src/Domain/SharedVaults/SharedVaultService.spec.ts @@ -1,4 +1,6 @@ -import { IsVaultAdmin } from './../VaultUser/UseCase/IsVaultAdmin' +import { InternalEventBusInterface } from './../Internal/InternalEventBusInterface' +import { GetOwnedSharedVaults } from './UseCase/GetOwnedSharedVaults' +import { IsVaultOwner } from './../VaultUser/UseCase/IsVaultOwner' import { EncryptionProviderInterface } from './../Encryption/EncryptionProviderInterface' import { DeleteSharedVault } from './UseCase/DeleteSharedVault' import { ConvertToSharedVault } from './UseCase/ConvertToSharedVault' @@ -14,8 +16,6 @@ import { SharedVaultService } from './SharedVaultService' import { SyncServiceInterface } from '../Sync/SyncServiceInterface' import { ItemManagerInterface } from '../Item/ItemManagerInterface' import { SessionsClientInterface } from '../Session/SessionsClientInterface' -import { VaultServiceInterface } from '../Vaults/VaultServiceInterface' -import { InternalEventBusInterface } from '../..' import { ContactPublicKeySetInterface, TrustedContactInterface } from '@standardnotes/models' describe('SharedVaultService', () => { @@ -30,8 +30,8 @@ describe('SharedVaultService', () => { const encryption = {} as jest.Mocked const session = {} as jest.Mocked - const vaults = {} as jest.Mocked const getVault = {} as jest.Mocked + const getOwnedVaults = {} as jest.Mocked const createSharedVaultUseCase = {} as jest.Mocked const handleKeyPairChange = {} as jest.Mocked const notifyVaultUsersOfKeyRotation = {} as jest.Mocked @@ -41,7 +41,7 @@ describe('SharedVaultService', () => { const shareContactWithVault = {} as jest.Mocked const convertToSharedVault = {} as jest.Mocked const deleteSharedVaultUseCase = {} as jest.Mocked - const isVaultAdmin = {} as jest.Mocked + const isVaultAdmin = {} as jest.Mocked const eventBus = {} as jest.Mocked eventBus.addEventHandler = jest.fn() @@ -50,8 +50,8 @@ describe('SharedVaultService', () => { items, encryption, session, - vaults, getVault, + getOwnedVaults, createSharedVaultUseCase, handleKeyPairChange, notifyVaultUsersOfKeyRotation, diff --git a/packages/services/src/Domain/SharedVaults/SharedVaultService.ts b/packages/services/src/Domain/SharedVaults/SharedVaultService.ts index 40b530f0f..c31516fac 100644 --- a/packages/services/src/Domain/SharedVaults/SharedVaultService.ts +++ b/packages/services/src/Domain/SharedVaults/SharedVaultService.ts @@ -18,7 +18,6 @@ import { InternalEventBusInterface } from '../Internal/InternalEventBusInterface import { SyncEvent } from '../Event/SyncEvent' import { SessionEvent } from '../Session/SessionEvent' import { InternalEventInterface } from '../Internal/InternalEventInterface' -import { VaultServiceInterface } from '../Vaults/VaultServiceInterface' import { UserEventServiceEvent, UserEventServiceEventPayload } from '../UserEvent/UserEventServiceEvent' import { DeleteThirdPartyVault } from './UseCase/DeleteExternalSharedVault' import { DeleteSharedVault } from './UseCase/DeleteSharedVault' @@ -33,7 +32,8 @@ import { ContentType } from '@standardnotes/domain-core' import { HandleKeyPairChange } from '../Contacts/UseCase/HandleKeyPairChange' import { FindContact } from '../Contacts/UseCase/FindContact' import { EncryptionProviderInterface } from '../Encryption/EncryptionProviderInterface' -import { IsVaultAdmin } from '../VaultUser/UseCase/IsVaultAdmin' +import { IsVaultOwner } from '../VaultUser/UseCase/IsVaultOwner' +import { GetOwnedSharedVaults } from './UseCase/GetOwnedSharedVaults' export class SharedVaultService extends AbstractService @@ -43,9 +43,9 @@ export class SharedVaultService private items: ItemManagerInterface, private encryption: EncryptionProviderInterface, private session: SessionsClientInterface, - private vaults: VaultServiceInterface, private _getVault: GetVault, - private _createSharedVaultUseCase: CreateSharedVault, + private _getOwnedSharedVaults: GetOwnedSharedVaults, + private _createSharedVault: CreateSharedVault, private _handleKeyPairChange: HandleKeyPairChange, private _notifyVaultUsersOfKeyRotation: NotifyVaultUsersOfKeyRotation, private _sendVaultDataChangeMessage: SendVaultDataChangedMessage, @@ -53,8 +53,8 @@ export class SharedVaultService private _deleteThirdPartyVault: DeleteThirdPartyVault, private _shareContactWithVault: ShareContactWithVault, private _convertToSharedVault: ConvertToSharedVault, - private _deleteSharedVaultUseCase: DeleteSharedVault, - private _isVaultAdmin: IsVaultAdmin, + private _deleteSharedVault: DeleteSharedVault, + private _isVaultAdmin: IsVaultOwner, eventBus: InternalEventBusInterface, ) { super(eventBus) @@ -81,9 +81,8 @@ export class SharedVaultService ;(this.items as unknown) = undefined ;(this.encryption as unknown) = undefined ;(this.session as unknown) = undefined - ;(this.vaults as unknown) = undefined ;(this._getVault as unknown) = undefined - ;(this._createSharedVaultUseCase as unknown) = undefined + ;(this._createSharedVault as unknown) = undefined ;(this._handleKeyPairChange as unknown) = undefined ;(this._notifyVaultUsersOfKeyRotation as unknown) = undefined ;(this._sendVaultDataChangeMessage as unknown) = undefined @@ -91,7 +90,7 @@ export class SharedVaultService ;(this._deleteThirdPartyVault as unknown) = undefined ;(this._shareContactWithVault as unknown) = undefined ;(this._convertToSharedVault as unknown) = undefined - ;(this._deleteSharedVaultUseCase as unknown) = undefined + ;(this._deleteSharedVault as unknown) = undefined ;(this._isVaultAdmin as unknown) = undefined } @@ -173,7 +172,7 @@ export class SharedVaultService userInputtedPassword: string | undefined storagePreference?: KeySystemRootKeyStorageMode }): Promise { - return this._createSharedVaultUseCase.execute({ + return this._createSharedVault.execute({ vaultName: dto.name, vaultDescription: dto.description, userInputtedPassword: dto.userInputtedPassword, @@ -187,11 +186,6 @@ export class SharedVaultService return this._convertToSharedVault.execute({ vault }) } - private getAllSharedVaults(): SharedVaultListingInterface[] { - const vaults = this.vaults.getVaults().filter((vault) => vault.isSharedVaultListing()) - return vaults as SharedVaultListingInterface[] - } - private async handleTrustedContactsChange(contacts: TrustedContactInterface[]): Promise { for (const contact of contacts) { if (contact.isMe) { @@ -220,7 +214,7 @@ export class SharedVaultService } public async deleteSharedVault(sharedVault: SharedVaultListingInterface): Promise { - return this._deleteSharedVaultUseCase.execute({ sharedVault }) + return this._deleteSharedVault.execute({ sharedVault }) } async shareContactWithVaults(contact: TrustedContactInterface): Promise { @@ -228,14 +222,7 @@ export class SharedVaultService throw new Error('Cannot share self contact') } - const ownedVaults = this.getAllSharedVaults().filter((vault) => { - return this._isVaultAdmin - .execute({ - sharedVault: vault, - userUuid: this.session.userUuid, - }) - .getValue() - }) + const ownedVaults = this._getOwnedSharedVaults.execute({ userUuid: this.session.userUuid }).getValue() for (const vault of ownedVaults) { await this._shareContactWithVault.execute({ diff --git a/packages/services/src/Domain/SharedVaults/UseCase/ContactBelongsToVault.ts b/packages/services/src/Domain/SharedVaults/UseCase/ContactBelongsToVault.ts new file mode 100644 index 000000000..613681c38 --- /dev/null +++ b/packages/services/src/Domain/SharedVaults/UseCase/ContactBelongsToVault.ts @@ -0,0 +1,23 @@ +import { GetVaultUsers } from './../../VaultUser/UseCase/GetVaultUsers' +import { Result, UseCaseInterface } from '@standardnotes/domain-core' +import { SharedVaultListingInterface, TrustedContactInterface } from '@standardnotes/models' + +export class ContactBelongsToVault implements UseCaseInterface { + constructor(private getVaultUsers: GetVaultUsers) {} + + async execute(dto: { + contact: TrustedContactInterface + vault: SharedVaultListingInterface + }): Promise> { + const users = await this.getVaultUsers.execute({ + sharedVaultUuid: dto.vault.sharing.sharedVaultUuid, + readFromCache: false, + }) + + if (users.isFailed()) { + return Result.fail('Failed to get vault users') + } + + return Result.ok(users.getValue().some((u) => u.user_uuid === dto.contact.contactUuid)) + } +} diff --git a/packages/services/src/Domain/SharedVaults/UseCase/GetOwnedSharedVaults.ts b/packages/services/src/Domain/SharedVaults/UseCase/GetOwnedSharedVaults.ts new file mode 100644 index 000000000..fb9eda1b5 --- /dev/null +++ b/packages/services/src/Domain/SharedVaults/UseCase/GetOwnedSharedVaults.ts @@ -0,0 +1,23 @@ +import { IsVaultOwner } from './../../VaultUser/UseCase/IsVaultOwner' +import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core' +import { SharedVaultListingInterface } from '@standardnotes/models' +import { GetSharedVaults } from './GetSharedVaults' + +export class GetOwnedSharedVaults implements SyncUseCaseInterface { + constructor(private getSharedVaults: GetSharedVaults, private isVaultOwnwer: IsVaultOwner) {} + + execute(dto: { userUuid: string }): Result { + const sharedVaults = this.getSharedVaults.execute().getValue() + + const ownedVaults = sharedVaults.filter((vault) => { + return this.isVaultOwnwer + .execute({ + sharedVault: vault, + userUuid: dto.userUuid, + }) + .getValue() + }) + + return Result.ok(ownedVaults) + } +} diff --git a/packages/services/src/Domain/SharedVaults/UseCase/GetSharedVaults.ts b/packages/services/src/Domain/SharedVaults/UseCase/GetSharedVaults.ts new file mode 100644 index 000000000..8a7eb6ce1 --- /dev/null +++ b/packages/services/src/Domain/SharedVaults/UseCase/GetSharedVaults.ts @@ -0,0 +1,16 @@ +import { GetVaults } from './../../Vaults/UseCase/GetVaults' +import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core' +import { SharedVaultListingInterface } from '@standardnotes/models' + +export class GetSharedVaults implements SyncUseCaseInterface { + constructor(private getVaults: GetVaults) {} + + execute(): Result { + const vaults = this.getVaults + .execute() + .getValue() + .filter((vault) => vault.isSharedVaultListing()) + + return Result.ok(vaults as SharedVaultListingInterface[]) + } +} diff --git a/packages/services/src/Domain/SharedVaults/UseCase/NotifyVaultUsersOfKeyRotation.ts b/packages/services/src/Domain/SharedVaults/UseCase/NotifyVaultUsersOfKeyRotation.ts index 970a3b28c..a7c6bd1ff 100644 --- a/packages/services/src/Domain/SharedVaults/UseCase/NotifyVaultUsersOfKeyRotation.ts +++ b/packages/services/src/Domain/SharedVaults/UseCase/NotifyVaultUsersOfKeyRotation.ts @@ -44,7 +44,10 @@ export class NotifyVaultUsersOfKeyRotation implements UseCaseInterface { await this.deleteAllInvites(params.sharedVault.sharing.sharedVaultUuid) - const contacts = await this.getVaultContacts.execute(params.sharedVault.sharing.sharedVaultUuid) + const contacts = await this.getVaultContacts.execute({ + sharedVaultUuid: params.sharedVault.sharing.sharedVaultUuid, + readFromCache: false, + }) for (const invite of existingInvites.getValue()) { const recipient = this.findContact.execute({ userUuid: invite.user_uuid }) diff --git a/packages/services/src/Domain/SharedVaults/UseCase/SendVaultDataChangedMessage.ts b/packages/services/src/Domain/SharedVaults/UseCase/SendVaultDataChangedMessage.ts index 5a80dc850..7b07c826e 100644 --- a/packages/services/src/Domain/SharedVaults/UseCase/SendVaultDataChangedMessage.ts +++ b/packages/services/src/Domain/SharedVaults/UseCase/SendVaultDataChangedMessage.ts @@ -29,13 +29,16 @@ export class SendVaultDataChangedMessage implements UseCaseInterface { signing: PkcKeyPair } }): Promise> { - const users = await this.getVaultUsers.execute({ sharedVaultUuid: params.vault.sharing.sharedVaultUuid }) - if (!users) { + const users = await this.getVaultUsers.execute({ + sharedVaultUuid: params.vault.sharing.sharedVaultUuid, + readFromCache: false, + }) + if (users.isFailed()) { return Result.fail('Cannot send metadata changed message; users not found') } const errors: string[] = [] - for (const user of users) { + for (const user of users.getValue()) { if (user.user_uuid === params.senderUuid) { continue } diff --git a/packages/services/src/Domain/SharedVaults/UseCase/SendVaultKeyChangedMessage.ts b/packages/services/src/Domain/SharedVaults/UseCase/SendVaultKeyChangedMessage.ts index 1af0edc38..9a2d183d4 100644 --- a/packages/services/src/Domain/SharedVaults/UseCase/SendVaultKeyChangedMessage.ts +++ b/packages/services/src/Domain/SharedVaults/UseCase/SendVaultKeyChangedMessage.ts @@ -32,14 +32,14 @@ export class SendVaultKeyChangedMessage implements UseCaseInterface { signing: PkcKeyPair } }): Promise> { - const users = await this.getVaultUsers.execute({ sharedVaultUuid: params.sharedVaultUuid }) - if (!users) { + const users = await this.getVaultUsers.execute({ sharedVaultUuid: params.sharedVaultUuid, readFromCache: false }) + if (users.isFailed()) { return Result.fail('Cannot send root key changed message; users not found') } const errors: string[] = [] - for (const user of users) { + for (const user of users.getValue()) { if (user.user_uuid === params.senderUuid) { continue } diff --git a/packages/services/src/Domain/SharedVaults/UseCase/ShareContactWithVault.ts b/packages/services/src/Domain/SharedVaults/UseCase/ShareContactWithVault.ts index 30a69e8ef..cee580b35 100644 --- a/packages/services/src/Domain/SharedVaults/UseCase/ShareContactWithVault.ts +++ b/packages/services/src/Domain/SharedVaults/UseCase/ShareContactWithVault.ts @@ -33,17 +33,18 @@ export class ShareContactWithVault implements UseCaseInterface { const users = await this.getVaultUsers.execute({ sharedVaultUuid: params.sharedVault.sharing.sharedVaultUuid, + readFromCache: false, }) - if (!users) { + if (users.isFailed()) { return Result.fail('Cannot share contact; shared vault users not found') } - if (users.length === 0) { + if (users.getValue().length === 0) { return Result.ok() } - for (const vaultUser of users) { + for (const vaultUser of users.getValue()) { if (vaultUser.user_uuid === params.senderUserUuid) { continue } diff --git a/packages/services/src/Domain/VaultInvite/VaultInviteService.ts b/packages/services/src/Domain/VaultInvite/VaultInviteService.ts index 8ad6416fe..066f48f2f 100644 --- a/packages/services/src/Domain/VaultInvite/VaultInviteService.ts +++ b/packages/services/src/Domain/VaultInvite/VaultInviteService.ts @@ -175,7 +175,10 @@ export class VaultInviteService contact: TrustedContactInterface, permissions: SharedVaultPermission, ): Promise> { - const contactsResult = await this._getVaultContacts.execute(sharedVault.sharing.sharedVaultUuid) + const contactsResult = await this._getVaultContacts.execute({ + sharedVaultUuid: sharedVault.sharing.sharedVaultUuid, + readFromCache: false, + }) if (contactsResult.isFailed()) { return Result.fail(contactsResult.getError()) } @@ -241,6 +244,7 @@ export class VaultInviteService const trustedMessage = this._getTrustedPayload.execute({ message: invite, privateKey: this.encryption.getKeyPair().privateKey, + ownUserUuid: this.session.userUuid, sender: sender.getValue(), }) diff --git a/packages/services/src/Domain/VaultUser/UseCase/GetVaultContacts.ts b/packages/services/src/Domain/VaultUser/UseCase/GetVaultContacts.ts index eea864bc8..ee7ccce5e 100644 --- a/packages/services/src/Domain/VaultUser/UseCase/GetVaultContacts.ts +++ b/packages/services/src/Domain/VaultUser/UseCase/GetVaultContacts.ts @@ -7,13 +7,17 @@ import { Result, UseCaseInterface } from '@standardnotes/domain-core' export class GetVaultContacts implements UseCaseInterface { constructor(private findContact: FindContact, private getVaultUsers: GetVaultUsers) {} - async execute(sharedVaultUuid: string): Promise> { - const users = await this.getVaultUsers.execute({ sharedVaultUuid }) - if (!users) { + async execute(dto: { sharedVaultUuid: string; readFromCache: boolean }): Promise> { + const users = await this.getVaultUsers.execute({ + sharedVaultUuid: dto.sharedVaultUuid, + readFromCache: dto.readFromCache, + }) + if (users.isFailed()) { return Result.fail('Failed to get vault users') } const contacts = users + .getValue() .map((user) => this.findContact.execute({ userUuid: user.user_uuid })) .map((result) => (result.isFailed() ? undefined : result.getValue())) .filter(isNotUndefined) diff --git a/packages/services/src/Domain/VaultUser/UseCase/GetVaultUsers.ts b/packages/services/src/Domain/VaultUser/UseCase/GetVaultUsers.ts index 173faeff0..60cc982aa 100644 --- a/packages/services/src/Domain/VaultUser/UseCase/GetVaultUsers.ts +++ b/packages/services/src/Domain/VaultUser/UseCase/GetVaultUsers.ts @@ -1,16 +1,31 @@ -import { SharedVaultUserServerHash, isErrorResponse } from '@standardnotes/responses' +import { VaultUserCache } from './../VaultUserCache' +import { SharedVaultUserServerHash, getErrorFromErrorResponse, isErrorResponse } from '@standardnotes/responses' import { SharedVaultUsersServerInterface } from '@standardnotes/api' +import { Result, UseCaseInterface } from '@standardnotes/domain-core' -export class GetVaultUsers { - constructor(private vaultUsersServer: SharedVaultUsersServerInterface) {} +export class GetVaultUsers implements UseCaseInterface { + constructor(private vaultUsersServer: SharedVaultUsersServerInterface, private cache: VaultUserCache) {} + + async execute(params: { + sharedVaultUuid: string + readFromCache: boolean + }): Promise> { + if (params.readFromCache) { + const cachedUsers = this.cache.get(params.sharedVaultUuid) + + if (cachedUsers) { + return Result.ok(cachedUsers) + } + } - async execute(params: { sharedVaultUuid: string }): Promise { const response = await this.vaultUsersServer.getSharedVaultUsers({ sharedVaultUuid: params.sharedVaultUuid }) if (isErrorResponse(response)) { - return undefined + return Result.fail(getErrorFromErrorResponse(response).message) } - return response.data.users + this.cache.set(params.sharedVaultUuid, response.data.users) + + return Result.ok(response.data.users) } } diff --git a/packages/services/src/Domain/VaultUser/UseCase/IsVaultAdmin.ts b/packages/services/src/Domain/VaultUser/UseCase/IsVaultOwner.ts similarity index 88% rename from packages/services/src/Domain/VaultUser/UseCase/IsVaultAdmin.ts rename to packages/services/src/Domain/VaultUser/UseCase/IsVaultOwner.ts index edf081462..44f85bfa6 100644 --- a/packages/services/src/Domain/VaultUser/UseCase/IsVaultAdmin.ts +++ b/packages/services/src/Domain/VaultUser/UseCase/IsVaultOwner.ts @@ -1,7 +1,7 @@ import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core' import { SharedVaultListingInterface } from '@standardnotes/models' -export class IsVaultAdmin implements SyncUseCaseInterface { +export class IsVaultOwner implements SyncUseCaseInterface { execute(dto: { sharedVault: SharedVaultListingInterface; userUuid: string }): Result { if (!dto.sharedVault.sharing.ownerUserUuid) { throw new Error(`Shared vault ${dto.sharedVault.sharing.sharedVaultUuid} does not have an owner user uuid`) diff --git a/packages/services/src/Domain/VaultUser/VaultUserCache.ts b/packages/services/src/Domain/VaultUser/VaultUserCache.ts new file mode 100644 index 000000000..7f7a85cd9 --- /dev/null +++ b/packages/services/src/Domain/VaultUser/VaultUserCache.ts @@ -0,0 +1,15 @@ +import { SharedVaultUserServerHash } from '@standardnotes/responses' + +type SharedVaultUuid = string + +export class VaultUserCache { + private cache = new Map() + + public get(sharedVaultUuid: SharedVaultUuid): SharedVaultUserServerHash[] | undefined { + return this.cache.get(sharedVaultUuid) + } + + public set(sharedVaultUuid: SharedVaultUuid, users: SharedVaultUserServerHash[]): void { + this.cache.set(sharedVaultUuid, users) + } +} diff --git a/packages/services/src/Domain/VaultUser/VaultUserService.ts b/packages/services/src/Domain/VaultUser/VaultUserService.ts index 5e4301eda..c34d853e9 100644 --- a/packages/services/src/Domain/VaultUser/VaultUserService.ts +++ b/packages/services/src/Domain/VaultUser/VaultUserService.ts @@ -11,7 +11,7 @@ import { ClientDisplayableError, SharedVaultUserServerHash, isClientDisplayableE import { AbstractService } from './../Service/AbstractService' import { VaultUserServiceEvent } from './VaultUserServiceEvent' import { Result } from '@standardnotes/domain-core' -import { IsVaultAdmin } from './UseCase/IsVaultAdmin' +import { IsVaultOwner } from './UseCase/IsVaultOwner' export class VaultUserService extends AbstractService implements VaultUserServiceInterface { constructor( @@ -19,7 +19,7 @@ export class VaultUserService extends AbstractService imp private vaults: VaultServiceInterface, private _getVaultUsers: GetVaultUsers, private _removeVaultMember: RemoveVaultMember, - private _isVaultAdmin: IsVaultAdmin, + private _isVaultOwner: IsVaultOwner, private _getVault: GetVault, private _leaveVault: LeaveVault, eventBus: InternalEventBusInterface, @@ -33,7 +33,7 @@ export class VaultUserService extends AbstractService imp ;(this.vaults as unknown) = undefined ;(this._getVaultUsers as unknown) = undefined ;(this._removeVaultMember as unknown) = undefined - ;(this._isVaultAdmin as unknown) = undefined + ;(this._isVaultOwner as unknown) = undefined ;(this._getVault as unknown) = undefined ;(this._leaveVault as unknown) = undefined } @@ -41,11 +41,19 @@ export class VaultUserService extends AbstractService imp public async getSharedVaultUsers( sharedVault: SharedVaultListingInterface, ): Promise { - return this._getVaultUsers.execute({ sharedVaultUuid: sharedVault.sharing.sharedVaultUuid }) + const result = await this._getVaultUsers.execute({ + sharedVaultUuid: sharedVault.sharing.sharedVaultUuid, + readFromCache: false, + }) + if (result.isFailed()) { + return undefined + } + + return result.getValue() } public isCurrentUserSharedVaultAdmin(sharedVault: SharedVaultListingInterface): boolean { - return this._isVaultAdmin + return this._isVaultOwner .execute({ sharedVault, userUuid: this.session.userUuid, diff --git a/packages/services/src/Domain/Vaults/UseCase/GetVault.ts b/packages/services/src/Domain/Vaults/UseCase/GetVault.ts index 68c0cca66..1930bd702 100644 --- a/packages/services/src/Domain/Vaults/UseCase/GetVault.ts +++ b/packages/services/src/Domain/Vaults/UseCase/GetVault.ts @@ -20,7 +20,7 @@ export class GetVault implements SyncUseCaseInterface { } else { const result = vaults.find((listing) => listing.sharing?.sharedVaultUuid === query.sharedVaultUuid) as T if (!result) { - return Result.fail('Vault not found') + return Result.fail('Shared vault not found') } return Result.ok(result) diff --git a/packages/services/src/Domain/Vaults/UseCase/GetVaults.ts b/packages/services/src/Domain/Vaults/UseCase/GetVaults.ts new file mode 100644 index 000000000..bdce75091 --- /dev/null +++ b/packages/services/src/Domain/Vaults/UseCase/GetVaults.ts @@ -0,0 +1,15 @@ +import { ContentType, Result, SyncUseCaseInterface } from '@standardnotes/domain-core' +import { VaultListingInterface } from '@standardnotes/models' +import { ItemManagerInterface } from '../../Item/ItemManagerInterface' + +export class GetVaults implements SyncUseCaseInterface { + constructor(private items: ItemManagerInterface) {} + + execute(): Result { + const vaults = this.items.getItems(ContentType.TYPES.VaultListing).sort((a, b) => { + return a.name.localeCompare(b.name) + }) + + return Result.ok(vaults) + } +} diff --git a/packages/services/src/Domain/Vaults/VaultService.ts b/packages/services/src/Domain/Vaults/VaultService.ts index 451cb8720..d6f038aaf 100644 --- a/packages/services/src/Domain/Vaults/VaultService.ts +++ b/packages/services/src/Domain/Vaults/VaultService.ts @@ -28,6 +28,7 @@ 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' export class VaultService extends AbstractService @@ -43,11 +44,12 @@ export class VaultService private keys: KeySystemKeyManagerInterface, private alerts: AlertService, private _getVault: GetVault, + private _getVaults: GetVaults, private _changeVaultKeyOptions: ChangeVaultKeyOptions, private _moveItemsToVault: MoveItemsToVault, private _createVault: CreateVault, - private _removeItemFromVaultUseCase: RemoveItemFromVault, - private _deleteVaultUseCase: DeleteVault, + private _removeItemFromVault: RemoveItemFromVault, + private _deleteVaultUse: DeleteVault, private _rotateVaultKey: RotateVaultKey, eventBus: InternalEventBusInterface, ) { @@ -62,9 +64,7 @@ export class VaultService } getVaults(): VaultListingInterface[] { - return this.items.getItems(ContentType.TYPES.VaultListing).sort((a, b) => { - return a.name.localeCompare(b.name) - }) + return this._getVaults.execute().getValue() } getLockedvaults(): VaultListingInterface[] { @@ -166,7 +166,7 @@ export class VaultService throw new Error('Attempting to remove item from locked vault') } - await this._removeItemFromVaultUseCase.execute({ item }) + await this._removeItemFromVault.execute({ item }) return this.items.findSureItem(item.uuid) } @@ -175,7 +175,7 @@ export class VaultService throw new Error('Shared vault must be deleted through SharedVaultService') } - const error = await this._deleteVaultUseCase.execute(vault) + const error = await this._deleteVaultUse.execute(vault) if (isClientDisplayableError(error)) { return false @@ -328,11 +328,12 @@ export class VaultService ;(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._removeItemFromVaultUseCase as unknown) = undefined - ;(this._deleteVaultUseCase as unknown) = undefined + ;(this._removeItemFromVault as unknown) = undefined + ;(this._deleteVaultUse as unknown) = undefined ;(this._rotateVaultKey as unknown) = undefined this.lockMap.clear() diff --git a/packages/services/src/Domain/index.ts b/packages/services/src/Domain/index.ts index 5e4897db4..d39dff9ae 100644 --- a/packages/services/src/Domain/index.ts +++ b/packages/services/src/Domain/index.ts @@ -36,6 +36,7 @@ export * from './Contacts/ContactService' export * from './Contacts/ContactServiceInterface' export * from './Contacts/SelfContactManager' export * from './Contacts/UseCase/CreateOrEditContact' +export * from './Contacts/UseCase/DeleteContact' export * from './Contacts/UseCase/EditContact' export * from './Contacts/UseCase/FindContact' export * from './Contacts/UseCase/GetAllContacts' @@ -133,10 +134,13 @@ export * from './Session/UserKeyPairChangedEventData' export * from './SharedVaults/SharedVaultService' export * from './SharedVaults/SharedVaultServiceEvent' export * from './SharedVaults/SharedVaultServiceInterface' +export * from './SharedVaults/UseCase/ContactBelongsToVault' export * from './SharedVaults/UseCase/ConvertToSharedVault' export * from './SharedVaults/UseCase/CreateSharedVault' export * from './SharedVaults/UseCase/DeleteExternalSharedVault' export * from './SharedVaults/UseCase/DeleteSharedVault' +export * from './SharedVaults/UseCase/GetOwnedSharedVaults' +export * from './SharedVaults/UseCase/GetSharedVaults' export * from './SharedVaults/UseCase/NotifyVaultUsersOfKeyRotation' export * from './SharedVaults/UseCase/SendVaultDataChangedMessage' export * from './SharedVaults/UseCase/SendVaultKeyChangedMessage' @@ -188,6 +192,7 @@ export * from './Vaults/UseCase/ChangeVaultKeyOptions' export * from './Vaults/UseCase/CreateVault' export * from './Vaults/UseCase/DeleteVault' export * from './Vaults/UseCase/GetVault' +export * from './Vaults/UseCase/GetVaults' export * from './Vaults/UseCase/MoveItemsToVault' export * from './Vaults/UseCase/RemoveItemFromVault' export * from './Vaults/UseCase/RotateVaultKey' @@ -197,9 +202,10 @@ export * from './Vaults/VaultServiceInterface' export * from './VaultUser/UseCase/GetVaultContacts' export * from './VaultUser/UseCase/GetVaultContacts' export * from './VaultUser/UseCase/GetVaultUsers' -export * from './VaultUser/UseCase/IsVaultAdmin' +export * from './VaultUser/UseCase/IsVaultOwner' export * from './VaultUser/UseCase/LeaveSharedVault' export * from './VaultUser/UseCase/RemoveSharedVaultMember' +export * from './VaultUser/VaultUserCache' export * from './VaultUser/VaultUserService' export * from './VaultUser/VaultUserServiceEvent' export * from './VaultUser/VaultUserServiceInterface' diff --git a/packages/snjs/lib/Application/Dependencies/Dependencies.ts b/packages/snjs/lib/Application/Dependencies/Dependencies.ts index c847a6a81..0a245dc77 100644 --- a/packages/snjs/lib/Application/Dependencies/Dependencies.ts +++ b/packages/snjs/lib/Application/Dependencies/Dependencies.ts @@ -109,8 +109,14 @@ import { ItemsEncryptionService, DecryptBackupFile, VaultUserService, - IsVaultAdmin, + IsVaultOwner, VaultInviteService, + VaultUserCache, + GetVaults, + GetSharedVaults, + GetOwnedSharedVaults, + ContactBelongsToVault, + DeleteContact, } from '@standardnotes/services' import { ItemManager } from '../../Services/Items/ItemManager' import { PayloadManager } from '../../Services/Payloads/PayloadManager' @@ -207,8 +213,8 @@ export class Dependencies { ) }) - this.factory.set(TYPES.IsVaultAdmin, () => { - return new IsVaultAdmin() + this.factory.set(TYPES.IsVaultOwner, () => { + return new IsVaultOwner() }) this.factory.set(TYPES.DecryptBackupFile, () => { @@ -223,6 +229,15 @@ export class Dependencies { return new FindContact(this.get(TYPES.ItemManager)) }) + this.factory.set(TYPES.DeleteContact, () => { + return new DeleteContact( + this.get(TYPES.MutatorService), + this.get(TYPES.SyncService), + this.get(TYPES.GetOwnedSharedVaults), + this.get(TYPES.ContactBelongsToVault), + ) + }) + this.factory.set(TYPES.EditContact, () => { return new EditContact(this.get(TYPES.MutatorService), this.get(TYPES.SyncService)) }) @@ -248,6 +263,22 @@ export class Dependencies { return new GetVault(this.get(TYPES.ItemManager)) }) + this.factory.set(TYPES.GetVaults, () => { + return new GetVaults(this.get(TYPES.ItemManager)) + }) + + this.factory.set(TYPES.GetSharedVaults, () => { + return new GetSharedVaults(this.get(TYPES.GetVaults)) + }) + + this.factory.set(TYPES.GetOwnedSharedVaults, () => { + return new GetOwnedSharedVaults(this.get(TYPES.GetSharedVaults), this.get(TYPES.IsVaultOwner)) + }) + + this.factory.set(TYPES.ContactBelongsToVault, () => { + return new ContactBelongsToVault(this.get(TYPES.GetVaultUsers)) + }) + this.factory.set(TYPES.ChangeVaultKeyOptions, () => { return new ChangeVaultKeyOptions( this.get(TYPES.MutatorService), @@ -453,7 +484,7 @@ export class Dependencies { }) this.factory.set(TYPES.GetVaultUsers, () => { - return new GetVaultUsers(this.get(TYPES.SharedVaultUsersServer)) + return new GetVaultUsers(this.get(TYPES.SharedVaultUsersServer), this.get(TYPES.VaultUserCache)) }) this.factory.set(TYPES.DecryptOwnMessage, () => { @@ -621,13 +652,17 @@ export class Dependencies { this.get(TYPES.VaultService), this.get(TYPES.GetVaultUsers), this.get(TYPES.RemoveVaultMember), - this.get(TYPES.IsVaultAdmin), + this.get(TYPES.IsVaultOwner), this.get(TYPES.GetVault), this.get(TYPES.LeaveVault), this.get(TYPES.InternalEventBus), ) }) + this.factory.set(TYPES.VaultUserCache, () => { + return new VaultUserCache() + }) + this.factory.set(TYPES.VaultInviteService, () => { return new VaultInviteService( this.get(TYPES.ItemManager), @@ -650,9 +685,10 @@ export class Dependencies { this.factory.set(TYPES.AsymmetricMessageService, () => { return new AsymmetricMessageService( - this.get(TYPES.AsymmetricMessageServer), this.get(TYPES.EncryptionService), this.get(TYPES.MutatorService), + this.get(TYPES.SessionManager), + this.get(TYPES.AsymmetricMessageServer), this.get(TYPES.CreateOrEditContact), this.get(TYPES.FindContact), this.get(TYPES.GetAllContacts), @@ -673,8 +709,8 @@ export class Dependencies { this.get(TYPES.ItemManager), this.get(TYPES.EncryptionService), this.get(TYPES.SessionManager), - this.get(TYPES.VaultService), this.get(TYPES.GetVault), + this.get(TYPES.GetOwnedSharedVaults), this.get(TYPES.CreateSharedVault), this.get(TYPES.HandleKeyPairChange), this.get(TYPES.NotifyVaultUsersOfKeyRotation), @@ -684,7 +720,7 @@ export class Dependencies { this.get(TYPES.ShareContactWithVault), this.get(TYPES.ConvertToSharedVault), this.get(TYPES.DeleteSharedVault), - this.get(TYPES.IsVaultAdmin), + this.get(TYPES.IsVaultOwner), this.get(TYPES.InternalEventBus), ) }) @@ -698,6 +734,7 @@ export class Dependencies { this.get(TYPES.KeySystemKeyManager), this.get(TYPES.AlertService), this.get(TYPES.GetVault), + this.get(TYPES.GetVaults), this.get(TYPES.ChangeVaultKeyOptions), this.get(TYPES.MoveItemsToVault), this.get(TYPES.CreateVault), @@ -727,6 +764,7 @@ export class Dependencies { this.get(TYPES.UserService), this.get(TYPES.SelfContactManager), this.get(TYPES.EncryptionService), + this.get(TYPES.DeleteContact), this.get(TYPES.FindContact), this.get(TYPES.GetAllContacts), this.get(TYPES.CreateOrEditContact), diff --git a/packages/snjs/lib/Application/Dependencies/Types.ts b/packages/snjs/lib/Application/Dependencies/Types.ts index 55bca4e5b..c1b55ec50 100644 --- a/packages/snjs/lib/Application/Dependencies/Types.ts +++ b/packages/snjs/lib/Application/Dependencies/Types.ts @@ -61,6 +61,7 @@ export const TYPES = { ItemsEncryptionService: Symbol.for('ItemsEncryptionService'), VaultUserService: Symbol.for('VaultUserService'), VaultInviteService: Symbol.for('VaultInviteService'), + VaultUserCache: Symbol.for('VaultUserCache'), // Servers RevisionServer: Symbol.for('RevisionServer'), @@ -94,9 +95,14 @@ export const TYPES = { EditContact: Symbol.for('EditContact'), ValidateItemSigner: Symbol.for('ValidateItemSigner'), GetVault: Symbol.for('GetVault'), + GetVaults: Symbol.for('GetVaults'), + GetSharedVaults: Symbol.for('GetSharedVaults'), + GetOwnedSharedVaults: Symbol.for('GetOwnedSharedVaults'), ChangeVaultKeyOptions: Symbol.for('ChangeVaultKeyOptions'), MoveItemsToVault: Symbol.for('MoveItemsToVault'), CreateVault: Symbol.for('CreateVault'), + DeleteContact: Symbol.for('DeleteContact'), + ContactBelongsToVault: Symbol.for('ContactBelongsToVault'), RemoveItemFromVault: Symbol.for('RemoveItemFromVault'), DeleteVault: Symbol.for('DeleteVault'), RotateVaultKey: Symbol.for('RotateVaultKey'), @@ -142,7 +148,7 @@ export const TYPES = { EncryptTypeAPayload: Symbol.for('EncryptTypeAPayload'), EncryptTypeAPayloadWithKeyLookup: Symbol.for('EncryptTypeAPayloadWithKeyLookup'), DecryptBackupFile: Symbol.for('DecryptBackupFile'), - IsVaultAdmin: Symbol.for('IsVaultAdmin'), + IsVaultOwner: Symbol.for('IsVaultOwner'), // Mappers SessionStorageMapper: Symbol.for('SessionStorageMapper'), diff --git a/packages/snjs/mocha/TestRegistry/VaultTests.js b/packages/snjs/mocha/TestRegistry/VaultTests.js index af3496be2..ecaa6743e 100644 --- a/packages/snjs/mocha/TestRegistry/VaultTests.js +++ b/packages/snjs/mocha/TestRegistry/VaultTests.js @@ -1,18 +1,21 @@ - -export const VaultTests = [ - 'vaults/vaults.test.js', - 'vaults/pkc.test.js', - 'vaults/contacts.test.js', - 'vaults/crypto.test.js', - 'vaults/asymmetric-messages.test.js', - 'vaults/keypair-change.test.js', - 'vaults/signatures.test.js', - 'vaults/shared_vaults.test.js', - 'vaults/invites.test.js', - 'vaults/items.test.js', - 'vaults/conflicts.test.js', - 'vaults/deletion.test.js', - 'vaults/permissions.test.js', - 'vaults/key_rotation.test.js', - 'vaults/files.test.js', -]; +export const VaultTests = { + enabled: false, + exclusive: false, + files: [ + 'vaults/vaults.test.js', + 'vaults/pkc.test.js', + 'vaults/contacts.test.js', + 'vaults/crypto.test.js', + 'vaults/asymmetric-messages.test.js', + 'vaults/keypair-change.test.js', + 'vaults/signatures.test.js', + 'vaults/shared_vaults.test.js', + 'vaults/invites.test.js', + 'vaults/items.test.js', + 'vaults/conflicts.test.js', + 'vaults/deletion.test.js', + 'vaults/permissions.test.js', + 'vaults/key_rotation.test.js', + 'vaults/files.test.js', + ], +} diff --git a/packages/snjs/mocha/lib/AppContext.js b/packages/snjs/mocha/lib/AppContext.js index d076f7e87..625d46533 100644 --- a/packages/snjs/mocha/lib/AppContext.js +++ b/packages/snjs/mocha/lib/AppContext.js @@ -578,9 +578,11 @@ export class AppContext { } async changeNoteTitle(note, title) { - return this.application.mutator.changeNote(note, (mutator) => { + await this.application.mutator.changeNote(note, (mutator) => { mutator.title = title }) + + return this.findItem(note.uuid) } async changeNoteTitleAndSync(note, title) { diff --git a/packages/snjs/mocha/test.html b/packages/snjs/mocha/test.html index aa8afdc5b..cd06a91be 100644 --- a/packages/snjs/mocha/test.html +++ b/packages/snjs/mocha/test.html @@ -38,10 +38,6 @@