diff --git a/packages/api-gateway/bin/server.ts b/packages/api-gateway/bin/server.ts index ce8d2832c..a6ca07a06 100644 --- a/packages/api-gateway/bin/server.ts +++ b/packages/api-gateway/bin/server.ts @@ -12,7 +12,7 @@ import '../src/Controller/v1/InvoicesController' import '../src/Controller/v1/RevisionsController' import '../src/Controller/v1/ItemsController' import '../src/Controller/v1/ContactsController' -import '../src/Controller/v1/VaultsController' +import '../src/Controller/v1/GroupsController' import '../src/Controller/v1/PaymentsController' import '../src/Controller/v1/WebSocketsController' import '../src/Controller/v1/TokensController' diff --git a/packages/api-gateway/src/Controller/index.ts b/packages/api-gateway/src/Controller/index.ts index 63a828e31..924fdaf36 100644 --- a/packages/api-gateway/src/Controller/index.ts +++ b/packages/api-gateway/src/Controller/index.ts @@ -10,7 +10,7 @@ export * from './v1/FilesController' export * from './v1/InvoicesController' export * from './v1/ItemsController' export * from './v1/ContactsController' -export * from './v1/VaultsController' +export * from './v1/GroupsController' export * from './v1/OfflineController' export * from './v1/PaymentsController' export * from './v1/RevisionsController' diff --git a/packages/api-gateway/src/Controller/v1/VaultsController.ts b/packages/api-gateway/src/Controller/v1/GroupsController.ts similarity index 87% rename from packages/api-gateway/src/Controller/v1/VaultsController.ts rename to packages/api-gateway/src/Controller/v1/GroupsController.ts index bc4911679..ba04b22fd 100644 --- a/packages/api-gateway/src/Controller/v1/VaultsController.ts +++ b/packages/api-gateway/src/Controller/v1/GroupsController.ts @@ -4,8 +4,8 @@ import { BaseHttpController, controller, all } from 'inversify-express-utils' import { TYPES } from '../../Bootstrap/Types' import { HttpServiceInterface } from '../../Service/Http/HttpServiceInterface' -@controller('/v1/vaults') -export class VaultsController extends BaseHttpController { +@controller('/v1/groups') +export class GroupsController extends BaseHttpController { constructor(@inject(TYPES.HTTPService) private httpService: HttpServiceInterface) { super() } diff --git a/packages/auth/src/Domain/Setting/SettingService.ts b/packages/auth/src/Domain/Setting/SettingService.ts index 7460c8ca4..9c4d284d4 100644 --- a/packages/auth/src/Domain/Setting/SettingService.ts +++ b/packages/auth/src/Domain/Setting/SettingService.ts @@ -27,8 +27,8 @@ export class SettingService implements SettingServiceInterface { async applyDefaultSettingsUponRegistration(user: User): Promise { let defaultSettingsWithValues = this.settingsAssociationService.getDefaultSettingsAndValuesForNewUser() - if (user.isPotentiallyAVaultAccount()) { - defaultSettingsWithValues = this.settingsAssociationService.getDefaultSettingsAndValuesForNewVaultAccount() + if (user.isPotentiallyAPrivateUsernameAccount()) { + defaultSettingsWithValues = this.settingsAssociationService.getDefaultSettingsAndValuesForNewPrivateUsernameAccount() } for (const settingName of defaultSettingsWithValues.keys()) { diff --git a/packages/auth/src/Domain/Setting/SettingsAssociationService.ts b/packages/auth/src/Domain/Setting/SettingsAssociationService.ts index 8ecdf651a..6ce6ede33 100644 --- a/packages/auth/src/Domain/Setting/SettingsAssociationService.ts +++ b/packages/auth/src/Domain/Setting/SettingsAssociationService.ts @@ -66,7 +66,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt ], ]) - private readonly vaultAccountDefaultSettingsOverwrites = new Map([ + private readonly privateUsernameAccountDefaultSettingsOverwrites = new Map([ [ SettingName.NAMES.LogSessionUserAgent, { @@ -114,16 +114,18 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt return this.defaultSettings } - getDefaultSettingsAndValuesForNewVaultAccount(): Map { - const defaultVaultSettings = new Map(this.defaultSettings) + getDefaultSettingsAndValuesForNewPrivateUsernameAccount(): Map { + const defaultPrivateUsernameSettings = new Map(this.defaultSettings) - for (const vaultAccountDefaultSettingOverwriteKey of this.vaultAccountDefaultSettingsOverwrites.keys()) { - defaultVaultSettings.set( - vaultAccountDefaultSettingOverwriteKey, - this.vaultAccountDefaultSettingsOverwrites.get(vaultAccountDefaultSettingOverwriteKey) as SettingDescription, + for (const privateUsernameAccountDefaultSettingOverwriteKey of this.privateUsernameAccountDefaultSettingsOverwrites.keys()) { + defaultPrivateUsernameSettings.set( + privateUsernameAccountDefaultSettingOverwriteKey, + this.privateUsernameAccountDefaultSettingsOverwrites.get( + privateUsernameAccountDefaultSettingOverwriteKey, + ) as SettingDescription, ) } - return defaultVaultSettings + return defaultPrivateUsernameSettings } } diff --git a/packages/auth/src/Domain/Setting/SettingsAssociationServiceInterface.ts b/packages/auth/src/Domain/Setting/SettingsAssociationServiceInterface.ts index 80a32b896..73b5dbcb2 100644 --- a/packages/auth/src/Domain/Setting/SettingsAssociationServiceInterface.ts +++ b/packages/auth/src/Domain/Setting/SettingsAssociationServiceInterface.ts @@ -6,7 +6,7 @@ import { SettingDescription } from './SettingDescription' export interface SettingsAssociationServiceInterface { getDefaultSettingsAndValuesForNewUser(): Map - getDefaultSettingsAndValuesForNewVaultAccount(): Map + getDefaultSettingsAndValuesForNewPrivateUsernameAccount(): Map getPermissionAssociatedWithSetting(settingName: SettingName): PermissionName | undefined getEncryptionVersionForSetting(settingName: SettingName): EncryptionVersion getSensitivityForSetting(settingName: SettingName): boolean diff --git a/packages/auth/src/Domain/UseCase/InviteToSharedSubscription/InviteToSharedSubscription.ts b/packages/auth/src/Domain/UseCase/InviteToSharedSubscription/InviteToSharedSubscription.ts index 1a9fb88b2..87121bebc 100644 --- a/packages/auth/src/Domain/UseCase/InviteToSharedSubscription/InviteToSharedSubscription.ts +++ b/packages/auth/src/Domain/UseCase/InviteToSharedSubscription/InviteToSharedSubscription.ts @@ -68,7 +68,7 @@ export class InviteToSharedSubscription implements UseCaseInterface { sharedSubscriptionInvition.inviterIdentifier = dto.inviterEmail sharedSubscriptionInvition.inviterIdentifierType = InviterIdentifierType.Email sharedSubscriptionInvition.inviteeIdentifier = dto.inviteeIdentifier - sharedSubscriptionInvition.inviteeIdentifierType = this.isInviteeIdentifierPotentiallyAVaultAccount( + sharedSubscriptionInvition.inviteeIdentifierType = this.isInviteeIdentifierPotentiallyAPrivateUsernameAccount( dto.inviteeIdentifier, ) ? InviteeIdentifierType.Hash @@ -106,7 +106,7 @@ export class InviteToSharedSubscription implements UseCaseInterface { } } - private isInviteeIdentifierPotentiallyAVaultAccount(identifier: string): boolean { + private isInviteeIdentifierPotentiallyAPrivateUsernameAccount(identifier: string): boolean { return identifier.length === 64 && !identifier.includes('@') } } diff --git a/packages/auth/src/Domain/User/User.ts b/packages/auth/src/Domain/User/User.ts index 038fc7a25..94d3b97cf 100644 --- a/packages/auth/src/Domain/User/User.ts +++ b/packages/auth/src/Domain/User/User.ts @@ -218,7 +218,7 @@ export class User { return parseInt(this.version) >= parseInt(ProtocolVersion.V004) } - isPotentiallyAVaultAccount(): boolean { + isPotentiallyAPrivateUsernameAccount(): boolean { return this.email.length === 64 && !this.email.includes('@') } } diff --git a/packages/domain-events/src/Domain/Event/GroupFileRemovedEvent.ts b/packages/domain-events/src/Domain/Event/GroupFileRemovedEvent.ts new file mode 100644 index 000000000..dfe3602c3 --- /dev/null +++ b/packages/domain-events/src/Domain/Event/GroupFileRemovedEvent.ts @@ -0,0 +1,7 @@ +import { DomainEventInterface } from './DomainEventInterface' +import { GroupFileRemovedEventPayload } from './GroupFileRemovedEventPayload' + +export interface GroupFileRemovedEvent extends DomainEventInterface { + type: 'GROUP_FILE_REMOVED' + payload: GroupFileRemovedEventPayload +} diff --git a/packages/domain-events/src/Domain/Event/GroupFileRemovedEventPayload.ts b/packages/domain-events/src/Domain/Event/GroupFileRemovedEventPayload.ts new file mode 100644 index 000000000..720ff2040 --- /dev/null +++ b/packages/domain-events/src/Domain/Event/GroupFileRemovedEventPayload.ts @@ -0,0 +1,6 @@ +export interface GroupFileRemovedEventPayload { + groupUuid: string + fileByteSize: number + filePath: string + fileName: string +} diff --git a/packages/domain-events/src/Domain/Event/GroupFileUploadedEvent.ts b/packages/domain-events/src/Domain/Event/GroupFileUploadedEvent.ts new file mode 100644 index 000000000..a2cc08908 --- /dev/null +++ b/packages/domain-events/src/Domain/Event/GroupFileUploadedEvent.ts @@ -0,0 +1,7 @@ +import { DomainEventInterface } from './DomainEventInterface' +import { GroupFileUploadedEventPayload } from './GroupFileUploadedEventPayload' + +export interface GroupFileUploadedEvent extends DomainEventInterface { + type: 'GROUP_FILE_UPLOADED' + payload: GroupFileUploadedEventPayload +} diff --git a/packages/domain-events/src/Domain/Event/GroupFileUploadedEventPayload.ts b/packages/domain-events/src/Domain/Event/GroupFileUploadedEventPayload.ts new file mode 100644 index 000000000..3c2b66fd7 --- /dev/null +++ b/packages/domain-events/src/Domain/Event/GroupFileUploadedEventPayload.ts @@ -0,0 +1,6 @@ +export interface GroupFileUploadedEventPayload { + groupUuid: string + fileByteSize: number + filePath: string + fileName: string +} diff --git a/packages/domain-events/src/Domain/Event/VaultFileRemovedEvent.ts b/packages/domain-events/src/Domain/Event/VaultFileRemovedEvent.ts deleted file mode 100644 index e35486a6b..000000000 --- a/packages/domain-events/src/Domain/Event/VaultFileRemovedEvent.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { DomainEventInterface } from './DomainEventInterface' -import { VaultFileRemovedEventPayload } from './VaultFileRemovedEventPayload' - -export interface VaultFileRemovedEvent extends DomainEventInterface { - type: 'VAULT_FILE_REMOVED' - payload: VaultFileRemovedEventPayload -} diff --git a/packages/domain-events/src/Domain/Event/VaultFileRemovedEventPayload.ts b/packages/domain-events/src/Domain/Event/VaultFileRemovedEventPayload.ts deleted file mode 100644 index 2073f6dd4..000000000 --- a/packages/domain-events/src/Domain/Event/VaultFileRemovedEventPayload.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface VaultFileRemovedEventPayload { - vaultUuid: string - fileByteSize: number - filePath: string - fileName: string -} diff --git a/packages/domain-events/src/Domain/Event/VaultFileUploadedEvent.ts b/packages/domain-events/src/Domain/Event/VaultFileUploadedEvent.ts deleted file mode 100644 index 3a41eb88b..000000000 --- a/packages/domain-events/src/Domain/Event/VaultFileUploadedEvent.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { DomainEventInterface } from './DomainEventInterface' -import { VaultFileUploadedEventPayload } from './VaultFileUploadedEventPayload' - -export interface VaultFileUploadedEvent extends DomainEventInterface { - type: 'VAULT_FILE_UPLOADED' - payload: VaultFileUploadedEventPayload -} diff --git a/packages/domain-events/src/Domain/Event/VaultFileUploadedEventPayload.ts b/packages/domain-events/src/Domain/Event/VaultFileUploadedEventPayload.ts deleted file mode 100644 index eb1873f13..000000000 --- a/packages/domain-events/src/Domain/Event/VaultFileUploadedEventPayload.ts +++ /dev/null @@ -1,6 +0,0 @@ -export interface VaultFileUploadedEventPayload { - vaultUuid: string - fileByteSize: number - filePath: string - fileName: string -} diff --git a/packages/domain-events/src/Domain/index.ts b/packages/domain-events/src/Domain/index.ts index 3fd7c47fa..5436275cc 100644 --- a/packages/domain-events/src/Domain/index.ts +++ b/packages/domain-events/src/Domain/index.ts @@ -32,10 +32,10 @@ export * from './Event/UserFileRemovedEventPayload' export * from './Event/UserFileUploadedEvent' export * from './Event/UserFileUploadedEventPayload' -export * from './Event/VaultFileRemovedEvent' -export * from './Event/VaultFileRemovedEventPayload' -export * from './Event/VaultFileUploadedEvent' -export * from './Event/VaultFileUploadedEventPayload' +export * from './Event/GroupFileRemovedEvent' +export * from './Event/GroupFileRemovedEventPayload' +export * from './Event/GroupFileUploadedEvent' +export * from './Event/GroupFileUploadedEventPayload' export * from './Event/ItemDumpedEvent' export * from './Event/ItemDumpedEventPayload' diff --git a/packages/files/bin/server.ts b/packages/files/bin/server.ts index 2130bec8e..b53388346 100644 --- a/packages/files/bin/server.ts +++ b/packages/files/bin/server.ts @@ -6,7 +6,7 @@ import * as busboy from 'connect-busboy' import '../src/Controller/HealthCheckController' import '../src/Controller/FilesController' -import '../src/Controller/VaultFilesController' +import '../src/Controller/GroupFilesController' import helmet from 'helmet' import * as cors from 'cors' diff --git a/packages/files/src/Bootstrap/Container.ts b/packages/files/src/Bootstrap/Container.ts index b65ab371d..b872056b5 100644 --- a/packages/files/src/Bootstrap/Container.ts +++ b/packages/files/src/Bootstrap/Container.ts @@ -44,7 +44,7 @@ import { MarkFilesToBeRemoved } from '../Domain/UseCase/MarkFilesToBeRemoved/Mar import { AccountDeletionRequestedEventHandler } from '../Domain/Handler/AccountDeletionRequestedEventHandler' import { SharedSubscriptionInvitationCanceledEventHandler } from '../Domain/Handler/SharedSubscriptionInvitationCanceledEventHandler' import { InMemoryUploadRepository } from '../Infra/InMemory/InMemoryUploadRepository' -import { VaultValetTokenAuthMiddleware } from '../Controller/VaultValetTokenAuthMiddleware' +import { GroupValetTokenAuthMiddleware } from '../Controller/GroupValetTokenAuthMiddleware' import { FileMoverInterface } from '../Domain/Services/FileMoverInterface' import { S3FileMover } from '../Infra/S3/S3FileMover' import { FSFileMover } from '../Infra/FS/FSFileMover' @@ -157,7 +157,7 @@ export class ContainerConfigLoader { // middleware container.bind(TYPES.ValetTokenAuthMiddleware).to(ValetTokenAuthMiddleware) - container.bind(TYPES.VaultValetTokenAuthMiddleware).to(VaultValetTokenAuthMiddleware) + container.bind(TYPES.GroupValetTokenAuthMiddleware).to(GroupValetTokenAuthMiddleware) // services container diff --git a/packages/files/src/Bootstrap/Types.ts b/packages/files/src/Bootstrap/Types.ts index b07e0bb57..79aab1ce8 100644 --- a/packages/files/src/Bootstrap/Types.ts +++ b/packages/files/src/Bootstrap/Types.ts @@ -31,7 +31,7 @@ const TYPES = { // middleware ValetTokenAuthMiddleware: Symbol.for('ValetTokenAuthMiddleware'), - VaultValetTokenAuthMiddleware: Symbol.for('VaultValetTokenAuthMiddleware'), + GroupValetTokenAuthMiddleware: Symbol.for('GroupValetTokenAuthMiddleware'), // env vars S3_ENDPOINT: Symbol.for('S3_ENDPOINT'), diff --git a/packages/files/src/Controller/VaultFilesController.ts b/packages/files/src/Controller/GroupFilesController.ts similarity index 89% rename from packages/files/src/Controller/VaultFilesController.ts rename to packages/files/src/Controller/GroupFilesController.ts index 0c3c9f7d9..7273050c9 100644 --- a/packages/files/src/Controller/VaultFilesController.ts +++ b/packages/files/src/Controller/GroupFilesController.ts @@ -9,11 +9,11 @@ import { CreateUploadSession } from '../Domain/UseCase/CreateUploadSession/Creat import { FinishUploadSession } from '../Domain/UseCase/FinishUploadSession/FinishUploadSession' import { GetFileMetadata } from '../Domain/UseCase/GetFileMetadata/GetFileMetadata' import { RemoveFile } from '../Domain/UseCase/RemoveFile/RemoveFile' -import { ValetTokenOperation, VaultValetTokenData } from '@standardnotes/security' +import { ValetTokenOperation, GroupValetTokenData } from '@standardnotes/security' import { MoveFile } from '../Domain/UseCase/MoveFile/MoveFile' -@controller('/v1/vault/files', TYPES.VaultValetTokenAuthMiddleware) -export class VaultFilesController extends BaseHttpController { +@controller('/v1/group/files', TYPES.GroupValetTokenAuthMiddleware) +export class GroupFilesController extends BaseHttpController { constructor( @inject(TYPES.UploadFileChunk) private uploadFileChunk: UploadFileChunk, @inject(TYPES.CreateUploadSession) private createUploadSession: CreateUploadSession, @@ -32,7 +32,7 @@ export class VaultFilesController extends BaseHttpController { _request: Request, response: Response, ): Promise { - const locals = response.locals as VaultValetTokenData + const locals = response.locals as GroupValetTokenData if (locals.permittedOperation !== ValetTokenOperation.Move) { return this.badRequest('Not permitted for this operation') } @@ -61,13 +61,13 @@ export class VaultFilesController extends BaseHttpController { _request: Request, response: Response, ): Promise { - const locals = response.locals as VaultValetTokenData + const locals = response.locals as GroupValetTokenData if (locals.permittedOperation !== ValetTokenOperation.Write) { return this.badRequest('Not permitted for this operation') } const result = await this.createUploadSession.execute({ - ownerUuid: locals.vaultUuid, + ownerUuid: locals.groupUuid, resourceRemoteIdentifier: locals.remoteIdentifier, }) @@ -83,7 +83,7 @@ export class VaultFilesController extends BaseHttpController { request: Request, response: Response, ): Promise { - const locals = response.locals as VaultValetTokenData + const locals = response.locals as GroupValetTokenData if (locals.permittedOperation !== ValetTokenOperation.Write) { return this.badRequest('Not permitted for this operation') } @@ -94,7 +94,7 @@ export class VaultFilesController extends BaseHttpController { } const result = await this.uploadFileChunk.execute({ - ownerUuid: locals.vaultUuid, + ownerUuid: locals.groupUuid, resourceRemoteIdentifier: locals.remoteIdentifier, resourceUnencryptedFileSize: locals.unencryptedFileSize as number, chunkId, @@ -113,14 +113,14 @@ export class VaultFilesController extends BaseHttpController { _request: Request, response: Response, ): Promise { - const locals = response.locals as VaultValetTokenData + const locals = response.locals as GroupValetTokenData if (locals.permittedOperation !== ValetTokenOperation.Write) { return this.badRequest('Not permitted for this operation') } const result = await this.finishUploadSession.execute({ - ownerUuid: locals.vaultUuid, - ownerType: 'vault', + ownerUuid: locals.groupUuid, + ownerType: 'group', resourceRemoteIdentifier: locals.remoteIdentifier, uploadBytesLimit: locals.uploadBytesLimit, uploadBytesUsed: locals.uploadBytesUsed, @@ -138,13 +138,13 @@ export class VaultFilesController extends BaseHttpController { _request: Request, response: Response, ): Promise { - const locals = response.locals as VaultValetTokenData + const locals = response.locals as GroupValetTokenData if (locals.permittedOperation !== ValetTokenOperation.Delete) { return this.badRequest('Not permitted for this operation') } const result = await this.removeFile.execute({ - vaultUuid: locals.vaultUuid, + groupUuid: locals.groupUuid, resourceRemoteIdentifier: locals.remoteIdentifier, }) @@ -160,7 +160,7 @@ export class VaultFilesController extends BaseHttpController { request: Request, response: Response, ): Promise Writable)> { - const locals = response.locals as VaultValetTokenData + const locals = response.locals as GroupValetTokenData if (locals.permittedOperation !== ValetTokenOperation.Read) { return this.badRequest('Not permitted for this operation') } @@ -176,7 +176,7 @@ export class VaultFilesController extends BaseHttpController { } const fileMetadata = await this.getFileMetadata.execute({ - ownerUuid: locals.vaultUuid, + ownerUuid: locals.groupUuid, resourceRemoteIdentifier: locals.remoteIdentifier, }) @@ -197,7 +197,7 @@ export class VaultFilesController extends BaseHttpController { response.writeHead(206, headers) const result = await this.streamDownloadFile.execute({ - ownerUuid: locals.vaultUuid, + ownerUuid: locals.groupUuid, resourceRemoteIdentifier: locals.remoteIdentifier, startRange, endRange, diff --git a/packages/files/src/Controller/VaultValetTokenAuthMiddleware.ts b/packages/files/src/Controller/GroupValetTokenAuthMiddleware.ts similarity index 84% rename from packages/files/src/Controller/VaultValetTokenAuthMiddleware.ts rename to packages/files/src/Controller/GroupValetTokenAuthMiddleware.ts index 73b9c78cb..8d4d62ade 100644 --- a/packages/files/src/Controller/VaultValetTokenAuthMiddleware.ts +++ b/packages/files/src/Controller/GroupValetTokenAuthMiddleware.ts @@ -1,4 +1,4 @@ -import { VaultValetTokenData, TokenDecoderInterface } from '@standardnotes/security' +import { GroupValetTokenData, TokenDecoderInterface } from '@standardnotes/security' import { Uuid } from '@standardnotes/domain-core' import { NextFunction, Request, Response } from 'express' import { inject, injectable } from 'inversify' @@ -7,9 +7,9 @@ import { Logger } from 'winston' import TYPES from '../Bootstrap/Types' @injectable() -export class VaultValetTokenAuthMiddleware extends BaseMiddleware { +export class GroupValetTokenAuthMiddleware extends BaseMiddleware { constructor( - @inject(TYPES.ValetTokenDecoder) private tokenDecoder: TokenDecoderInterface, + @inject(TYPES.ValetTokenDecoder) private tokenDecoder: TokenDecoderInterface, @inject(TYPES.Logger) private logger: Logger, ) { super() @@ -19,7 +19,7 @@ export class VaultValetTokenAuthMiddleware extends BaseMiddleware { try { const valetToken = request.headers['x-valet-token'] || request.body.valetToken || request.query.valetToken if (!valetToken) { - this.logger.debug('VaultValetTokenAuthMiddleware missing valet token.') + this.logger.debug('GroupValetTokenAuthMiddleware missing valet token.') response.status(401).send({ error: { @@ -34,7 +34,7 @@ export class VaultValetTokenAuthMiddleware extends BaseMiddleware { const valetTokenData = this.tokenDecoder.decodeToken(valetToken) if (valetTokenData === undefined) { - this.logger.debug('VaultValetTokenAuthMiddleware authentication failure.') + this.logger.debug('GroupValetTokenAuthMiddleware authentication failure.') response.status(401).send({ error: { @@ -60,8 +60,8 @@ export class VaultValetTokenAuthMiddleware extends BaseMiddleware { return } - const whitelistedData: VaultValetTokenData = { - vaultUuid: valetTokenData.vaultUuid, + const whitelistedData: GroupValetTokenData = { + groupUuid: valetTokenData.groupUuid, remoteIdentifier: valetTokenData.remoteIdentifier, permittedOperation: valetTokenData.permittedOperation, uploadBytesUsed: valetTokenData.uploadBytesUsed, diff --git a/packages/files/src/Domain/Event/DomainEventFactory.ts b/packages/files/src/Domain/Event/DomainEventFactory.ts index b94c61428..67160a705 100644 --- a/packages/files/src/Domain/Event/DomainEventFactory.ts +++ b/packages/files/src/Domain/Event/DomainEventFactory.ts @@ -2,8 +2,8 @@ import { UserFileRemovedEvent, UserFileUploadedEvent, DomainEventService, - VaultFileUploadedEvent, - VaultFileRemovedEvent, + GroupFileUploadedEvent, + GroupFileRemovedEvent, } from '@standardnotes/domain-events' import { TimerInterface } from '@standardnotes/time' import { inject, injectable } from 'inversify' @@ -56,18 +56,18 @@ export class DomainEventFactory implements DomainEventFactoryInterface { } } - createVaultFileUploadedEvent(payload: { - vaultUuid: string + createGroupFileUploadedEvent(payload: { + groupUuid: string filePath: string fileName: string fileByteSize: number - }): VaultFileUploadedEvent { + }): GroupFileUploadedEvent { return { - type: 'VAULT_FILE_UPLOADED', + type: 'GROUP_FILE_UPLOADED', createdAt: this.timer.getUTCDate(), meta: { correlation: { - userIdentifier: payload.vaultUuid, + userIdentifier: payload.groupUuid, userIdentifierType: 'uuid', }, origin: DomainEventService.Files, @@ -76,18 +76,18 @@ export class DomainEventFactory implements DomainEventFactoryInterface { } } - createVaultFileRemovedEvent(payload: { - vaultUuid: string + createGroupFileRemovedEvent(payload: { + groupUuid: string filePath: string fileName: string fileByteSize: number - }): VaultFileRemovedEvent { + }): GroupFileRemovedEvent { return { - type: 'VAULT_FILE_REMOVED', + type: 'GROUP_FILE_REMOVED', createdAt: this.timer.getUTCDate(), meta: { correlation: { - userIdentifier: payload.vaultUuid, + userIdentifier: payload.groupUuid, userIdentifierType: 'uuid', }, origin: DomainEventService.Files, diff --git a/packages/files/src/Domain/Event/DomainEventFactoryInterface.ts b/packages/files/src/Domain/Event/DomainEventFactoryInterface.ts index f3b59aeab..e0944683d 100644 --- a/packages/files/src/Domain/Event/DomainEventFactoryInterface.ts +++ b/packages/files/src/Domain/Event/DomainEventFactoryInterface.ts @@ -1,8 +1,8 @@ import { UserFileUploadedEvent, UserFileRemovedEvent, - VaultFileUploadedEvent, - VaultFileRemovedEvent, + GroupFileUploadedEvent, + GroupFileRemovedEvent, } from '@standardnotes/domain-events' export interface DomainEventFactoryInterface { @@ -20,16 +20,16 @@ export interface DomainEventFactoryInterface { regularSubscriptionUuid: string }): UserFileRemovedEvent - createVaultFileUploadedEvent(payload: { - vaultUuid: string + createGroupFileUploadedEvent(payload: { + groupUuid: string filePath: string fileName: string fileByteSize: number - }): VaultFileUploadedEvent - createVaultFileRemovedEvent(payload: { - vaultUuid: string + }): GroupFileUploadedEvent + createGroupFileRemovedEvent(payload: { + groupUuid: string filePath: string fileName: string fileByteSize: number - }): VaultFileRemovedEvent + }): GroupFileRemovedEvent } diff --git a/packages/files/src/Domain/UseCase/FinishUploadSession/FinishUploadSession.ts b/packages/files/src/Domain/UseCase/FinishUploadSession/FinishUploadSession.ts index 6b6bfe636..64ad1cf31 100644 --- a/packages/files/src/Domain/UseCase/FinishUploadSession/FinishUploadSession.ts +++ b/packages/files/src/Domain/UseCase/FinishUploadSession/FinishUploadSession.ts @@ -64,8 +64,8 @@ export class FinishUploadSession implements UseCaseInterface { ) } else { await this.domainEventPublisher.publish( - this.domainEventFactory.createVaultFileUploadedEvent({ - vaultUuid: dto.ownerUuid, + this.domainEventFactory.createGroupFileUploadedEvent({ + groupUuid: dto.ownerUuid, filePath: `${dto.ownerUuid}/${dto.resourceRemoteIdentifier}`, fileName: dto.resourceRemoteIdentifier, fileByteSize: totalFileSize, diff --git a/packages/files/src/Domain/UseCase/FinishUploadSession/FinishUploadSessionDTO.ts b/packages/files/src/Domain/UseCase/FinishUploadSession/FinishUploadSessionDTO.ts index 7a7d338ee..04fa5ba8f 100644 --- a/packages/files/src/Domain/UseCase/FinishUploadSession/FinishUploadSessionDTO.ts +++ b/packages/files/src/Domain/UseCase/FinishUploadSession/FinishUploadSessionDTO.ts @@ -1,6 +1,6 @@ export type FinishUploadSessionDTO = { ownerUuid: string - ownerType: 'user' | 'vault' + ownerType: 'user' | 'group' resourceRemoteIdentifier: string uploadBytesUsed: number uploadBytesLimit: number diff --git a/packages/files/src/Domain/UseCase/MoveFile/MoveFileDTO.ts b/packages/files/src/Domain/UseCase/MoveFile/MoveFileDTO.ts index 3f877ce29..1e38a9b4e 100644 --- a/packages/files/src/Domain/UseCase/MoveFile/MoveFileDTO.ts +++ b/packages/files/src/Domain/UseCase/MoveFile/MoveFileDTO.ts @@ -1,7 +1,7 @@ -import { VaultMoveType } from '@standardnotes/security' +import { GroupMoveType } from '@standardnotes/security' export type MoveFileDTO = { - moveType: VaultMoveType + moveType: GroupMoveType fromUuid: string toUuid: string resourceRemoteIdentifier: string diff --git a/packages/files/src/Domain/UseCase/RemoveFile/RemoveFile.ts b/packages/files/src/Domain/UseCase/RemoveFile/RemoveFile.ts index 66badd0f2..2df83ab26 100644 --- a/packages/files/src/Domain/UseCase/RemoveFile/RemoveFile.ts +++ b/packages/files/src/Domain/UseCase/RemoveFile/RemoveFile.ts @@ -22,7 +22,7 @@ export class RemoveFile implements UseCaseInterface { try { this.logger.debug(`Removing file: ${dto.resourceRemoteIdentifier}`) - const ownerUuid = isRemoveUserFileDTO(dto) ? dto.userUuid : dto.vaultUuid + const ownerUuid = isRemoveUserFileDTO(dto) ? dto.userUuid : dto.groupUuid const filePath = `${ownerUuid}/${dto.resourceRemoteIdentifier}` @@ -40,9 +40,9 @@ export class RemoveFile implements UseCaseInterface { ) } else { await this.domainEventPublisher.publish( - this.domainEventFactory.createVaultFileRemovedEvent({ - vaultUuid: dto.vaultUuid, - filePath: `${dto.vaultUuid}/${dto.resourceRemoteIdentifier}`, + this.domainEventFactory.createGroupFileRemovedEvent({ + groupUuid: dto.groupUuid, + filePath: `${dto.groupUuid}/${dto.resourceRemoteIdentifier}`, fileName: dto.resourceRemoteIdentifier, fileByteSize: removedFileSize, }), diff --git a/packages/files/src/Domain/UseCase/RemoveFile/RemoveFileDTO.ts b/packages/files/src/Domain/UseCase/RemoveFile/RemoveFileDTO.ts index 9b655dc8d..de735e910 100644 --- a/packages/files/src/Domain/UseCase/RemoveFile/RemoveFileDTO.ts +++ b/packages/files/src/Domain/UseCase/RemoveFile/RemoveFileDTO.ts @@ -1,4 +1,4 @@ -export type RemoveFileDTO = RemoveUserFileDTO | RemoveVaultFileDTO +export type RemoveFileDTO = RemoveUserFileDTO | RemoveGroupFileDTO export type RemoveUserFileDTO = { userUuid: string @@ -6,8 +6,8 @@ export type RemoveUserFileDTO = { regularSubscriptionUuid: string } -export type RemoveVaultFileDTO = { - vaultUuid: string +export type RemoveGroupFileDTO = { + groupUuid: string resourceRemoteIdentifier: string } diff --git a/packages/security/src/Domain/Token/GroupMoveType.ts b/packages/security/src/Domain/Token/GroupMoveType.ts new file mode 100644 index 000000000..b475ec02d --- /dev/null +++ b/packages/security/src/Domain/Token/GroupMoveType.ts @@ -0,0 +1 @@ +export type GroupMoveType = 'group-to-user' | 'user-to-group' | 'group-to-group' diff --git a/packages/security/src/Domain/Token/VaultValetTokenData.ts b/packages/security/src/Domain/Token/GroupValetTokenData.ts similarity index 68% rename from packages/security/src/Domain/Token/VaultValetTokenData.ts rename to packages/security/src/Domain/Token/GroupValetTokenData.ts index a428f0e3b..92463e59a 100644 --- a/packages/security/src/Domain/Token/VaultValetTokenData.ts +++ b/packages/security/src/Domain/Token/GroupValetTokenData.ts @@ -1,15 +1,15 @@ import { ValetTokenOperation } from './ValetTokenOperation' -import { VaultMoveType } from './VaultMoveType' +import { GroupMoveType } from './GroupMoveType' -export type VaultValetTokenData = { - vaultUuid: string +export type GroupValetTokenData = { + groupUuid: string permittedOperation: ValetTokenOperation remoteIdentifier: string unencryptedFileSize?: number uploadBytesUsed: number uploadBytesLimit: number moveOperation?: { - type: VaultMoveType + type: GroupMoveType fromUuid: string toUuid: string } diff --git a/packages/security/src/Domain/Token/VaultMoveType.ts b/packages/security/src/Domain/Token/VaultMoveType.ts deleted file mode 100644 index b00b62202..000000000 --- a/packages/security/src/Domain/Token/VaultMoveType.ts +++ /dev/null @@ -1 +0,0 @@ -export type VaultMoveType = 'vault-to-user' | 'user-to-vault' | 'vault-to-vault' diff --git a/packages/security/src/Domain/index.ts b/packages/security/src/Domain/index.ts index 04c612e6e..9e2732d62 100644 --- a/packages/security/src/Domain/index.ts +++ b/packages/security/src/Domain/index.ts @@ -10,8 +10,8 @@ export * from './Token/CrossServiceTokenData' export * from './Token/OfflineFeaturesTokenData' export * from './Token/OfflineUserTokenData' export * from './Token/SessionTokenData' -export * from './Token/VaultValetTokenData' +export * from './Token/GroupValetTokenData' export * from './Token/ValetTokenData' export * from './Token/ValetTokenOperation' -export * from './Token/VaultMoveType' +export * from './Token/GroupMoveType' export * from './Token/WebSocketConnectionToken' diff --git a/packages/syncing-server/README.md b/packages/syncing-server/README.md index 4dffed2c3..8b0d0be5b 100644 --- a/packages/syncing-server/README.md +++ b/packages/syncing-server/README.md @@ -3,5 +3,5 @@ ### Generating a migration ``` -yarn build && yarn typeorm migration:generate -d dist/src/Bootstrap/DataSource.js migrations/mysql/add_vault_uuid_to_items +yarn build && yarn typeorm migration:generate -d dist/src/Bootstrap/DataSource.js migrations/mysql/add_group_uuid_to_items ``` \ No newline at end of file diff --git a/packages/syncing-server/bin/server.ts b/packages/syncing-server/bin/server.ts index aa9c8814d..53339f240 100644 --- a/packages/syncing-server/bin/server.ts +++ b/packages/syncing-server/bin/server.ts @@ -4,9 +4,9 @@ import 'newrelic' import '../src/Controller/HealthCheckController' import '../src/Controller/ItemsController' -import '../src/Controller/VaultsController' -import '../src/Controller/VaultUsersController' -import '../src/Controller/VaultInvitesController' +import '../src/Controller/GroupsController' +import '../src/Controller/GroupUsersController' +import '../src/Controller/GroupInvitesController' import '../src/Controller/ContactsController' import helmet from 'helmet' diff --git a/packages/syncing-server/migrations/mysql/1684173017359-create_vault.ts b/packages/syncing-server/migrations/mysql/1684173017359-create_vault.ts index 70b67e7a3..b7f7534f3 100644 --- a/packages/syncing-server/migrations/mysql/1684173017359-create_vault.ts +++ b/packages/syncing-server/migrations/mysql/1684173017359-create_vault.ts @@ -1,11 +1,11 @@ import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm' -export class CreateVault1684173017359 implements MigrationInterface { - name = 'createVault1684173017359' +export class CreateGroup1684173017359 implements MigrationInterface { + name = 'createGroup1684173017359' public async up(queryRunner: QueryRunner): Promise { const table = new Table({ - name: 'vaults', + name: 'groups', columns: [ new TableColumn({ name: 'uuid', @@ -48,6 +48,6 @@ export class CreateVault1684173017359 implements MigrationInterface { } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable('vaults') + await queryRunner.dropTable('groups') } } diff --git a/packages/syncing-server/migrations/mysql/1684173023883-create_vault_user.ts b/packages/syncing-server/migrations/mysql/1684173023883-create_vault_user.ts index e4b30d648..670791e2a 100644 --- a/packages/syncing-server/migrations/mysql/1684173023883-create_vault_user.ts +++ b/packages/syncing-server/migrations/mysql/1684173023883-create_vault_user.ts @@ -1,11 +1,11 @@ import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm' -export class CreateVaultUser1684173023883 implements MigrationInterface { - name = 'createVaultUser1684173023883' +export class CreateGroupUser1684173023883 implements MigrationInterface { + name = 'createGroupUser1684173023883' public async up(queryRunner: QueryRunner): Promise { const table = new Table({ - name: 'vault_users', + name: 'group_users', columns: [ new TableColumn({ name: 'uuid', @@ -14,7 +14,7 @@ export class CreateVaultUser1684173023883 implements MigrationInterface { isPrimary: true, }), new TableColumn({ - name: 'vault_uuid', + name: 'group_uuid', type: 'varchar', length: '36', isNullable: false, @@ -46,6 +46,6 @@ export class CreateVaultUser1684173023883 implements MigrationInterface { } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable('vault_users') + await queryRunner.dropTable('group_users') } } diff --git a/packages/syncing-server/migrations/mysql/1684318117584-add_vault_uuid_to_items.ts b/packages/syncing-server/migrations/mysql/1684318117584-add_vault_uuid_to_items.ts index c62299479..4414be632 100644 --- a/packages/syncing-server/migrations/mysql/1684318117584-add_vault_uuid_to_items.ts +++ b/packages/syncing-server/migrations/mysql/1684318117584-add_vault_uuid_to_items.ts @@ -1,13 +1,13 @@ import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm' -export class AddVaultUuidToItems1684318117584 implements MigrationInterface { - name = 'addVaultUuidToItems1684318117584' +export class AddGroupUuidToItems1684318117584 implements MigrationInterface { + name = 'addGroupUuidToItems1684318117584' public async up(queryRunner: QueryRunner): Promise { await queryRunner.addColumn( 'items', new TableColumn({ - name: 'vault_uuid', + name: 'group_uuid', type: 'varchar', length: '36', isNullable: true, @@ -34,7 +34,7 @@ export class AddVaultUuidToItems1684318117584 implements MigrationInterface { } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropColumn('items', 'vault_uuid') + await queryRunner.dropColumn('items', 'group_uuid') await queryRunner.dropColumn('items', 'last_edited_by_uuid') } } diff --git a/packages/syncing-server/migrations/mysql/1684528646896-create_vault_invites.ts b/packages/syncing-server/migrations/mysql/1684528646896-create_vault_invites.ts index fd2a50cf7..1581da4ff 100644 --- a/packages/syncing-server/migrations/mysql/1684528646896-create_vault_invites.ts +++ b/packages/syncing-server/migrations/mysql/1684528646896-create_vault_invites.ts @@ -1,11 +1,11 @@ import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm' -export class CreateVaultInvites1684528646896 implements MigrationInterface { - name = 'createVaultInvites1684528646896' +export class CreateGroupInvites1684528646896 implements MigrationInterface { + name = 'createGroupInvites1684528646896' public async up(queryRunner: QueryRunner): Promise { const table = new Table({ - name: 'vault_invites', + name: 'group_invites', columns: [ new TableColumn({ name: 'uuid', @@ -14,7 +14,7 @@ export class CreateVaultInvites1684528646896 implements MigrationInterface { isPrimary: true, }), new TableColumn({ - name: 'vault_uuid', + name: 'group_uuid', type: 'varchar', length: '36', isNullable: false, @@ -44,7 +44,7 @@ export class CreateVaultInvites1684528646896 implements MigrationInterface { isNullable: false, }), new TableColumn({ - name: 'encrypted_vault_data', + name: 'encrypted_group_data', type: 'text', isNullable: false, }), @@ -69,6 +69,6 @@ export class CreateVaultInvites1684528646896 implements MigrationInterface { } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable('vault_invites') + await queryRunner.dropTable('group_invites') } } diff --git a/packages/syncing-server/migrations/mysql/1685469710749-create_remove_vault_users.ts b/packages/syncing-server/migrations/mysql/1685469710749-create_remove_vault_users.ts index 0b720700c..7ccfa4aeb 100644 --- a/packages/syncing-server/migrations/mysql/1685469710749-create_remove_vault_users.ts +++ b/packages/syncing-server/migrations/mysql/1685469710749-create_remove_vault_users.ts @@ -1,11 +1,11 @@ import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm' -export class CreateRemoveVaultUsers1685469710749 implements MigrationInterface { - name = 'createRemoveVaultUsers1685469710749' +export class CreateRemoveGroupUsers1685469710749 implements MigrationInterface { + name = 'createRemoveGroupUsers1685469710749' public async up(queryRunner: QueryRunner): Promise { const table = new Table({ - name: 'removed_vault_users', + name: 'removed_group_users', columns: [ new TableColumn({ name: 'uuid', @@ -14,7 +14,7 @@ export class CreateRemoveVaultUsers1685469710749 implements MigrationInterface { isPrimary: true, }), new TableColumn({ - name: 'vault_uuid', + name: 'group_uuid', type: 'varchar', length: '36', isNullable: false, @@ -46,6 +46,6 @@ export class CreateRemoveVaultUsers1685469710749 implements MigrationInterface { } public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable('removed_vault_users') + await queryRunner.dropTable('removed_group_users') } } diff --git a/packages/syncing-server/src/Bootstrap/CommonContainerConfigLoader.ts b/packages/syncing-server/src/Bootstrap/CommonContainerConfigLoader.ts index e4cdaced9..1f2198ecd 100644 --- a/packages/syncing-server/src/Bootstrap/CommonContainerConfigLoader.ts +++ b/packages/syncing-server/src/Bootstrap/CommonContainerConfigLoader.ts @@ -1,9 +1,9 @@ -import { TypeORMVaultInviteRepository } from '../Domain/VaultInvite/Repository/TypeORMVaultInviteRepository' +import { TypeORMGroupInviteRepository } from '../Domain/GroupInvite/Repository/TypeORMGroupInviteRepository' import { Contact } from './../Domain/Contact/Model/Contact' -import { VaultInvite } from '../Domain/VaultInvite/Model/VaultInvite' -import { VaultUser } from '../Domain/VaultUser/Model/VaultUser' -import { Vault } from '../Domain/Vault/Model/Vault' -import { TypeORMVaultRepository } from '../Domain/Vault/Repository/TypeORMVaultRepository' +import { GroupInvite } from '../Domain/GroupInvite/Model/GroupInvite' +import { GroupUser } from '../Domain/GroupUser/Model/GroupUser' +import { Group } from '../Domain/Group/Model/Group' +import { TypeORMGroupRepository } from '../Domain/Group/Repository/TypeORMGroupRepository' import * as winston from 'winston' import { Container, interfaces } from 'inversify' @@ -24,23 +24,23 @@ import { DomainEventFactory } from '../Domain/Event/DomainEventFactory' import { Timer, TimerInterface } from '@standardnotes/time' import { ItemTransferCalculatorInterface } from '../Domain/Item/ItemTransferCalculatorInterface' import { ItemTransferCalculator } from '../Domain/Item/ItemTransferCalculator' -import { VaultsRepositoryInterface } from '../Domain/Vault/Repository/VaultRepositoryInterface' -import { VaultUserRepositoryInterface } from '../Domain/VaultUser/Repository/VaultUserRepositoryInterface' -import { TypeORMVaultUserRepository } from '../Domain/VaultUser/Repository/TypeORMVaultUserRepository' -import { VaultUserProjection } from '../Projection/VaultUserProjection' -import { VaultUserProjector } from '../Projection/VaultUserProjector' -import { VaultProjection } from '../Projection/VaultProjection' -import { VaultProjector } from '../Projection/VaultProjector' -import { VaultInviteProjection } from '../Projection/VaultInviteProjection' -import { VaultInviteProjector } from '../Projection/VaultInviteProjector' +import { GroupsRepositoryInterface } from '../Domain/Group/Repository/GroupRepositoryInterface' +import { GroupUserRepositoryInterface } from '../Domain/GroupUser/Repository/GroupUserRepositoryInterface' +import { TypeORMGroupUserRepository } from '../Domain/GroupUser/Repository/TypeORMGroupUserRepository' +import { GroupUserProjection } from '../Projection/GroupUserProjection' +import { GroupUserProjector } from '../Projection/GroupUserProjector' +import { GroupProjection } from '../Projection/GroupProjection' +import { GroupProjector } from '../Projection/GroupProjector' +import { GroupInviteProjection } from '../Projection/GroupInviteProjection' +import { GroupInviteProjector } from '../Projection/GroupInviteProjector' import { ContactProjection } from '../Projection/ContactProjection' import { ContactProjector } from '../Projection/ContactProjector' -import { VaultInviteRepositoryInterface } from '../Domain/VaultInvite/Repository/VaultInviteRepositoryInterface' +import { GroupInviteRepositoryInterface } from '../Domain/GroupInvite/Repository/GroupInviteRepositoryInterface' import { ContactsRepositoryInterface } from '../Domain/Contact/Repository/ContactRepositoryInterface' import { TypeORMContactRepository } from '../Domain/Contact/Repository/TypeORMContactRepository' -import { RemovedVaultUserRepositoryInterface } from '../Domain/RemovedVaultUser/Repository/RemovedVaultUserRepositoryInterface' -import { TypeORMRemovedVaultUserRepository } from '../Domain/RemovedVaultUser/Repository/TypeORMRemovedVaultUserRepository' -import { RemovedVaultUser } from '../Domain/RemovedVaultUser/Model/RemovedVaultUser' +import { RemovedGroupUserRepositoryInterface } from '../Domain/RemovedGroupUser/Repository/RemovedGroupUserRepositoryInterface' +import { TypeORMRemovedGroupUserRepository } from '../Domain/RemovedGroupUser/Repository/TypeORMRemovedGroupUserRepository' +import { RemovedGroupUser } from '../Domain/RemovedGroupUser/Model/RemovedGroupUser' // eslint-disable-next-line @typescript-eslint/no-var-requires const newrelicFormatter = require('@newrelic/winston-enricher') @@ -99,23 +99,23 @@ export class CommonContainerConfigLoader { container.bind(TYPES.ItemRepository).toDynamicValue((context: interfaces.Context) => { return new TypeORMItemRepository(context.container.get(TYPES.ORMItemRepository)) }) - container.bind(TYPES.VaultRepository).toDynamicValue((context: interfaces.Context) => { - return new TypeORMVaultRepository(context.container.get(TYPES.ORMVaultRepository)) + container.bind(TYPES.GroupRepository).toDynamicValue((context: interfaces.Context) => { + return new TypeORMGroupRepository(context.container.get(TYPES.ORMGroupRepository)) }) container - .bind(TYPES.VaultUserRepository) + .bind(TYPES.GroupUserRepository) .toDynamicValue((context: interfaces.Context) => { - return new TypeORMVaultUserRepository(context.container.get(TYPES.ORMVaultUserRepository)) + return new TypeORMGroupUserRepository(context.container.get(TYPES.ORMGroupUserRepository)) }) container - .bind(TYPES.RemovedVaultUserRepository) + .bind(TYPES.RemovedGroupUserRepository) .toDynamicValue((context: interfaces.Context) => { - return new TypeORMRemovedVaultUserRepository(context.container.get(TYPES.ORMRemovedVaultUserRepository)) + return new TypeORMRemovedGroupUserRepository(context.container.get(TYPES.ORMRemovedGroupUserRepository)) }) container - .bind(TYPES.VaultInviteRepository) + .bind(TYPES.GroupInviteRepository) .toDynamicValue((context: interfaces.Context) => { - return new TypeORMVaultInviteRepository(context.container.get(TYPES.ORMVaultInviteRepository)) + return new TypeORMGroupInviteRepository(context.container.get(TYPES.ORMGroupInviteRepository)) }) container .bind(TYPES.ContactRepository) @@ -125,16 +125,16 @@ export class CommonContainerConfigLoader { // ORM container.bind>(TYPES.ORMItemRepository).toDynamicValue(() => AppDataSource.getRepository(Item)) - container.bind>(TYPES.ORMVaultRepository).toDynamicValue(() => AppDataSource.getRepository(Vault)) + container.bind>(TYPES.ORMGroupRepository).toDynamicValue(() => AppDataSource.getRepository(Group)) container - .bind>(TYPES.ORMVaultUserRepository) - .toDynamicValue(() => AppDataSource.getRepository(VaultUser)) + .bind>(TYPES.ORMGroupUserRepository) + .toDynamicValue(() => AppDataSource.getRepository(GroupUser)) container - .bind>(TYPES.ORMRemovedVaultUserRepository) - .toDynamicValue(() => AppDataSource.getRepository(RemovedVaultUser)) + .bind>(TYPES.ORMRemovedGroupUserRepository) + .toDynamicValue(() => AppDataSource.getRepository(RemovedGroupUser)) container - .bind>(TYPES.ORMVaultInviteRepository) - .toDynamicValue(() => AppDataSource.getRepository(VaultInvite)) + .bind>(TYPES.ORMGroupInviteRepository) + .toDynamicValue(() => AppDataSource.getRepository(GroupInvite)) container .bind>(TYPES.ORMContactRepository) .toDynamicValue(() => AppDataSource.getRepository(Contact)) @@ -145,16 +145,16 @@ export class CommonContainerConfigLoader { .toDynamicValue((context: interfaces.Context) => { return new ItemProjector(context.container.get(TYPES.Timer)) }) - container.bind>(TYPES.VaultProjector).toDynamicValue(() => { - return new VaultProjector() + container.bind>(TYPES.GroupProjector).toDynamicValue(() => { + return new GroupProjector() }) - container.bind>(TYPES.VaultUserProjector).toDynamicValue(() => { - return new VaultUserProjector() + container.bind>(TYPES.GroupUserProjector).toDynamicValue(() => { + return new GroupUserProjector() }) container - .bind>(TYPES.VaultInviteProjector) + .bind>(TYPES.GroupInviteProjector) .toDynamicValue(() => { - return new VaultInviteProjector() + return new GroupInviteProjector() }) container.bind>(TYPES.ContactProjector).toDynamicValue(() => { return new ContactProjector() diff --git a/packages/syncing-server/src/Bootstrap/DataSource.ts b/packages/syncing-server/src/Bootstrap/DataSource.ts index 5fcea8da8..cff44e412 100644 --- a/packages/syncing-server/src/Bootstrap/DataSource.ts +++ b/packages/syncing-server/src/Bootstrap/DataSource.ts @@ -1,13 +1,13 @@ -import { RemovedVaultUser } from './../Domain/RemovedVaultUser/Model/RemovedVaultUser' +import { RemovedGroupUser } from './../Domain/RemovedGroupUser/Model/RemovedGroupUser' import { Contact } from './../Domain/Contact/Model/Contact' -import { VaultInvite } from '../Domain/VaultInvite/Model/VaultInvite' -import { VaultUser } from '../Domain/VaultUser/Model/VaultUser' +import { GroupInvite } from '../Domain/GroupInvite/Model/GroupInvite' +import { GroupUser } from '../Domain/GroupUser/Model/GroupUser' import { DataSource, LoggerOptions } from 'typeorm' import { MysqlConnectionOptions } from 'typeorm/driver/mysql/MysqlConnectionOptions' import { Item } from '../Domain/Item/Item' import { Env } from './Env' import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions' -import { Vault } from '../Domain/Vault/Model/Vault' +import { Group } from '../Domain/Group/Model/Group' const env: Env = new Env() env.load() @@ -43,7 +43,7 @@ const replicationConfig = { const commonDataSourceOptions = { maxQueryExecutionTime, - entities: [Item, Vault, VaultUser, RemovedVaultUser, VaultInvite, Contact], + entities: [Item, Group, GroupUser, RemovedGroupUser, GroupInvite, Contact], migrations: [`dist/migrations/${isConfiguredForMySQL ? 'mysql' : 'sqlite'}/*.js`], migrationsRun: true, logging: env.get('DB_DEBUG_LEVEL'), diff --git a/packages/syncing-server/src/Bootstrap/ServerContainerConfigLoader.ts b/packages/syncing-server/src/Bootstrap/ServerContainerConfigLoader.ts index 1bc44e5ca..221828858 100644 --- a/packages/syncing-server/src/Bootstrap/ServerContainerConfigLoader.ts +++ b/packages/syncing-server/src/Bootstrap/ServerContainerConfigLoader.ts @@ -1,9 +1,9 @@ import { GetGlobalItem } from './../Domain/UseCase/GetGlobalItem/GetGlobalItem' import { ContactFactory } from './../Domain/Contact/Factory/ContactFactory' -import { VaultServiceInterface } from '../Domain/Vault/Service/VaultServiceInterface' -import { VaultUserFactory } from '../Domain/VaultUser/Factory/VaultUserFactory' -import { VaultFactoryInterface } from '../Domain/Vault/Factory/VaultFactoryInterface' +import { GroupServiceInterface } from '../Domain/Group/Service/GroupServiceInterface' +import { GroupUserFactory } from '../Domain/GroupUser/Factory/GroupUserFactory' +import { GroupFactoryInterface } from '../Domain/Group/Factory/GroupFactoryInterface' import { Container, interfaces } from 'inversify' import { Env } from './Env' @@ -20,7 +20,7 @@ import { SyncItems } from '../Domain/UseCase/SyncItems' import { ItemConflictProjector } from '../Projection/ItemConflictProjector' import { ItemSaveValidatorInterface } from '../Domain/Item/SaveValidator/ItemSaveValidatorInterface' import { ItemSaveValidator } from '../Domain/Item/SaveValidator/ItemSaveValidator' -import { VaultFilter } from '../Domain/Item/SaveRule/VaultFilter' +import { GroupFilter } from '../Domain/Item/SaveRule/GroupFilter' import { OwnershipFilter } from '../Domain/Item/SaveRule/OwnershipFilter' import { TimeDifferenceFilter } from '../Domain/Item/SaveRule/TimeDifferenceFilter' import { ItemFactoryInterface } from '../Domain/Item/ItemFactoryInterface' @@ -37,21 +37,21 @@ import { ItemConflict } from '../Domain/Item/ItemConflict' import { ItemConflictProjection } from '../Projection/ItemConflictProjection' import { CommonContainerConfigLoader } from './CommonContainerConfigLoader' import { TokenEncoder, TokenEncoderInterface, ValetTokenData } from '@standardnotes/security' -import { VaultFactory } from '../Domain/Vault/Factory/VaultFactory' -import { VaultService } from '../Domain/Vault/Service/VaultService' -import { VaultUserService } from '../Domain/VaultUser/Service/VaultUserService' -import { VaultUserServiceInterface } from '../Domain/VaultUser/Service/VaultUserServiceInterface' -import { VaultInviteServiceInterface } from '../Domain/VaultInvite/Service/VaultInviteServiceInterface' -import { VaultInviteService } from '../Domain/VaultInvite/Service/VaultInviteService' -import { VaultInviteFactory } from '../Domain/VaultInvite/Factory/VaultInviteFactory' +import { GroupFactory } from '../Domain/Group/Factory/GroupFactory' +import { GroupService } from '../Domain/Group/Service/GroupService' +import { GroupUserService } from '../Domain/GroupUser/Service/GroupUserService' +import { GroupUserServiceInterface } from '../Domain/GroupUser/Service/GroupUserServiceInterface' +import { GroupInviteServiceInterface } from '../Domain/GroupInvite/Service/GroupInviteServiceInterface' +import { GroupInviteService } from '../Domain/GroupInvite/Service/GroupInviteService' +import { GroupInviteFactory } from '../Domain/GroupInvite/Factory/GroupInviteFactory' import { ContactServiceInterface } from '../Domain/Contact/Service/ContactServiceInterface' import { ContactService } from '../Domain/Contact/Service/ContactService' import { ContactFactoryInterface } from '../Domain/Contact/Factory/ContactFactoryInterface' -import { VaultSnjsVersionFilter } from '../Domain/Item/SaveRule/VaultSnjsVersionFilter' -import { CreateVaultFileValetToken } from '../Domain/UseCase/CreateVaultFileValetToken/CreateVaultFileValetToken' -import { RemovedVaultUserService } from '../Domain/RemovedVaultUser/Service/RemovedVaultUserService' -import { RemovedVaultUserServiceInterface } from '../Domain/RemovedVaultUser/Service/RemovedVaultUserServiceInterface' -import { RemovedVaultUserFactory } from '../Domain/RemovedVaultUser/Factory/RemovedVaultUserFactory' +import { GroupSnjsVersionFilter } from '../Domain/Item/SaveRule/GroupSnjsVersionFilter' +import { CreateGroupFileValetToken } from '../Domain/UseCase/CreateGroupFileValetToken/CreateGroupFileValetToken' +import { RemovedGroupUserService } from '../Domain/RemovedGroupUser/Service/RemovedGroupUserService' +import { RemovedGroupUserServiceInterface } from '../Domain/RemovedGroupUser/Service/RemovedGroupUserServiceInterface' +import { RemovedGroupUserFactory } from '../Domain/RemovedGroupUser/Factory/RemovedGroupUserFactory' export class ServerContainerConfigLoader extends CommonContainerConfigLoader { private readonly DEFAULT_CONTENT_SIZE_TRANSFER_LIMIT = 10_000_000 @@ -103,8 +103,8 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader { container.bind(TYPES.SyncItems).toDynamicValue((context: interfaces.Context) => { return new SyncItems( context.container.get(TYPES.ItemService), - context.container.get(TYPES.VaultService), - context.container.get(TYPES.VaultInviteService), + context.container.get(TYPES.GroupService), + context.container.get(TYPES.GroupInviteService), context.container.get(TYPES.ContactService), ) }) @@ -119,11 +119,11 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader { }) container - .bind(TYPES.CreateVaultFileReadValetToken) + .bind(TYPES.CreateGroupFileReadValetToken) .toDynamicValue((context: interfaces.Context) => { - return new CreateVaultFileValetToken( - context.container.get(TYPES.VaultService), - context.container.get(TYPES.VaultUserService), + return new CreateGroupFileValetToken( + context.container.get(TYPES.GroupService), + context.container.get(TYPES.GroupUserService), context.container.get(TYPES.ValetTokenEncoder), context.container.get(TYPES.VALET_TOKEN_TTL), ) @@ -147,48 +147,48 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader { context.container.get(TYPES.Timer), context.container.get(TYPES.ItemProjector), context.container.get(TYPES.MAX_ITEMS_LIMIT), - context.container.get(TYPES.VaultUserRepository), - context.container.get(TYPES.VaultService), + context.container.get(TYPES.GroupUserRepository), + context.container.get(TYPES.GroupService), context.container.get(TYPES.Logger), ) }) // Services - container.bind(TYPES.VaultService).toDynamicValue((context: interfaces.Context) => { - return new VaultService( - context.container.get(TYPES.VaultRepository), - context.container.get(TYPES.VaultFactory), - context.container.get(TYPES.VaultUserService), - context.container.get(TYPES.VaultInviteService), + container.bind(TYPES.GroupService).toDynamicValue((context: interfaces.Context) => { + return new GroupService( + context.container.get(TYPES.GroupRepository), + context.container.get(TYPES.GroupFactory), + context.container.get(TYPES.GroupUserService), + context.container.get(TYPES.GroupInviteService), context.container.get(TYPES.Timer), ) }) - container.bind(TYPES.VaultUserService).toDynamicValue((context: interfaces.Context) => { - return new VaultUserService( - context.container.get(TYPES.VaultRepository), - context.container.get(TYPES.VaultUserRepository), - context.container.get(TYPES.VaultUserFactory), - context.container.get(TYPES.RemovedVaultUserService), + container.bind(TYPES.GroupUserService).toDynamicValue((context: interfaces.Context) => { + return new GroupUserService( + context.container.get(TYPES.GroupRepository), + context.container.get(TYPES.GroupUserRepository), + context.container.get(TYPES.GroupUserFactory), + context.container.get(TYPES.RemovedGroupUserService), context.container.get(TYPES.Timer), ) }) container - .bind(TYPES.RemovedVaultUserService) + .bind(TYPES.RemovedGroupUserService) .toDynamicValue((context: interfaces.Context) => { - return new RemovedVaultUserService( - context.container.get(TYPES.VaultRepository), - context.container.get(TYPES.RemovedVaultUserRepository), - context.container.get(TYPES.RemovedVaultUserFactory), + return new RemovedGroupUserService( + context.container.get(TYPES.GroupRepository), + context.container.get(TYPES.RemovedGroupUserRepository), + context.container.get(TYPES.RemovedGroupUserFactory), context.container.get(TYPES.Timer), ) }) container - .bind(TYPES.VaultInviteService) + .bind(TYPES.GroupInviteService) .toDynamicValue((context: interfaces.Context) => { - return new VaultInviteService( - context.container.get(TYPES.VaultRepository), - context.container.get(TYPES.VaultInviteRepository), - context.container.get(TYPES.VaultInviteFactory), - context.container.get(TYPES.VaultUserService), + return new GroupInviteService( + context.container.get(TYPES.GroupRepository), + context.container.get(TYPES.GroupInviteRepository), + context.container.get(TYPES.GroupInviteFactory), + context.container.get(TYPES.GroupUserService), context.container.get(TYPES.Timer), ) }) @@ -212,9 +212,9 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader { context.container.get(TYPES.ItemProjector), context.container.get(TYPES.ItemConflictProjector), context.container.get(TYPES.SavedItemProjector), - context.container.get(TYPES.VaultInviteProjector), + context.container.get(TYPES.GroupInviteProjector), context.container.get(TYPES.ContactProjector), - context.container.get(TYPES.VaultProjector), + context.container.get(TYPES.GroupProjector), ) }) container @@ -229,19 +229,19 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader { container.bind(TYPES.ItemFactory).toDynamicValue((context: interfaces.Context) => { return new ItemFactory(context.container.get(TYPES.Timer), context.container.get(TYPES.ItemProjector)) }) - container.bind(TYPES.VaultFactory).toDynamicValue((context: interfaces.Context) => { - return new VaultFactory(context.container.get(TYPES.Timer)) + container.bind(TYPES.GroupFactory).toDynamicValue((context: interfaces.Context) => { + return new GroupFactory(context.container.get(TYPES.Timer)) }) - container.bind(TYPES.VaultUserFactory).toDynamicValue((context: interfaces.Context) => { - return new VaultUserFactory(context.container.get(TYPES.Timer)) + container.bind(TYPES.GroupUserFactory).toDynamicValue((context: interfaces.Context) => { + return new GroupUserFactory(context.container.get(TYPES.Timer)) }) container - .bind(TYPES.RemovedVaultUserFactory) + .bind(TYPES.RemovedGroupUserFactory) .toDynamicValue((context: interfaces.Context) => { - return new RemovedVaultUserFactory(context.container.get(TYPES.Timer)) + return new RemovedGroupUserFactory(context.container.get(TYPES.Timer)) }) - container.bind(TYPES.VaultInviteFactory).toDynamicValue((context: interfaces.Context) => { - return new VaultInviteFactory(context.container.get(TYPES.Timer)) + container.bind(TYPES.GroupInviteFactory).toDynamicValue((context: interfaces.Context) => { + return new GroupInviteFactory(context.container.get(TYPES.Timer)) }) container.bind(TYPES.ContactFactory).toDynamicValue((context: interfaces.Context) => { return new ContactFactory(context.container.get(TYPES.Timer)) @@ -249,10 +249,10 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader { container.bind(TYPES.OwnershipFilter).toDynamicValue(() => new OwnershipFilter()) container - .bind(TYPES.VaultFilter) + .bind(TYPES.GroupFilter) .toDynamicValue( (context: interfaces.Context) => - new VaultFilter(context.container.get(TYPES.VaultService), context.container.get(TYPES.VaultUserService)), + new GroupFilter(context.container.get(TYPES.GroupService), context.container.get(TYPES.GroupUserService)), ) container .bind(TYPES.TimeDifferenceFilter) @@ -261,16 +261,16 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader { container.bind(TYPES.ContentTypeFilter).toDynamicValue(() => new ContentTypeFilter()) container.bind(TYPES.ContentFilter).toDynamicValue(() => new ContentFilter()) container - .bind(TYPES.VaultSnjsVersionFilter) - .toDynamicValue(() => new VaultSnjsVersionFilter()) + .bind(TYPES.GroupSnjsVersionFilter) + .toDynamicValue(() => new GroupSnjsVersionFilter()) container .bind(TYPES.ItemSaveValidator) .toDynamicValue((context: interfaces.Context) => { return new ItemSaveValidator([ context.container.get(TYPES.OwnershipFilter), - context.container.get(TYPES.VaultFilter), - context.container.get(TYPES.VaultSnjsVersionFilter), + context.container.get(TYPES.GroupFilter), + context.container.get(TYPES.GroupSnjsVersionFilter), context.container.get(TYPES.TimeDifferenceFilter), context.container.get(TYPES.UuidFilter), context.container.get(TYPES.ContentTypeFilter), diff --git a/packages/syncing-server/src/Bootstrap/Types.ts b/packages/syncing-server/src/Bootstrap/Types.ts index 1b2f4df71..10ed7c773 100644 --- a/packages/syncing-server/src/Bootstrap/Types.ts +++ b/packages/syncing-server/src/Bootstrap/Types.ts @@ -9,19 +9,19 @@ const TYPES = { // Repositories ItemRepository: Symbol.for('ItemRepository'), - VaultRepository: Symbol.for('VaultRepository'), - VaultUserRepository: Symbol.for('VaultUserRepository'), - RemovedVaultUserRepository: Symbol.for('RemovedVaultUserRepository'), + GroupRepository: Symbol.for('GroupRepository'), + GroupUserRepository: Symbol.for('GroupUserRepository'), + RemovedGroupUserRepository: Symbol.for('RemovedGroupUserRepository'), ContactRepository: Symbol.for('ContactRepository'), - VaultInviteRepository: Symbol.for('VaultInviteRepository'), + GroupInviteRepository: Symbol.for('GroupInviteRepository'), // ORM ORMItemRepository: Symbol.for('ORMItemRepository'), - ORMVaultRepository: Symbol.for('ORMVaultRepository'), - ORMVaultUserRepository: Symbol.for('ORMVaultUserRepository'), - ORMRemovedVaultUserRepository: Symbol.for('ORMRemovedVaultUserRepository'), + ORMGroupRepository: Symbol.for('ORMGroupRepository'), + ORMGroupUserRepository: Symbol.for('ORMGroupUserRepository'), + ORMRemovedGroupUserRepository: Symbol.for('ORMRemovedGroupUserRepository'), ORMContactRepository: Symbol.for('ORMContactRepository'), - ORMVaultInviteRepository: Symbol.for('ORMVaultInviteRepository'), + ORMGroupInviteRepository: Symbol.for('ORMGroupInviteRepository'), // Middleware AuthMiddleware: Symbol.for('AuthMiddleware'), @@ -30,25 +30,25 @@ const TYPES = { ItemProjector: Symbol.for('ItemProjector'), SavedItemProjector: Symbol.for('SavedItemProjector'), ItemConflictProjector: Symbol.for('ItemConflictProjector'), - VaultProjector: Symbol.for('VaultProjector'), - VaultUserProjector: Symbol.for('VaultUserProjector'), + GroupProjector: Symbol.for('GroupProjector'), + GroupUserProjector: Symbol.for('GroupUserProjector'), ContactProjector: Symbol.for('ContactProjector'), - VaultInviteProjector: Symbol.for('VaultInviteProjector'), + GroupInviteProjector: Symbol.for('GroupInviteProjector'), // Services - VaultService: Symbol.for('VaultService'), - VaultUserService: Symbol.for('VaultUserService'), - RemovedVaultUserService: Symbol.for('RemovedVaultUserService'), + GroupService: Symbol.for('GroupService'), + GroupUserService: Symbol.for('GroupUserService'), + RemovedGroupUserService: Symbol.for('RemovedGroupUserService'), ContactService: Symbol.for('ContactService'), - VaultInviteService: Symbol.for('VaultInviteService'), + GroupInviteService: Symbol.for('GroupInviteService'), // Factories ItemFactory: Symbol.for('ItemFactory'), - VaultFactory: Symbol.for('VaultFactory'), - VaultUserFactory: Symbol.for('VaultUserFactory'), - RemovedVaultUserFactory: Symbol.for('RemovedVaultUserFactory'), + GroupFactory: Symbol.for('GroupFactory'), + GroupUserFactory: Symbol.for('GroupUserFactory'), + RemovedGroupUserFactory: Symbol.for('RemovedGroupUserFactory'), ContactFactory: Symbol.for('ContactFactory'), - VaultInviteFactory: Symbol.for('VaultInviteFactory'), + GroupInviteFactory: Symbol.for('GroupInviteFactory'), // env vars REDIS_URL: Symbol.for('REDIS_URL'), @@ -75,15 +75,15 @@ const TYPES = { CheckIntegrity: Symbol.for('CheckIntegrity'), GetItem: Symbol.for('GetItem'), GetGlobalItem: Symbol.for('GetGlobalItem'), - CreateVaultFileReadValetToken: Symbol.for('CreateVaultFileReadValetToken'), + CreateGroupFileReadValetToken: Symbol.for('CreateGroupFileReadValetToken'), // Handlers UserCredentialsChangedEventHandler: Symbol.for('UserCredentialsChangedEventHandler'), AccountDeletionRequestedEventHandler: Symbol.for('AccountDeletionRequestedEventHandler'), DuplicateItemSyncedEventHandler: Symbol.for('DuplicateItemSyncedEventHandler'), EmailBackupRequestedEventHandler: Symbol.for('EmailBackupRequestedEventHandler'), ItemRevisionCreationRequestedEventHandler: Symbol.for('ItemRevisionCreationRequestedEventHandler'), - VaultFileUploadedEventHandler: Symbol.for('VaultFileUploadedEventHandler'), - VaultFileRemovedEventHandler: Symbol.for('VaultFileRemovedEventHandler'), + GroupFileUploadedEventHandler: Symbol.for('GroupFileUploadedEventHandler'), + GroupFileRemovedEventHandler: Symbol.for('GroupFileRemovedEventHandler'), ContentDecoder: Symbol.for('ContentDecoder'), DomainEventPublisher: Symbol.for('DomainEventPublisher'), @@ -101,8 +101,8 @@ const TYPES = { ItemBackupService: Symbol.for('ItemBackupService'), ItemSaveValidator: Symbol.for('ItemSaveValidator'), OwnershipFilter: Symbol.for('OwnershipFilter'), - VaultFilter: Symbol.for('VaultFilter'), - VaultSnjsVersionFilter: Symbol.for('VaultSnjsVersionFilter'), + GroupFilter: Symbol.for('GroupFilter'), + GroupSnjsVersionFilter: Symbol.for('GroupSnjsVersionFilter'), TimeDifferenceFilter: Symbol.for('TimeDifferenceFilter'), UuidFilter: Symbol.for('UuidFilter'), ContentTypeFilter: Symbol.for('ContentTypeFilter'), diff --git a/packages/syncing-server/src/Bootstrap/WorkerContainerConfigLoader.ts b/packages/syncing-server/src/Bootstrap/WorkerContainerConfigLoader.ts index 07e409188..4b3dae61e 100644 --- a/packages/syncing-server/src/Bootstrap/WorkerContainerConfigLoader.ts +++ b/packages/syncing-server/src/Bootstrap/WorkerContainerConfigLoader.ts @@ -31,8 +31,8 @@ import { ItemRevisionCreationRequestedEventHandler } from '../Domain/Handler/Ite import { FSItemBackupService } from '../Infra/FS/FSItemBackupService' import { CommonContainerConfigLoader } from './CommonContainerConfigLoader' import { UserCredentialsChangedEventHandler } from '../Domain/Handler/UserCredentialsChangedEventHandler' -import { VaultFileUploadedEventHandler } from '../Domain/Handler/VaultFileUploadedEventHandler' -import { VaultFileRemovedEventHandler } from '../Domain/Handler/VaultFileRemovedEventHandler' +import { GroupFileUploadedEventHandler } from '../Domain/Handler/GroupFileUploadedEventHandler' +import { GroupFileRemovedEventHandler } from '../Domain/Handler/GroupFileRemovedEventHandler' export class WorkerContainerConfigLoader extends CommonContainerConfigLoader { private readonly DEFAULT_FILE_UPLOAD_PATH = `${__dirname}/../../uploads` @@ -140,13 +140,13 @@ export class WorkerContainerConfigLoader extends CommonContainerConfigLoader { .toDynamicValue((context: interfaces.Context) => { return new UserCredentialsChangedEventHandler( context.container.get(TYPES.ContactRepository), - context.container.get(TYPES.VaultInviteRepository), + context.container.get(TYPES.GroupInviteRepository), context.container.get(TYPES.Timer), ) }) - container.bind(TYPES.VaultFileUploadedEventHandler).to(VaultFileUploadedEventHandler) - container.bind(TYPES.VaultFileRemovedEventHandler).to(VaultFileRemovedEventHandler) + container.bind(TYPES.GroupFileUploadedEventHandler).to(GroupFileUploadedEventHandler) + container.bind(TYPES.GroupFileRemovedEventHandler).to(GroupFileRemovedEventHandler) // Services container.bind(TYPES.ContentDecoder).toDynamicValue(() => new ContentDecoder()) @@ -199,8 +199,8 @@ export class WorkerContainerConfigLoader extends CommonContainerConfigLoader { ['EMAIL_BACKUP_REQUESTED', context.container.get(TYPES.EmailBackupRequestedEventHandler)], ['ITEM_REVISION_CREATION_REQUESTED', context.container.get(TYPES.ItemRevisionCreationRequestedEventHandler)], ['USER_CREDENTIALS_CHANGED', container.get(TYPES.UserCredentialsChangedEventHandler)], - ['VAULT_FILE_REMOVED', container.get(TYPES.VaultFileRemovedEventHandler)], - ['VAULT_FILE_UPLOADED', container.get(TYPES.VaultFileUploadedEventHandler)], + ['GROUP_FILE_REMOVED', container.get(TYPES.GroupFileRemovedEventHandler)], + ['GROUP_FILE_UPLOADED', container.get(TYPES.GroupFileUploadedEventHandler)], ]) const handler = diff --git a/packages/syncing-server/src/Controller/VaultInvitesController.ts b/packages/syncing-server/src/Controller/GroupInvitesController.ts similarity index 61% rename from packages/syncing-server/src/Controller/VaultInvitesController.ts rename to packages/syncing-server/src/Controller/GroupInvitesController.ts index cad21a7f2..288c34bb1 100644 --- a/packages/syncing-server/src/Controller/VaultInvitesController.ts +++ b/packages/syncing-server/src/Controller/GroupInvitesController.ts @@ -10,29 +10,29 @@ import { } from 'inversify-express-utils' import TYPES from '../Bootstrap/Types' import { inject } from 'inversify' -import { VaultInviteServiceInterface } from '../Domain/VaultInvite/Service/VaultInviteServiceInterface' -import { VaultInviteProjector } from '../Projection/VaultInviteProjector' +import { GroupInviteServiceInterface } from '../Domain/GroupInvite/Service/GroupInviteServiceInterface' +import { GroupInviteProjector } from '../Projection/GroupInviteProjector' -@controller('/vaults') -export class VaultInvitesController extends BaseHttpController { +@controller('/groups') +export class GroupInvitesController extends BaseHttpController { constructor( - @inject(TYPES.VaultInviteService) private vaultInviteService: VaultInviteServiceInterface, - @inject(TYPES.VaultInviteProjector) private vaultInviteProjector: VaultInviteProjector, + @inject(TYPES.GroupInviteService) private groupInviteService: GroupInviteServiceInterface, + @inject(TYPES.GroupInviteProjector) private groupInviteProjector: GroupInviteProjector, ) { super() } - @httpPost('/:vaultUuid/invites', TYPES.AuthMiddleware) - public async createVaultInvite( + @httpPost('/:groupUuid/invites', TYPES.AuthMiddleware) + public async createGroupInvite( request: Request, response: Response, ): Promise { - const result = await this.vaultInviteService.createInvite({ + const result = await this.groupInviteService.createInvite({ originatorUuid: response.locals.user.uuid, - vaultUuid: request.params.vaultUuid, + groupUuid: request.params.groupUuid, userUuid: request.body.invitee_uuid, inviterPublicKey: request.body.inviter_public_key, - encryptedVaultData: request.body.encrypted_vault_data, + encryptedGroupData: request.body.encrypted_group_data, inviteType: request.body.invite_type, permissions: request.body.permissions, }) @@ -41,19 +41,19 @@ export class VaultInvitesController extends BaseHttpController { return this.errorResponse(400, 'Could not create invite') } - return this.json({ invite: this.vaultInviteProjector.projectFull(result) }) + return this.json({ invite: this.groupInviteProjector.projectFull(result) }) } - @httpPatch('/:vaultUuid/invites/:inviteUuid', TYPES.AuthMiddleware) - public async updateVaultInvite( + @httpPatch('/:groupUuid/invites/:inviteUuid', TYPES.AuthMiddleware) + public async updateGroupInvite( request: Request, response: Response, ): Promise { - const result = await this.vaultInviteService.updateInvite({ + const result = await this.groupInviteService.updateInvite({ originatorUuid: response.locals.user.uuid, inviteUuid: request.params.inviteUuid, inviterPublicKey: request.body.inviter_public_key, - encryptedVaultData: request.body.encrypted_vault_data, + encryptedGroupData: request.body.encrypted_group_data, permissions: request.body.permissions, }) @@ -61,15 +61,15 @@ export class VaultInvitesController extends BaseHttpController { return this.errorResponse(400, 'Could not update invite') } - return this.json({ invite: this.vaultInviteProjector.projectFull(result) }) + return this.json({ invite: this.groupInviteProjector.projectFull(result) }) } - @httpPost('/:vaultUuid/invites/:inviteUuid/accept', TYPES.AuthMiddleware) - public async acceptVaultInvite( + @httpPost('/:groupUuid/invites/:inviteUuid/accept', TYPES.AuthMiddleware) + public async acceptGroupInvite( request: Request, response: Response, ): Promise { - const result = await this.vaultInviteService.acceptInvite({ + const result = await this.groupInviteService.acceptInvite({ originatorUuid: response.locals.user.uuid, inviteUuid: request.params.inviteUuid, }) @@ -81,12 +81,12 @@ export class VaultInvitesController extends BaseHttpController { return this.json({ success: true }) } - @httpPost('/:vaultUuid/invites/:inviteUuid/decline', TYPES.AuthMiddleware) - public async declineVaultInvite( + @httpPost('/:groupUuid/invites/:inviteUuid/decline', TYPES.AuthMiddleware) + public async declineGroupInvite( request: Request, response: Response, ): Promise { - const result = await this.vaultInviteService.declineInvite({ + const result = await this.groupInviteService.declineInvite({ originatorUuid: response.locals.user.uuid, inviteUuid: request.params.inviteUuid, }) @@ -103,7 +103,7 @@ export class VaultInvitesController extends BaseHttpController { _request: Request, response: Response, ): Promise { - await this.vaultInviteService.deleteAllInboundInvites({ + await this.groupInviteService.deleteAllInboundInvites({ userUuid: response.locals.user.uuid, }) @@ -115,15 +115,15 @@ export class VaultInvitesController extends BaseHttpController { _request: Request, response: Response, ): Promise { - const result = await this.vaultInviteService.getOutboundInvitesForUser({ + const result = await this.groupInviteService.getOutboundInvitesForUser({ userUuid: response.locals.user.uuid, }) if (!result) { - return this.errorResponse(400, 'Could not get vault invites') + return this.errorResponse(400, 'Could not get group invites') } - const projected = result.map((invite) => this.vaultInviteProjector.projectFull(invite)) + const projected = result.map((invite) => this.groupInviteProjector.projectFull(invite)) return this.json({ invites: projected }) } @@ -133,46 +133,46 @@ export class VaultInvitesController extends BaseHttpController { _request: Request, response: Response, ): Promise { - const result = await this.vaultInviteService.getInvitesForUser({ + const result = await this.groupInviteService.getInvitesForUser({ userUuid: response.locals.user.uuid, }) if (!result) { - return this.errorResponse(400, 'Could not get vault invites') + return this.errorResponse(400, 'Could not get group invites') } - const projected = result.map((invite) => this.vaultInviteProjector.projectFull(invite)) + const projected = result.map((invite) => this.groupInviteProjector.projectFull(invite)) return this.json({ invites: projected }) } - @httpGet('/:vaultUuid/invites', TYPES.AuthMiddleware) - public async getVaultInvites( + @httpGet('/:groupUuid/invites', TYPES.AuthMiddleware) + public async getGroupInvites( request: Request, response: Response, ): Promise { - const result = await this.vaultInviteService.getInvitesForVault({ + const result = await this.groupInviteService.getInvitesForGroup({ originatorUuid: response.locals.user.uuid, - vaultUuid: request.params.vaultUuid, + groupUuid: request.params.groupUuid, }) if (!result) { - return this.errorResponse(400, 'Could not get vault invites') + return this.errorResponse(400, 'Could not get group invites') } - const projected = result.map((invite) => this.vaultInviteProjector.projectFull(invite)) + const projected = result.map((invite) => this.groupInviteProjector.projectFull(invite)) return this.json({ invites: projected }) } - @httpDelete('/:vaultUuid/invites/:inviteUuid', TYPES.AuthMiddleware) - public async deleteVaultInvite( + @httpDelete('/:groupUuid/invites/:inviteUuid', TYPES.AuthMiddleware) + public async deleteGroupInvite( request: Request, response: Response, ): Promise { - const result = await this.vaultInviteService.deleteInvite({ + const result = await this.groupInviteService.deleteInvite({ originatorUuid: response.locals.user.uuid, - vaultUuid: request.params.vaultUuid, + groupUuid: request.params.groupUuid, inviteUuid: request.params.inviteUuid, }) diff --git a/packages/syncing-server/src/Controller/VaultUsersController.ts b/packages/syncing-server/src/Controller/GroupUsersController.ts similarity index 55% rename from packages/syncing-server/src/Controller/VaultUsersController.ts rename to packages/syncing-server/src/Controller/GroupUsersController.ts index b87feaa16..6b9babdae 100644 --- a/packages/syncing-server/src/Controller/VaultUsersController.ts +++ b/packages/syncing-server/src/Controller/GroupUsersController.ts @@ -2,48 +2,48 @@ import { Request, Response } from 'express' import { BaseHttpController, controller, results, httpDelete, httpGet } from 'inversify-express-utils' import TYPES from '../Bootstrap/Types' import { inject } from 'inversify' -import { VaultUserServiceInterface } from '../Domain/VaultUser/Service/VaultUserServiceInterface' -import { VaultUserProjector } from '../Projection/VaultUserProjector' +import { GroupUserServiceInterface } from '../Domain/GroupUser/Service/GroupUserServiceInterface' +import { GroupUserProjector } from '../Projection/GroupUserProjector' -@controller('/vaults/:vaultUuid/users') -export class VaultUsersController extends BaseHttpController { +@controller('/groups/:groupUuid/users') +export class GroupUsersController extends BaseHttpController { constructor( - @inject(TYPES.VaultUserService) private vaultUserService: VaultUserServiceInterface, - @inject(TYPES.VaultUserProjector) private vaultUserProjector: VaultUserProjector, + @inject(TYPES.GroupUserService) private groupUserService: GroupUserServiceInterface, + @inject(TYPES.GroupUserProjector) private groupUserProjector: GroupUserProjector, ) { super() } @httpGet('/', TYPES.AuthMiddleware) - public async getVaultUsers( + public async getGroupUsers( request: Request, response: Response, ): Promise { - const result = await this.vaultUserService.getVaultUsersForVault({ + const result = await this.groupUserService.getGroupUsersForGroup({ originatorUuid: response.locals.user.uuid, - vaultUuid: request.params.vaultUuid, + groupUuid: request.params.groupUuid, }) if (!result) { - return this.errorResponse(400, 'Could not get vault users') + return this.errorResponse(400, 'Could not get group users') } const { users, isAdmin } = result - const projected = users.map((vaultUser) => - this.vaultUserProjector.projectAsDisplayableUserForOtherVaultMembers(vaultUser, isAdmin), + const projected = users.map((groupUser) => + this.groupUserProjector.projectAsDisplayableUserForOtherGroupMembers(groupUser, isAdmin), ) return this.json({ users: projected }) } @httpDelete('/:userUuid', TYPES.AuthMiddleware) - public async deleteVaultUser( + public async deleteGroupUser( request: Request, response: Response, ): Promise { - const result = await this.vaultUserService.deleteVaultUser({ - vaultUuid: request.params.vaultUuid, + const result = await this.groupUserService.deleteGroupUser({ + groupUuid: request.params.groupUuid, userUuid: request.params.userUuid, originatorUuid: response.locals.user.uuid, }) diff --git a/packages/syncing-server/src/Controller/GroupsController.ts b/packages/syncing-server/src/Controller/GroupsController.ts new file mode 100644 index 000000000..3db066d48 --- /dev/null +++ b/packages/syncing-server/src/Controller/GroupsController.ts @@ -0,0 +1,150 @@ +import { GroupUser } from '../Domain/GroupUser/Model/GroupUser' +import { GroupServiceInterface } from '../Domain/Group/Service/GroupServiceInterface' +import { Request, Response } from 'express' +import { + BaseHttpController, + controller, + httpPost, + results, + httpDelete, + httpPatch, + httpGet, +} from 'inversify-express-utils' +import TYPES from '../Bootstrap/Types' +import { inject } from 'inversify' +import { ProjectorInterface } from '../Projection/ProjectorInterface' +import { Group } from '../Domain/Group/Model/Group' +import { GroupProjection } from '../Projection/GroupProjection' +import { GroupUserProjection } from '../Projection/GroupUserProjection' +import { CreateGroupFileValetToken } from '../Domain/UseCase/CreateGroupFileValetToken/CreateGroupFileValetToken' +import { RemovedGroupUserServiceInterface } from '../Domain/RemovedGroupUser/Service/RemovedGroupUserServiceInterface' + +@controller('/groups') +export class GroupsController extends BaseHttpController { + constructor( + @inject(TYPES.GroupService) private groupService: GroupServiceInterface, + @inject(TYPES.RemovedGroupUserService) private removedGroupUserService: RemovedGroupUserServiceInterface, + @inject(TYPES.GroupProjector) private groupProjector: ProjectorInterface, + @inject(TYPES.GroupUserProjector) private groupUserProjector: ProjectorInterface, + @inject(TYPES.CreateGroupFileReadValetToken) private createGroupFileReadValetToken: CreateGroupFileValetToken, + ) { + super() + } + + @httpGet('/', TYPES.AuthMiddleware) + public async getGroups(_request: Request, response: Response): Promise { + const groups = await this.groupService.getGroups({ + userUuid: response.locals.user.uuid, + }) + + return this.json({ + groups: groups.map((group) => this.groupProjector.projectFull(group)), + }) + } + + @httpPost('/:groupUuid/valet-tokens', TYPES.AuthMiddleware) + public async getValetTokenForGroupFile( + request: Request, + response: Response, + ): Promise { + const valetTokenResult = await this.createGroupFileReadValetToken.execute({ + userUuid: response.locals.user.uuid, + groupUuid: request.params.groupUuid, + fileUuid: request.body.file_uuid, + remoteIdentifier: request.body.remote_identifier, + operation: request.body.operation, + unencryptedFileSize: request.body.unencrypted_file_size, + moveOperationType: request.body.move_operation_type, + groupToGroupMoveTargetUuid: request.body.group_to_group_move_target_uuid, + }) + + if (valetTokenResult.success === false) { + return this.errorResponse(400, `Failed to create group valet token: ${valetTokenResult.reason}`) + } + + return this.json({ + valetToken: valetTokenResult.valetToken, + }) + } + + @httpPost('/', TYPES.AuthMiddleware) + public async createGroup(request: Request, response: Response): Promise { + const result = await this.groupService.createGroup({ + userUuid: response.locals.user.uuid, + groupUuid: request.body.group_uuid, + specifiedItemsKeyUuid: request.body.specified_items_key_uuid, + }) + + if (!result) { + return this.errorResponse(400, 'Could not create group') + } + + return this.json({ + group: this.groupProjector.projectFull(result.group), + groupUser: this.groupUserProjector.projectFull(result.groupUser), + }) + } + + @httpPatch('/:groupUuid', TYPES.AuthMiddleware) + public async updateGroup(request: Request, response: Response): Promise { + const result = await this.groupService.updateGroup({ + groupUuid: request.params.groupUuid, + originatorUuid: response.locals.user.uuid, + specifiedItemsKeyUuid: request.body.specified_items_key_uuid, + }) + + if (!result) { + return this.errorResponse(400, 'Could not update group') + } + + return this.json({ + group: this.groupProjector.projectFull(result), + }) + } + + @httpDelete('/:groupUuid', TYPES.AuthMiddleware) + public async deleteGroup(request: Request, response: Response): Promise { + const result = await this.groupService.deleteGroup({ + groupUuid: request.params.groupUuid, + originatorUuid: response.locals.user.uuid, + }) + + if (!result) { + return this.errorResponse(400, 'Could not delete group') + } + + return this.json({ success: true }) + } + + @httpGet('/removed', TYPES.AuthMiddleware) + public async getAllRemovedFromGroupsForCurrentUser( + _request: Request, + response: Response, + ): Promise { + const result = await this.removedGroupUserService.getAllRemovedGroupUsersForUser({ + userUuid: response.locals.user.uuid, + }) + + if (!result) { + return this.errorResponse(400, 'Could not get group users') + } + + return this.json({ + removedGroups: result.map((removedUser) => { + return { + groupUuid: removedUser.groupUuid, + removedAt: removedUser.createdAtTimestamp, + } + }), + }) + } + + private errorResponse(status: number, message?: string, tag?: string) { + return this.json( + { + error: { message, tag }, + }, + status, + ) + } +} diff --git a/packages/syncing-server/src/Controller/ItemsController.ts b/packages/syncing-server/src/Controller/ItemsController.ts index 6d2ea9dc3..cd76e659a 100644 --- a/packages/syncing-server/src/Controller/ItemsController.ts +++ b/packages/syncing-server/src/Controller/ItemsController.ts @@ -37,7 +37,7 @@ export class ItemsController extends BaseHttpController { computeIntegrityHash: request.body.compute_integrity === true, syncToken: request.body.sync_token, cursorToken: request.body.cursor_token, - vaultUuids: request.body.vault_uuids, + groupUuids: request.body.group_uuids, limit: request.body.limit, contentType: request.body.content_type, apiVersion: request.body.api ?? ApiVersion.v20161215, diff --git a/packages/syncing-server/src/Controller/VaultsController.ts b/packages/syncing-server/src/Controller/VaultsController.ts deleted file mode 100644 index 5770b493b..000000000 --- a/packages/syncing-server/src/Controller/VaultsController.ts +++ /dev/null @@ -1,150 +0,0 @@ -import { VaultUser } from '../Domain/VaultUser/Model/VaultUser' -import { VaultServiceInterface } from '../Domain/Vault/Service/VaultServiceInterface' -import { Request, Response } from 'express' -import { - BaseHttpController, - controller, - httpPost, - results, - httpDelete, - httpPatch, - httpGet, -} from 'inversify-express-utils' -import TYPES from '../Bootstrap/Types' -import { inject } from 'inversify' -import { ProjectorInterface } from '../Projection/ProjectorInterface' -import { Vault } from '../Domain/Vault/Model/Vault' -import { VaultProjection } from '../Projection/VaultProjection' -import { VaultUserProjection } from '../Projection/VaultUserProjection' -import { CreateVaultFileValetToken } from '../Domain/UseCase/CreateVaultFileValetToken/CreateVaultFileValetToken' -import { RemovedVaultUserServiceInterface } from '../Domain/RemovedVaultUser/Service/RemovedVaultUserServiceInterface' - -@controller('/vaults') -export class VaultsController extends BaseHttpController { - constructor( - @inject(TYPES.VaultService) private vaultService: VaultServiceInterface, - @inject(TYPES.RemovedVaultUserService) private removedVaultUserService: RemovedVaultUserServiceInterface, - @inject(TYPES.VaultProjector) private vaultProjector: ProjectorInterface, - @inject(TYPES.VaultUserProjector) private vaultUserProjector: ProjectorInterface, - @inject(TYPES.CreateVaultFileReadValetToken) private createVaultFileReadValetToken: CreateVaultFileValetToken, - ) { - super() - } - - @httpGet('/', TYPES.AuthMiddleware) - public async getVaults(_request: Request, response: Response): Promise { - const vaults = await this.vaultService.getVaults({ - userUuid: response.locals.user.uuid, - }) - - return this.json({ - vaults: vaults.map((vault) => this.vaultProjector.projectFull(vault)), - }) - } - - @httpPost('/:vaultUuid/valet-tokens', TYPES.AuthMiddleware) - public async getValetTokenForVaultFile( - request: Request, - response: Response, - ): Promise { - const valetTokenResult = await this.createVaultFileReadValetToken.execute({ - userUuid: response.locals.user.uuid, - vaultUuid: request.params.vaultUuid, - fileUuid: request.body.file_uuid, - remoteIdentifier: request.body.remote_identifier, - operation: request.body.operation, - unencryptedFileSize: request.body.unencrypted_file_size, - moveOperationType: request.body.move_operation_type, - vaultToVaultMoveTargetUuid: request.body.vault_to_vault_move_target_uuid, - }) - - if (valetTokenResult.success === false) { - return this.errorResponse(400, `Failed to create vault valet token: ${valetTokenResult.reason}`) - } - - return this.json({ - valetToken: valetTokenResult.valetToken, - }) - } - - @httpPost('/', TYPES.AuthMiddleware) - public async createVault(request: Request, response: Response): Promise { - const result = await this.vaultService.createVault({ - userUuid: response.locals.user.uuid, - vaultUuid: request.body.vault_uuid, - specifiedItemsKeyUuid: request.body.specified_items_key_uuid, - }) - - if (!result) { - return this.errorResponse(400, 'Could not create vault') - } - - return this.json({ - vault: this.vaultProjector.projectFull(result.vault), - vaultUser: this.vaultUserProjector.projectFull(result.vaultUser), - }) - } - - @httpPatch('/:vaultUuid', TYPES.AuthMiddleware) - public async updateVault(request: Request, response: Response): Promise { - const result = await this.vaultService.updateVault({ - vaultUuid: request.params.vaultUuid, - originatorUuid: response.locals.user.uuid, - specifiedItemsKeyUuid: request.body.specified_items_key_uuid, - }) - - if (!result) { - return this.errorResponse(400, 'Could not update vault') - } - - return this.json({ - vault: this.vaultProjector.projectFull(result), - }) - } - - @httpDelete('/:vaultUuid', TYPES.AuthMiddleware) - public async deleteVault(request: Request, response: Response): Promise { - const result = await this.vaultService.deleteVault({ - vaultUuid: request.params.vaultUuid, - originatorUuid: response.locals.user.uuid, - }) - - if (!result) { - return this.errorResponse(400, 'Could not delete vault') - } - - return this.json({ success: true }) - } - - @httpGet('/removed', TYPES.AuthMiddleware) - public async getAllRemovedFromVaultsForCurrentUser( - _request: Request, - response: Response, - ): Promise { - const result = await this.removedVaultUserService.getAllRemovedVaultUsersForUser({ - userUuid: response.locals.user.uuid, - }) - - if (!result) { - return this.errorResponse(400, 'Could not get vault users') - } - - return this.json({ - removedVaults: result.map((removedUser) => { - return { - vaultUuid: removedUser.vaultUuid, - removedAt: removedUser.createdAtTimestamp, - } - }), - }) - } - - private errorResponse(status: number, message?: string, tag?: string) { - return this.json( - { - error: { message, tag }, - }, - status, - ) - } -} diff --git a/packages/syncing-server/src/Domain/Group/Factory/GroupFactory.ts b/packages/syncing-server/src/Domain/Group/Factory/GroupFactory.ts new file mode 100644 index 000000000..fc16b04ea --- /dev/null +++ b/packages/syncing-server/src/Domain/Group/Factory/GroupFactory.ts @@ -0,0 +1,32 @@ +import { TimerInterface } from '@standardnotes/time' +import { GroupFactoryInterface } from './GroupFactoryInterface' +import { GroupHash } from './GroupHash' +import { Group } from '../Model/Group' + +export class GroupFactory implements GroupFactoryInterface { + constructor(private timer: TimerInterface) {} + + create(dto: { userUuid: string; groupHash: GroupHash }): Group { + const newGroup = new Group() + newGroup.uuid = dto.groupHash.uuid + newGroup.userUuid = dto.userUuid + newGroup.specifiedItemsKeyUuid = dto.groupHash.specified_items_key_uuid + newGroup.fileUploadBytesUsed = dto.groupHash.file_upload_bytes_used + newGroup.fileUploadBytesLimit = dto.groupHash.file_upload_bytes_limit + + const now = this.timer.getTimestampInMicroseconds() + newGroup.updatedAtTimestamp = now + newGroup.createdAtTimestamp = now + + if (dto.groupHash.created_at_timestamp) { + newGroup.createdAtTimestamp = dto.groupHash.created_at_timestamp + } + + return newGroup + } + + createStub(dto: { userUuid: string; groupHash: GroupHash }): Group { + const item = this.create(dto) + return item + } +} diff --git a/packages/syncing-server/src/Domain/Group/Factory/GroupFactoryInterface.ts b/packages/syncing-server/src/Domain/Group/Factory/GroupFactoryInterface.ts new file mode 100644 index 000000000..ae8852f53 --- /dev/null +++ b/packages/syncing-server/src/Domain/Group/Factory/GroupFactoryInterface.ts @@ -0,0 +1,7 @@ +import { Group } from '../Model/Group' +import { GroupHash } from './GroupHash' + +export interface GroupFactoryInterface { + create(dto: { userUuid: string; groupHash: GroupHash }): Group + createStub(dto: { userUuid: string; groupHash: GroupHash }): Group +} diff --git a/packages/syncing-server/src/Domain/Vault/Factory/VaultHash.ts b/packages/syncing-server/src/Domain/Group/Factory/GroupHash.ts similarity index 88% rename from packages/syncing-server/src/Domain/Vault/Factory/VaultHash.ts rename to packages/syncing-server/src/Domain/Group/Factory/GroupHash.ts index e1ffb8d42..865fe10f7 100644 --- a/packages/syncing-server/src/Domain/Vault/Factory/VaultHash.ts +++ b/packages/syncing-server/src/Domain/Group/Factory/GroupHash.ts @@ -1,4 +1,4 @@ -export type VaultHash = { +export type GroupHash = { uuid: string user_uuid: string specified_items_key_uuid: string diff --git a/packages/syncing-server/src/Domain/Vault/Model/Vault.ts b/packages/syncing-server/src/Domain/Group/Model/Group.ts similarity index 89% rename from packages/syncing-server/src/Domain/Vault/Model/Vault.ts rename to packages/syncing-server/src/Domain/Group/Model/Group.ts index c3d3e6e8c..c8db6de04 100644 --- a/packages/syncing-server/src/Domain/Vault/Model/Vault.ts +++ b/packages/syncing-server/src/Domain/Group/Model/Group.ts @@ -1,7 +1,7 @@ import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm' -@Entity({ name: 'vaults' }) -export class Vault { +@Entity({ name: 'groups' }) +export class Group { @PrimaryGeneratedColumn('uuid') declare uuid: string @@ -9,7 +9,7 @@ export class Vault { name: 'user_uuid', length: 36, }) - @Index('index_vaults_on_user_uuid') + @Index('index_groups_on_user_uuid') declare userUuid: string @Column({ diff --git a/packages/syncing-server/src/Domain/Group/Repository/GroupRepositoryInterface.ts b/packages/syncing-server/src/Domain/Group/Repository/GroupRepositoryInterface.ts new file mode 100644 index 000000000..46628533e --- /dev/null +++ b/packages/syncing-server/src/Domain/Group/Repository/GroupRepositoryInterface.ts @@ -0,0 +1,19 @@ +import { Group } from '../Model/Group' + +export type GroupQuery = { + userUuid: string +} + +export type UserGroupsQuery = { + userUuid?: string + groupUuids?: string[] + lastSyncTime?: number +} + +export interface GroupsRepositoryInterface { + findByUuid(uuid: string): Promise + create(group: Group): Promise + save(group: Group): Promise + remove(group: Group): Promise + findAll(query: UserGroupsQuery): Promise +} diff --git a/packages/syncing-server/src/Domain/Group/Repository/TypeORMGroupRepository.ts b/packages/syncing-server/src/Domain/Group/Repository/TypeORMGroupRepository.ts new file mode 100644 index 000000000..84d1ae5b4 --- /dev/null +++ b/packages/syncing-server/src/Domain/Group/Repository/TypeORMGroupRepository.ts @@ -0,0 +1,56 @@ +import { Repository, SelectQueryBuilder } from 'typeorm' +import { GroupsRepositoryInterface, UserGroupsQuery } from './GroupRepositoryInterface' +import { Group } from '../Model/Group' + +export class TypeORMGroupRepository implements GroupsRepositoryInterface { + constructor(private ormRepository: Repository) {} + + async create(group: Group): Promise { + return this.ormRepository.save(group) + } + + async save(group: Group): Promise { + return this.ormRepository.save(group) + } + + findByUuid(uuid: string): Promise { + return this.ormRepository + .createQueryBuilder('group') + .where('group.uuid = :uuid', { + uuid, + }) + .getOne() + } + + async remove(group: Group): Promise { + return this.ormRepository.remove(group) + } + + async findAll(query: UserGroupsQuery): Promise { + return this.createFindAllQueryBuilder(query).getMany() + } + + private createFindAllQueryBuilder(query: UserGroupsQuery): SelectQueryBuilder { + const queryBuilder = this.ormRepository.createQueryBuilder('group') + + if (!query.userUuid && !query.groupUuids) { + throw new Error('Either userUuid or groupUuids must be provided') + } + + if (query.userUuid) { + queryBuilder.where('group.user_uuid = :userUuid', { userUuid: query.userUuid }) + } + + if (query.groupUuids) { + queryBuilder.where('group.uuid IN (:...groupUuids)', { groupUuids: query.groupUuids }) + } + + if (query.lastSyncTime) { + queryBuilder.andWhere('group.updated_at_timestamp > :lastSyncTime', { + lastSyncTime: query.lastSyncTime, + }) + } + + return queryBuilder + } +} diff --git a/packages/syncing-server/src/Domain/Group/Service/GroupService.ts b/packages/syncing-server/src/Domain/Group/Service/GroupService.ts new file mode 100644 index 000000000..47bd7622a --- /dev/null +++ b/packages/syncing-server/src/Domain/Group/Service/GroupService.ts @@ -0,0 +1,101 @@ +import { TimerInterface } from '@standardnotes/time' +import { Group } from '../Model/Group' +import { GroupsRepositoryInterface } from '../Repository/GroupRepositoryInterface' +import { CreateGroupDTO, GroupServiceInterface, UpdateGroupDTO, CreateGroupResult } from './GroupServiceInterface' +import { GroupFactoryInterface } from '../Factory/GroupFactoryInterface' +import { GroupUserServiceInterface } from '../../GroupUser/Service/GroupUserServiceInterface' +import { GroupInviteServiceInterface } from '../../GroupInvite/Service/GroupInviteServiceInterface' + +export class GroupService implements GroupServiceInterface { + constructor( + private groupRepository: GroupsRepositoryInterface, + private groupFactory: GroupFactoryInterface, + private groupUserService: GroupUserServiceInterface, + private groupInviteService: GroupInviteServiceInterface, + private timer: TimerInterface, + ) {} + + async createGroup(dto: CreateGroupDTO): Promise { + const existingGroup = await this.groupRepository.findByUuid(dto.groupUuid) + if (existingGroup) { + return null + } + + const timestamp = this.timer.getTimestampInMicroseconds() + const group = this.groupFactory.create({ + userUuid: dto.userUuid, + groupHash: { + uuid: dto.groupUuid, + user_uuid: dto.userUuid, + specified_items_key_uuid: dto.specifiedItemsKeyUuid, + file_upload_bytes_limit: 1_000_000, + file_upload_bytes_used: 0, + created_at_timestamp: timestamp, + updated_at_timestamp: timestamp, + }, + }) + + const savedGroup = await this.groupRepository.create(group) + + const groupUser = await this.groupUserService.addGroupUser({ + groupUuid: savedGroup.uuid, + userUuid: dto.userUuid, + permissions: 'admin', + }) + + return { group, groupUser } + } + + async getGroup(dto: { groupUuid: string }): Promise { + const group = await this.groupRepository.findByUuid(dto.groupUuid) + + return group + } + + async getGroups(dto: { userUuid: string; lastSyncTime?: number }): Promise { + const groupUsers = await this.groupUserService.getAllGroupUsersForUser({ + userUuid: dto.userUuid, + }) + + const groupUuids = groupUsers.map((groupUser) => groupUser.groupUuid) + + if (groupUuids.length === 0) { + return [] + } + + return this.groupRepository.findAll({ groupUuids, lastSyncTime: dto.lastSyncTime }) + } + + async updateGroup(dto: UpdateGroupDTO): Promise { + const group = await this.groupRepository.findByUuid(dto.groupUuid) + if (!group || group.userUuid !== dto.originatorUuid) { + return null + } + + group.specifiedItemsKeyUuid = dto.specifiedItemsKeyUuid + group.updatedAtTimestamp = this.timer.getTimestampInMicroseconds() + + const savedGroup = await this.groupRepository.save(group) + + return savedGroup + } + + async deleteGroup(dto: { groupUuid: string; originatorUuid: string }): Promise { + const group = await this.groupRepository.findByUuid(dto.groupUuid) + if (!group || group.userUuid !== dto.originatorUuid) { + return false + } + + await this.groupRepository.remove(group) + await this.groupUserService.deleteAllGroupUsersForGroup({ + groupUuid: dto.groupUuid, + originatorUuid: dto.originatorUuid, + }) + await this.groupInviteService.deleteAllInvitesForGroup({ + groupUuid: dto.groupUuid, + originatorUuid: dto.originatorUuid, + }) + + return true + } +} diff --git a/packages/syncing-server/src/Domain/Group/Service/GroupServiceInterface.ts b/packages/syncing-server/src/Domain/Group/Service/GroupServiceInterface.ts new file mode 100644 index 000000000..884c017eb --- /dev/null +++ b/packages/syncing-server/src/Domain/Group/Service/GroupServiceInterface.ts @@ -0,0 +1,31 @@ +import { GroupUser } from './../../GroupUser/Model/GroupUser' +import { Group } from '../Model/Group' + +export type CreateGroupDTO = { + userUuid: string + groupUuid: string + specifiedItemsKeyUuid: string +} + +export type CreateGroupResult = { + group: Group + groupUser: GroupUser +} + +export type UpdateGroupDTO = { + originatorUuid: string + groupUuid: string + specifiedItemsKeyUuid: string +} + +export interface GroupServiceInterface { + createGroup(dto: CreateGroupDTO): Promise + + updateGroup(dto: UpdateGroupDTO): Promise + + deleteGroup(dto: { groupUuid: string; originatorUuid: string }): Promise + + getGroup(dto: { groupUuid: string }): Promise + + getGroups(dto: { userUuid: string; lastSyncTime?: number }): Promise +} diff --git a/packages/syncing-server/src/Domain/GroupInvite/Factory/GroupInviteFactory.ts b/packages/syncing-server/src/Domain/GroupInvite/Factory/GroupInviteFactory.ts new file mode 100644 index 000000000..625208681 --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupInvite/Factory/GroupInviteFactory.ts @@ -0,0 +1,35 @@ +import { TimerInterface } from '@standardnotes/time' +import { GroupInviteFactoryInterface } from './GroupInviteFactoryInterface' +import { GroupInviteHash } from './GroupInviteHash' +import { GroupInvite } from '../Model/GroupInvite' + +export class GroupInviteFactory implements GroupInviteFactoryInterface { + constructor(private timer: TimerInterface) {} + + create(dto: GroupInviteHash): GroupInvite { + const newGroupInvite = new GroupInvite() + newGroupInvite.uuid = dto.uuid + newGroupInvite.userUuid = dto.user_uuid + newGroupInvite.groupUuid = dto.group_uuid + newGroupInvite.inviterUuid = dto.inviter_uuid + newGroupInvite.inviterPublicKey = dto.inviter_public_key + newGroupInvite.encryptedGroupData = dto.encrypted_group_data + newGroupInvite.inviteType = dto.invite_type + newGroupInvite.permissions = dto.permissions + + const now = this.timer.getTimestampInMicroseconds() + newGroupInvite.updatedAtTimestamp = now + newGroupInvite.createdAtTimestamp = now + + if (dto.created_at_timestamp) { + newGroupInvite.createdAtTimestamp = dto.created_at_timestamp + } + + return newGroupInvite + } + + createStub(dto: GroupInviteHash): GroupInvite { + const item = this.create(dto) + return item + } +} diff --git a/packages/syncing-server/src/Domain/GroupInvite/Factory/GroupInviteFactoryInterface.ts b/packages/syncing-server/src/Domain/GroupInvite/Factory/GroupInviteFactoryInterface.ts new file mode 100644 index 000000000..cc20a6929 --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupInvite/Factory/GroupInviteFactoryInterface.ts @@ -0,0 +1,7 @@ +import { GroupInvite } from '../Model/GroupInvite' +import { GroupInviteHash } from './GroupInviteHash' + +export interface GroupInviteFactoryInterface { + create(dto: GroupInviteHash): GroupInvite + createStub(dto: GroupInviteHash): GroupInvite +} diff --git a/packages/syncing-server/src/Domain/GroupInvite/Factory/GroupInviteHash.ts b/packages/syncing-server/src/Domain/GroupInvite/Factory/GroupInviteHash.ts new file mode 100644 index 000000000..ad0c381e2 --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupInvite/Factory/GroupInviteHash.ts @@ -0,0 +1,15 @@ +import { GroupUserPermission } from '../../GroupUser/Model/GroupUserPermission' +import { GroupInviteType } from '../Model/GroupInviteType' + +export type GroupInviteHash = { + uuid: string + user_uuid: string + group_uuid: string + inviter_uuid: string + inviter_public_key: string + encrypted_group_data: string + invite_type: GroupInviteType + permissions: GroupUserPermission + created_at_timestamp?: number + updated_at_timestamp?: number +} diff --git a/packages/syncing-server/src/Domain/VaultInvite/Model/VaultInvite.ts b/packages/syncing-server/src/Domain/GroupInvite/Model/GroupInvite.ts similarity index 65% rename from packages/syncing-server/src/Domain/VaultInvite/Model/VaultInvite.ts rename to packages/syncing-server/src/Domain/GroupInvite/Model/GroupInvite.ts index 005ed7729..f10262bb4 100644 --- a/packages/syncing-server/src/Domain/VaultInvite/Model/VaultInvite.ts +++ b/packages/syncing-server/src/Domain/GroupInvite/Model/GroupInvite.ts @@ -1,17 +1,17 @@ import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm' -import { VaultInviteType } from './VaultInviteType' -import { VaultUserPermission } from '../../VaultUser/Model/VaultUserPermission' +import { GroupInviteType } from './GroupInviteType' +import { GroupUserPermission } from '../../GroupUser/Model/GroupUserPermission' -@Entity({ name: 'vault_invites' }) -export class VaultInvite { +@Entity({ name: 'group_invites' }) +export class GroupInvite { @PrimaryGeneratedColumn('uuid') declare uuid: string @Column({ - name: 'vault_uuid', + name: 'group_uuid', length: 36, }) - declare vaultUuid: string + declare groupUuid: string @Column({ name: 'user_uuid', @@ -27,7 +27,7 @@ export class VaultInvite { @Column({ name: 'invite_type', }) - declare inviteType: VaultInviteType + declare inviteType: GroupInviteType @Column({ name: 'inviter_public_key', @@ -35,14 +35,14 @@ export class VaultInvite { declare inviterPublicKey: string @Column({ - name: 'encrypted_vault_data', + name: 'encrypted_group_data', }) - declare encryptedVaultData: string + declare encryptedGroupData: string @Column({ name: 'permissions', }) - declare permissions: VaultUserPermission + declare permissions: GroupUserPermission @Column({ name: 'created_at_timestamp', diff --git a/packages/syncing-server/src/Domain/VaultInvite/Model/VaultInviteType.ts b/packages/syncing-server/src/Domain/GroupInvite/Model/GroupInviteType.ts similarity index 61% rename from packages/syncing-server/src/Domain/VaultInvite/Model/VaultInviteType.ts rename to packages/syncing-server/src/Domain/GroupInvite/Model/GroupInviteType.ts index 23d756c89..17924faa6 100644 --- a/packages/syncing-server/src/Domain/VaultInvite/Model/VaultInviteType.ts +++ b/packages/syncing-server/src/Domain/GroupInvite/Model/GroupInviteType.ts @@ -1,4 +1,4 @@ -export enum VaultInviteType { +export enum GroupInviteType { Join = 'join', KeyChange = 'key-change', } diff --git a/packages/syncing-server/src/Domain/GroupInvite/Repository/GroupInviteRepositoryInterface.ts b/packages/syncing-server/src/Domain/GroupInvite/Repository/GroupInviteRepositoryInterface.ts new file mode 100644 index 000000000..f27ee5bfd --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupInvite/Repository/GroupInviteRepositoryInterface.ts @@ -0,0 +1,25 @@ +import { GroupInvite } from '../Model/GroupInvite' + +export type GroupInviteQuery = { + userUuid: string +} + +export type GroupInviteFindAllForUserQuery = { + userUuid?: string + lastSyncTime?: number + inviterUuid?: string +} + +export type GroupInviteFindAllForGroup = { + groupUuid: string +} + +export interface GroupInviteRepositoryInterface { + findByUuid(groupInviteUuid: string): Promise + create(group: GroupInvite): Promise + save(groupInvite: GroupInvite): Promise + remove(group: GroupInvite): Promise + findAll(query: GroupInviteFindAllForUserQuery): Promise + findAllForGroup(query: GroupInviteFindAllForGroup): Promise + findByUserUuidAndGroupUuid(userUuid: string, groupUuid: string): Promise +} diff --git a/packages/syncing-server/src/Domain/GroupInvite/Repository/TypeORMGroupInviteRepository.ts b/packages/syncing-server/src/Domain/GroupInvite/Repository/TypeORMGroupInviteRepository.ts new file mode 100644 index 000000000..b95d62a5b --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupInvite/Repository/TypeORMGroupInviteRepository.ts @@ -0,0 +1,71 @@ +import { GroupInvite } from '../Model/GroupInvite' +import { Repository, SelectQueryBuilder } from 'typeorm' +import { + GroupInviteFindAllForGroup, + GroupInviteFindAllForUserQuery, + GroupInviteRepositoryInterface, +} from './GroupInviteRepositoryInterface' + +export class TypeORMGroupInviteRepository implements GroupInviteRepositoryInterface { + constructor(private ormRepository: Repository) {} + + async create(groupInvite: GroupInvite): Promise { + return this.ormRepository.save(groupInvite) + } + + async save(groupInvite: GroupInvite): Promise { + return this.ormRepository.save(groupInvite) + } + + findByUuid(uuid: string): Promise { + return this.ormRepository + .createQueryBuilder('group_invite') + .where('group_invite.uuid = :uuid', { + uuid, + }) + .getOne() + } + + findByUserUuidAndGroupUuid(userUuid: string, groupUuid: string): Promise { + return this.ormRepository + .createQueryBuilder('group_invite') + .where('group_invite.user_uuid = :userUuid', { userUuid }) + .andWhere('group_invite.group_uuid = :groupUuid', { groupUuid }) + .getOne() + } + + async remove(groupInvite: GroupInvite): Promise { + return this.ormRepository.remove(groupInvite) + } + + findAllForGroup(query: GroupInviteFindAllForGroup): Promise { + return this.ormRepository + .createQueryBuilder('group_invite') + .where('group_invite.group_uuid = :groupUuid', { groupUuid: query.groupUuid }) + .getMany() + } + + async findAll(query: GroupInviteFindAllForUserQuery): Promise { + return this.createFindAllQueryBuilder(query).getMany() + } + + private createFindAllQueryBuilder(query: GroupInviteFindAllForUserQuery): SelectQueryBuilder { + const queryBuilder = this.ormRepository.createQueryBuilder('group_invite') + + if (query.userUuid) { + queryBuilder.where('group_invite.user_uuid = :userUuid', { userUuid: query.userUuid }) + } else if (query.inviterUuid) { + queryBuilder.where('group_invite.inviter_uuid = :inviterUuid', { + inviterUuid: query.inviterUuid, + }) + } + + if (query.lastSyncTime) { + queryBuilder.andWhere('group_invite.updated_at_timestamp > :lastSyncTime', { + lastSyncTime: query.lastSyncTime, + }) + } + + return queryBuilder + } +} diff --git a/packages/syncing-server/src/Domain/GroupInvite/Service/CreateInviteDTO.ts b/packages/syncing-server/src/Domain/GroupInvite/Service/CreateInviteDTO.ts new file mode 100644 index 000000000..6aada1a34 --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupInvite/Service/CreateInviteDTO.ts @@ -0,0 +1,12 @@ +import { GroupInviteType } from '../Model/GroupInviteType' +import { GroupUserPermission } from '../../GroupUser/Model/GroupUserPermission' + +export type CreateInviteDTO = { + originatorUuid: string + groupUuid: string + userUuid: string + inviterPublicKey: string + encryptedGroupData: string + inviteType: GroupInviteType + permissions: GroupUserPermission +} diff --git a/packages/syncing-server/src/Domain/VaultInvite/Service/GetUserVaultInvitesDTO.ts b/packages/syncing-server/src/Domain/GroupInvite/Service/GetUserGroupInvitesDTO.ts similarity index 51% rename from packages/syncing-server/src/Domain/VaultInvite/Service/GetUserVaultInvitesDTO.ts rename to packages/syncing-server/src/Domain/GroupInvite/Service/GetUserGroupInvitesDTO.ts index b785d0706..2b6223381 100644 --- a/packages/syncing-server/src/Domain/VaultInvite/Service/GetUserVaultInvitesDTO.ts +++ b/packages/syncing-server/src/Domain/GroupInvite/Service/GetUserGroupInvitesDTO.ts @@ -1,4 +1,4 @@ -export interface GetUserVaultInvitesDTO { +export interface GetUserGroupInvitesDTO { userUuid: string lastSyncTime?: number } diff --git a/packages/syncing-server/src/Domain/GroupInvite/Service/GroupInviteService.ts b/packages/syncing-server/src/Domain/GroupInvite/Service/GroupInviteService.ts new file mode 100644 index 000000000..4c7c58e5e --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupInvite/Service/GroupInviteService.ts @@ -0,0 +1,172 @@ +import { TimerInterface } from '@standardnotes/time' +import { GroupInvite } from '../Model/GroupInvite' +import { GroupInviteFactoryInterface } from '../Factory/GroupInviteFactoryInterface' +import { v4 as uuidv4 } from 'uuid' +import { GroupInviteRepositoryInterface } from '../Repository/GroupInviteRepositoryInterface' +import { GroupInviteServiceInterface } from './GroupInviteServiceInterface' +import { GetUserGroupInvitesDTO } from './GetUserGroupInvitesDTO' +import { GroupsRepositoryInterface } from '../../Group/Repository/GroupRepositoryInterface' +import { GroupUserServiceInterface } from '../../GroupUser/Service/GroupUserServiceInterface' +import { GroupInviteType } from '../Model/GroupInviteType' +import { CreateInviteDTO } from './CreateInviteDTO' +import { UpdateInviteDTO } from './UpdateInviteDTO' + +export class GroupInviteService implements GroupInviteServiceInterface { + constructor( + private groupRepository: GroupsRepositoryInterface, + private groupInviteRepository: GroupInviteRepositoryInterface, + private groupInviteFactory: GroupInviteFactoryInterface, + private groupUserService: GroupUserServiceInterface, + private timer: TimerInterface, + ) {} + + async createInvite(dto: CreateInviteDTO): Promise { + const group = await this.groupRepository.findByUuid(dto.groupUuid) + if (!group || group.userUuid !== dto.originatorUuid) { + return null + } + + const timestamp = this.timer.getTimestampInMicroseconds() + + const groupInvite = this.groupInviteFactory.create({ + uuid: uuidv4(), + user_uuid: dto.userUuid, + group_uuid: dto.groupUuid, + inviter_uuid: dto.originatorUuid, + inviter_public_key: dto.inviterPublicKey, + encrypted_group_data: dto.encryptedGroupData, + invite_type: dto.inviteType, + permissions: dto.permissions, + created_at_timestamp: timestamp, + updated_at_timestamp: timestamp, + }) + + return this.groupInviteRepository.create(groupInvite) + } + + async updateInvite(dto: UpdateInviteDTO): Promise { + const groupInvite = await this.groupInviteRepository.findByUuid(dto.inviteUuid) + if (!groupInvite || groupInvite.inviterUuid !== dto.originatorUuid) { + return null + } + + groupInvite.inviterPublicKey = dto.inviterPublicKey + groupInvite.encryptedGroupData = dto.encryptedGroupData + if (dto.permissions) { + groupInvite.permissions = dto.permissions + } + groupInvite.updatedAtTimestamp = this.timer.getTimestampInMicroseconds() + + return this.groupInviteRepository.save(groupInvite) + } + + getInvitesForUser(dto: GetUserGroupInvitesDTO): Promise { + return this.groupInviteRepository.findAll(dto) + } + + getOutboundInvitesForUser(dto: { userUuid: string }): Promise { + return this.groupInviteRepository.findAll({ + inviterUuid: dto.userUuid, + }) + } + + async deleteAllInboundInvites(dto: { userUuid: string }): Promise { + const inboundInvites = await this.groupInviteRepository.findAll({ + userUuid: dto.userUuid, + }) + + for (const invite of inboundInvites) { + await this.groupInviteRepository.remove(invite) + } + } + + async getInvitesForGroup(dto: { groupUuid: string; originatorUuid: string }): Promise { + const group = await this.groupRepository.findByUuid(dto.groupUuid) + if (!group) { + return undefined + } + + const isUserGroupAdmin = group && group.userUuid === dto.originatorUuid + if (!isUserGroupAdmin) { + return undefined + } + + const users = await this.groupInviteRepository.findAllForGroup({ groupUuid: dto.groupUuid }) + + return users + } + + async acceptInvite(dto: { originatorUuid: string; inviteUuid: string }): Promise { + const invite = await this.groupInviteRepository.findByUuid(dto.inviteUuid) + if (!invite) { + return false + } + + const isAuthorized = invite.userUuid === dto.originatorUuid + if (!isAuthorized) { + return false + } + + if (invite.inviteType === GroupInviteType.Join) { + const addedUser = await this.groupUserService.addGroupUser({ + userUuid: dto.originatorUuid, + groupUuid: invite.groupUuid, + permissions: invite.permissions, + }) + + if (!addedUser) { + return false + } + } + + await this.groupInviteRepository.remove(invite) + + return true + } + + async declineInvite(dto: { originatorUuid: string; inviteUuid: string }): Promise { + const invite = await this.groupInviteRepository.findByUuid(dto.inviteUuid) + if (!invite) { + return false + } + + const isAuthorized = invite.userUuid === dto.originatorUuid + if (!isAuthorized) { + return false + } + + await this.groupInviteRepository.remove(invite) + + return true + } + + async deleteInvite(dto: { originatorUuid: string; inviteUuid: string }): Promise { + const invite = await this.groupInviteRepository.findByUuid(dto.inviteUuid) + if (!invite) { + return false + } + + const isAuthorized = invite.inviterUuid === dto.originatorUuid || invite.userUuid === dto.originatorUuid + if (!isAuthorized) { + return false + } + + await this.groupInviteRepository.remove(invite) + + return true + } + + async deleteAllInvitesForGroup(dto: { originatorUuid: string; groupUuid: string }): Promise { + const group = await this.groupRepository.findByUuid(dto.groupUuid) + if (!group || group.userUuid !== dto.originatorUuid) { + return false + } + + const groupInvites = await this.groupInviteRepository.findAllForGroup({ groupUuid: dto.groupUuid }) + for (const groupInvite of groupInvites) { + await this.groupInviteRepository.remove(groupInvite) + } + + return true + } +} diff --git a/packages/syncing-server/src/Domain/GroupInvite/Service/GroupInviteServiceInterface.ts b/packages/syncing-server/src/Domain/GroupInvite/Service/GroupInviteServiceInterface.ts new file mode 100644 index 000000000..1dc4fc6c6 --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupInvite/Service/GroupInviteServiceInterface.ts @@ -0,0 +1,26 @@ +import { GroupInvite } from '../Model/GroupInvite' +import { CreateInviteDTO } from './CreateInviteDTO' +import { GetUserGroupInvitesDTO } from './GetUserGroupInvitesDTO' +import { UpdateInviteDTO } from './UpdateInviteDTO' + +export interface GroupInviteServiceInterface { + createInvite(dto: CreateInviteDTO): Promise + + updateInvite(dto: UpdateInviteDTO): Promise + + getInvitesForUser(dto: GetUserGroupInvitesDTO): Promise + + getInvitesForGroup(dto: { groupUuid: string; originatorUuid: string }): Promise + + getOutboundInvitesForUser(dto: { userUuid: string }): Promise + + acceptInvite(dto: { originatorUuid: string; inviteUuid: string }): Promise + + declineInvite(dto: { originatorUuid: string; inviteUuid: string }): Promise + + deleteInvite(dto: { originatorUuid: string; groupUuid: string; inviteUuid: string }): Promise + + deleteAllInvitesForGroup(dto: { originatorUuid: string; groupUuid: string }): Promise + + deleteAllInboundInvites(dto: { userUuid: string }): Promise +} diff --git a/packages/syncing-server/src/Domain/GroupInvite/Service/UpdateInviteDTO.ts b/packages/syncing-server/src/Domain/GroupInvite/Service/UpdateInviteDTO.ts new file mode 100644 index 000000000..7b723ff3e --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupInvite/Service/UpdateInviteDTO.ts @@ -0,0 +1,9 @@ +import { GroupUserPermission } from '../../GroupUser/Model/GroupUserPermission' + +export type UpdateInviteDTO = { + originatorUuid: string + inviteUuid: string + inviterPublicKey: string + encryptedGroupData: string + permissions?: GroupUserPermission +} diff --git a/packages/syncing-server/src/Domain/GroupUser/Factory/GroupUserFactory.ts b/packages/syncing-server/src/Domain/GroupUser/Factory/GroupUserFactory.ts new file mode 100644 index 000000000..bbe177d50 --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupUser/Factory/GroupUserFactory.ts @@ -0,0 +1,31 @@ +import { TimerInterface } from '@standardnotes/time' +import { GroupUserFactoryInterface } from './GroupUserFactoryInterface' +import { GroupUserHash } from './GroupUserHash' +import { GroupUser } from '../Model/GroupUser' + +export class GroupUserFactory implements GroupUserFactoryInterface { + constructor(private timer: TimerInterface) {} + + create(dto: GroupUserHash): GroupUser { + const newGroupUser = new GroupUser() + newGroupUser.uuid = dto.uuid + newGroupUser.userUuid = dto.user_uuid + newGroupUser.groupUuid = dto.group_uuid + newGroupUser.permissions = dto.permissions + + const now = this.timer.getTimestampInMicroseconds() + newGroupUser.updatedAtTimestamp = now + newGroupUser.createdAtTimestamp = now + + if (dto.created_at_timestamp) { + newGroupUser.createdAtTimestamp = dto.created_at_timestamp + } + + return newGroupUser + } + + createStub(dto: GroupUserHash): GroupUser { + const item = this.create(dto) + return item + } +} diff --git a/packages/syncing-server/src/Domain/GroupUser/Factory/GroupUserFactoryInterface.ts b/packages/syncing-server/src/Domain/GroupUser/Factory/GroupUserFactoryInterface.ts new file mode 100644 index 000000000..b6fd68005 --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupUser/Factory/GroupUserFactoryInterface.ts @@ -0,0 +1,7 @@ +import { GroupUser } from '../Model/GroupUser' +import { GroupUserHash } from './GroupUserHash' + +export interface GroupUserFactoryInterface { + create(dto: GroupUserHash): GroupUser + createStub(dto: GroupUserHash): GroupUser +} diff --git a/packages/syncing-server/src/Domain/GroupUser/Factory/GroupUserHash.ts b/packages/syncing-server/src/Domain/GroupUser/Factory/GroupUserHash.ts new file mode 100644 index 000000000..d56bd172c --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupUser/Factory/GroupUserHash.ts @@ -0,0 +1,10 @@ +import { GroupUserPermission } from '../Model/GroupUserPermission' + +export type GroupUserHash = { + uuid: string + user_uuid: string + group_uuid: string + permissions: GroupUserPermission + created_at_timestamp?: number + updated_at_timestamp?: number +} diff --git a/packages/syncing-server/src/Domain/VaultUser/Model/VaultUser.ts b/packages/syncing-server/src/Domain/GroupUser/Model/GroupUser.ts similarity index 70% rename from packages/syncing-server/src/Domain/VaultUser/Model/VaultUser.ts rename to packages/syncing-server/src/Domain/GroupUser/Model/GroupUser.ts index 17f894da8..9b384c059 100644 --- a/packages/syncing-server/src/Domain/VaultUser/Model/VaultUser.ts +++ b/packages/syncing-server/src/Domain/GroupUser/Model/GroupUser.ts @@ -1,16 +1,16 @@ import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm' -import { VaultUserPermission } from './VaultUserPermission' +import { GroupUserPermission } from './GroupUserPermission' -@Entity({ name: 'vault_users' }) -export class VaultUser { +@Entity({ name: 'group_users' }) +export class GroupUser { @PrimaryGeneratedColumn('uuid') declare uuid: string @Column({ - name: 'vault_uuid', + name: 'group_uuid', length: 36, }) - declare vaultUuid: string + declare groupUuid: string @Column({ name: 'user_uuid', @@ -21,7 +21,7 @@ export class VaultUser { @Column({ name: 'permissions', }) - declare permissions: VaultUserPermission + declare permissions: GroupUserPermission @Column({ name: 'created_at_timestamp', diff --git a/packages/syncing-server/src/Domain/GroupUser/Model/GroupUserPermission.ts b/packages/syncing-server/src/Domain/GroupUser/Model/GroupUserPermission.ts new file mode 100644 index 000000000..1ecd2a35a --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupUser/Model/GroupUserPermission.ts @@ -0,0 +1 @@ +export type GroupUserPermission = 'read' | 'write' | 'admin' diff --git a/packages/syncing-server/src/Domain/GroupUser/Repository/GroupUserRepositoryInterface.ts b/packages/syncing-server/src/Domain/GroupUser/Repository/GroupUserRepositoryInterface.ts new file mode 100644 index 000000000..f6c556750 --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupUser/Repository/GroupUserRepositoryInterface.ts @@ -0,0 +1,20 @@ +import { GroupUser } from '../Model/GroupUser' + +export type GroupUserFindAllForUserQuery = { + userUuid: string + lastSyncTime?: number +} + +export type GroupUserFindAllForGroup = { + groupUuid: string +} + +export interface GroupUserRepositoryInterface { + findByUuid(groupUserUuid: string): Promise + create(group: GroupUser): Promise + save(groupUser: GroupUser): Promise + remove(group: GroupUser): Promise + findAllForUser(query: GroupUserFindAllForUserQuery): Promise + findAllForGroup(query: GroupUserFindAllForGroup): Promise + findByUserUuidAndGroupUuid(userUuid: string, groupUuid: string): Promise +} diff --git a/packages/syncing-server/src/Domain/GroupUser/Repository/TypeORMGroupUserRepository.ts b/packages/syncing-server/src/Domain/GroupUser/Repository/TypeORMGroupUserRepository.ts new file mode 100644 index 000000000..a3aa2ff5b --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupUser/Repository/TypeORMGroupUserRepository.ts @@ -0,0 +1,65 @@ +import { GroupUser } from '../Model/GroupUser' +import { Repository, SelectQueryBuilder } from 'typeorm' +import { + GroupUserFindAllForGroup, + GroupUserFindAllForUserQuery, + GroupUserRepositoryInterface, +} from './GroupUserRepositoryInterface' + +export class TypeORMGroupUserRepository implements GroupUserRepositoryInterface { + constructor(private ormRepository: Repository) {} + + async create(groupUser: GroupUser): Promise { + return this.ormRepository.save(groupUser) + } + + async save(groupUser: GroupUser): Promise { + return this.ormRepository.save(groupUser) + } + + findByUuid(uuid: string): Promise { + return this.ormRepository + .createQueryBuilder('group_user') + .where('group_user.uuid = :uuid', { + uuid, + }) + .getOne() + } + + findByUserUuidAndGroupUuid(userUuid: string, groupUuid: string): Promise { + return this.ormRepository + .createQueryBuilder('group_user') + .where('group_user.user_uuid = :userUuid', { userUuid }) + .andWhere('group_user.group_uuid = :groupUuid', { groupUuid }) + .getOne() + } + + async remove(groupUser: GroupUser): Promise { + return this.ormRepository.remove(groupUser) + } + + findAllForGroup(query: GroupUserFindAllForGroup): Promise { + return this.ormRepository + .createQueryBuilder('group_user') + .where('group_user.group_uuid = :groupUuid', { groupUuid: query.groupUuid }) + .getMany() + } + + async findAllForUser(query: GroupUserFindAllForUserQuery): Promise { + return this.createFindAllQueryBuilder(query).getMany() + } + + private createFindAllQueryBuilder(query: GroupUserFindAllForUserQuery): SelectQueryBuilder { + const queryBuilder = this.ormRepository.createQueryBuilder('group_user') + + queryBuilder.where('group_user.user_uuid = :userUuid', { userUuid: query.userUuid }) + + if (query.lastSyncTime) { + queryBuilder.andWhere('group_user.updated_at_timestamp > :lastSyncTime', { + lastSyncTime: query.lastSyncTime, + }) + } + + return queryBuilder + } +} diff --git a/packages/syncing-server/src/Domain/VaultUser/Service/GetVaultUsersDTO.ts b/packages/syncing-server/src/Domain/GroupUser/Service/GetGroupUsersDTO.ts similarity index 55% rename from packages/syncing-server/src/Domain/VaultUser/Service/GetVaultUsersDTO.ts rename to packages/syncing-server/src/Domain/GroupUser/Service/GetGroupUsersDTO.ts index 078b4f494..59462c7c0 100644 --- a/packages/syncing-server/src/Domain/VaultUser/Service/GetVaultUsersDTO.ts +++ b/packages/syncing-server/src/Domain/GroupUser/Service/GetGroupUsersDTO.ts @@ -1,4 +1,4 @@ -export interface GetVaultUsersDTO { +export interface GetGroupUsersDTO { userUuid: string lastSyncTime?: number } diff --git a/packages/syncing-server/src/Domain/GroupUser/Service/GroupUserService.ts b/packages/syncing-server/src/Domain/GroupUser/Service/GroupUserService.ts new file mode 100644 index 000000000..1ed49437f --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupUser/Service/GroupUserService.ts @@ -0,0 +1,132 @@ +import { RemovedGroupUserServiceInterface } from './../../RemovedGroupUser/Service/RemovedGroupUserServiceInterface' +import { TimerInterface } from '@standardnotes/time' +import { GroupUser } from '../Model/GroupUser' +import { GroupUserFactoryInterface } from '../Factory/GroupUserFactoryInterface' +import { v4 as uuidv4 } from 'uuid' +import { GroupUserRepositoryInterface } from '../Repository/GroupUserRepositoryInterface' +import { GroupUserServiceInterface } from './GroupUserServiceInterface' +import { GetGroupUsersDTO } from './GetGroupUsersDTO' +import { GroupsRepositoryInterface } from '../../Group/Repository/GroupRepositoryInterface' +import { GroupUserPermission } from '../Model/GroupUserPermission' + +export class GroupUserService implements GroupUserServiceInterface { + constructor( + private groupRepository: GroupsRepositoryInterface, + private groupUserRepository: GroupUserRepositoryInterface, + private groupUserFactory: GroupUserFactoryInterface, + private removedGroupUserService: RemovedGroupUserServiceInterface, + private timer: TimerInterface, + ) {} + + async addGroupUser(dto: { + groupUuid: string + userUuid: string + permissions: GroupUserPermission + }): Promise { + const group = await this.groupRepository.findByUuid(dto.groupUuid) + if (!group) { + throw new Error('Attempting to add a group user to a non-existent group') + } + + await this.removedGroupUserService.deleteRemovedGroupUser({ + groupUuid: dto.groupUuid, + userUuid: dto.userUuid, + }) + + const timestamp = this.timer.getTimestampInMicroseconds() + const groupUser = this.groupUserFactory.create({ + uuid: uuidv4(), + user_uuid: dto.userUuid, + group_uuid: dto.groupUuid, + permissions: dto.permissions, + created_at_timestamp: timestamp, + updated_at_timestamp: timestamp, + }) + + return this.groupUserRepository.create(groupUser) + } + + getAllGroupUsersForUser(dto: GetGroupUsersDTO): Promise { + return this.groupUserRepository.findAllForUser(dto) + } + + getUserForGroup(dto: { userUuid: string; groupUuid: string }): Promise { + return this.groupUserRepository.findByUserUuidAndGroupUuid(dto.userUuid, dto.groupUuid) + } + + async getGroupUsersForGroup(dto: { + groupUuid: string + originatorUuid: string + }): Promise<{ users: GroupUser[]; isAdmin: boolean } | undefined> { + const group = await this.groupRepository.findByUuid(dto.groupUuid) + if (!group) { + return undefined + } + + const isUserGroupAdmin = group && group.userUuid === dto.originatorUuid + const doesUserBelongToGroup = await this.groupUserRepository.findByUserUuidAndGroupUuid( + dto.originatorUuid, + dto.groupUuid, + ) + + if (!isUserGroupAdmin && !doesUserBelongToGroup) { + return undefined + } + + const users = await this.groupUserRepository.findAllForGroup({ groupUuid: dto.groupUuid }) + + return { + users, + isAdmin: isUserGroupAdmin, + } + } + + async deleteGroupUser(dto: { originatorUuid: string; groupUuid: string; userUuid: string }): Promise { + const group = await this.groupRepository.findByUuid(dto.groupUuid) + if (!group) { + return false + } + + if (group.userUuid === dto.userUuid) { + return false + } + + const isAuthorized = group.userUuid === dto.originatorUuid || dto.userUuid === dto.originatorUuid + if (!isAuthorized) { + return false + } + + const groupUser = await this.groupUserRepository.findByUserUuidAndGroupUuid(dto.userUuid, dto.groupUuid) + if (!groupUser) { + return false + } + + await this.groupUserRepository.remove(groupUser) + await this.removedGroupUserService.addRemovedGroupUser({ + groupUuid: dto.groupUuid, + userUuid: dto.userUuid, + removedBy: dto.originatorUuid, + }) + + return true + } + + async deleteAllGroupUsersForGroup(dto: { originatorUuid: string; groupUuid: string }): Promise { + const group = await this.groupRepository.findByUuid(dto.groupUuid) + if (!group || group.userUuid !== dto.originatorUuid) { + return false + } + + const groupUsers = await this.groupUserRepository.findAllForGroup({ groupUuid: dto.groupUuid }) + for (const groupUser of groupUsers) { + await this.groupUserRepository.remove(groupUser) + await this.removedGroupUserService.addRemovedGroupUser({ + groupUuid: dto.groupUuid, + userUuid: groupUser.userUuid, + removedBy: dto.originatorUuid, + }) + } + + return true + } +} diff --git a/packages/syncing-server/src/Domain/GroupUser/Service/GroupUserServiceInterface.ts b/packages/syncing-server/src/Domain/GroupUser/Service/GroupUserServiceInterface.ts new file mode 100644 index 000000000..623d47056 --- /dev/null +++ b/packages/syncing-server/src/Domain/GroupUser/Service/GroupUserServiceInterface.ts @@ -0,0 +1,20 @@ +import { GroupUser } from '../Model/GroupUser' +import { GroupUserPermission } from '../Model/GroupUserPermission' +import { GetGroupUsersDTO } from './GetGroupUsersDTO' + +export interface GroupUserServiceInterface { + addGroupUser(dto: { groupUuid: string; userUuid: string; permissions: GroupUserPermission }): Promise + + getAllGroupUsersForUser(dto: GetGroupUsersDTO): Promise + + getUserForGroup(dto: { userUuid: string; groupUuid: string }): Promise + + getGroupUsersForGroup(dto: { + groupUuid: string + originatorUuid: string + }): Promise<{ users: GroupUser[]; isAdmin: boolean } | undefined> + + deleteGroupUser(dto: { originatorUuid: string; groupUuid: string; userUuid: string }): Promise + + deleteAllGroupUsersForGroup(dto: { originatorUuid: string; groupUuid: string }): Promise +} diff --git a/packages/syncing-server/src/Domain/Handler/GroupFileRemovedEventHandler.ts b/packages/syncing-server/src/Domain/Handler/GroupFileRemovedEventHandler.ts new file mode 100644 index 000000000..7832ea695 --- /dev/null +++ b/packages/syncing-server/src/Domain/Handler/GroupFileRemovedEventHandler.ts @@ -0,0 +1,33 @@ +import { DomainEventHandlerInterface, GroupFileRemovedEvent } from '@standardnotes/domain-events' + +import { inject, injectable } from 'inversify' +import { Logger } from 'winston' + +import TYPES from '../../Bootstrap/Types' +import { GroupServiceInterface } from '../Group/Service/GroupServiceInterface' +import { GroupsRepositoryInterface } from '../Group/Repository/GroupRepositoryInterface' + +@injectable() +export class GroupFileRemovedEventHandler implements DomainEventHandlerInterface { + constructor( + @inject(TYPES.GroupService) private groupService: GroupServiceInterface, + @inject(TYPES.GroupRepository) private groupRepository: GroupsRepositoryInterface, + @inject(TYPES.Logger) private logger: Logger, + ) {} + + async handle(event: GroupFileRemovedEvent): Promise { + const group = await this.groupService.getGroup({ + groupUuid: event.payload.groupUuid, + }) + + if (group === null) { + this.logger.warn(`Could not find group with uuid: ${event.payload.groupUuid}`) + + return + } + + group.fileUploadBytesUsed -= event.payload.fileByteSize + + await this.groupRepository.save(group) + } +} diff --git a/packages/syncing-server/src/Domain/Handler/GroupFileUploadedEventHandler.ts b/packages/syncing-server/src/Domain/Handler/GroupFileUploadedEventHandler.ts new file mode 100644 index 000000000..6aad71a07 --- /dev/null +++ b/packages/syncing-server/src/Domain/Handler/GroupFileUploadedEventHandler.ts @@ -0,0 +1,33 @@ +import { DomainEventHandlerInterface, GroupFileUploadedEvent } from '@standardnotes/domain-events' + +import { inject, injectable } from 'inversify' +import { Logger } from 'winston' + +import TYPES from '../../Bootstrap/Types' +import { GroupServiceInterface } from '../Group/Service/GroupServiceInterface' +import { GroupsRepositoryInterface } from '../Group/Repository/GroupRepositoryInterface' + +@injectable() +export class GroupFileUploadedEventHandler implements DomainEventHandlerInterface { + constructor( + @inject(TYPES.GroupService) private groupService: GroupServiceInterface, + @inject(TYPES.GroupRepository) private groupRepository: GroupsRepositoryInterface, + @inject(TYPES.Logger) private logger: Logger, + ) {} + + async handle(event: GroupFileUploadedEvent): Promise { + const group = await this.groupService.getGroup({ + groupUuid: event.payload.groupUuid, + }) + + if (group === null) { + this.logger.warn(`Could not find group with uuid: ${event.payload.groupUuid}`) + + return + } + + group.fileUploadBytesUsed += event.payload.fileByteSize + + await this.groupRepository.save(group) + } +} diff --git a/packages/syncing-server/src/Domain/Handler/UserCredentialsChangedEventHandler.ts b/packages/syncing-server/src/Domain/Handler/UserCredentialsChangedEventHandler.ts index 6084c1989..6fc6c69b5 100644 --- a/packages/syncing-server/src/Domain/Handler/UserCredentialsChangedEventHandler.ts +++ b/packages/syncing-server/src/Domain/Handler/UserCredentialsChangedEventHandler.ts @@ -5,18 +5,18 @@ import { } from '@standardnotes/domain-events' import { TimerInterface } from '@standardnotes/time' import { ContactsRepositoryInterface } from '../Contact/Repository/ContactRepositoryInterface' -import { VaultInviteRepositoryInterface } from '../VaultInvite/Repository/VaultInviteRepositoryInterface' +import { GroupInviteRepositoryInterface } from '../GroupInvite/Repository/GroupInviteRepositoryInterface' export class UserCredentialsChangedEventHandler implements DomainEventHandlerInterface { constructor( private contactRepository: ContactsRepositoryInterface, - private vaultInviteRepository: VaultInviteRepositoryInterface, + private groupInviteRepository: GroupInviteRepositoryInterface, private timer: TimerInterface, ) {} async handle(event: UserCredentialsChangedEvent): Promise { await this.updatePublicKeyOfAllContacts(event.payload) - await this.nullifyPendingInboundVaultInvites(event.payload) + await this.nullifyPendingInboundGroupInvites(event.payload) } private async updatePublicKeyOfAllContacts(payload: UserCredentialsChangedEventPayload): Promise { @@ -31,13 +31,13 @@ export class UserCredentialsChangedEventHandler implements DomainEventHandlerInt } } - private async nullifyPendingInboundVaultInvites(payload: UserCredentialsChangedEventPayload): Promise { - const inboundInvites = await this.vaultInviteRepository.findAll({ + private async nullifyPendingInboundGroupInvites(payload: UserCredentialsChangedEventPayload): Promise { + const inboundInvites = await this.groupInviteRepository.findAll({ userUuid: payload.userUuid, }) for (const invite of inboundInvites) { - await this.vaultInviteRepository.remove(invite) + await this.groupInviteRepository.remove(invite) } } } diff --git a/packages/syncing-server/src/Domain/Handler/VaultFileRemovedEventHandler.ts b/packages/syncing-server/src/Domain/Handler/VaultFileRemovedEventHandler.ts deleted file mode 100644 index 42a136ca3..000000000 --- a/packages/syncing-server/src/Domain/Handler/VaultFileRemovedEventHandler.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { DomainEventHandlerInterface, VaultFileRemovedEvent } from '@standardnotes/domain-events' - -import { inject, injectable } from 'inversify' -import { Logger } from 'winston' - -import TYPES from '../../Bootstrap/Types' -import { VaultServiceInterface } from '../Vault/Service/VaultServiceInterface' -import { VaultsRepositoryInterface } from '../Vault/Repository/VaultRepositoryInterface' - -@injectable() -export class VaultFileRemovedEventHandler implements DomainEventHandlerInterface { - constructor( - @inject(TYPES.VaultService) private vaultService: VaultServiceInterface, - @inject(TYPES.VaultRepository) private vaultRepository: VaultsRepositoryInterface, - @inject(TYPES.Logger) private logger: Logger, - ) {} - - async handle(event: VaultFileRemovedEvent): Promise { - const vault = await this.vaultService.getVault({ - vaultUuid: event.payload.vaultUuid, - }) - - if (vault === null) { - this.logger.warn(`Could not find vault with uuid: ${event.payload.vaultUuid}`) - - return - } - - vault.fileUploadBytesUsed -= event.payload.fileByteSize - - await this.vaultRepository.save(vault) - } -} diff --git a/packages/syncing-server/src/Domain/Handler/VaultFileUploadedEventHandler.ts b/packages/syncing-server/src/Domain/Handler/VaultFileUploadedEventHandler.ts deleted file mode 100644 index 1d6925fba..000000000 --- a/packages/syncing-server/src/Domain/Handler/VaultFileUploadedEventHandler.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { DomainEventHandlerInterface, VaultFileUploadedEvent } from '@standardnotes/domain-events' - -import { inject, injectable } from 'inversify' -import { Logger } from 'winston' - -import TYPES from '../../Bootstrap/Types' -import { VaultServiceInterface } from '../Vault/Service/VaultServiceInterface' -import { VaultsRepositoryInterface } from '../Vault/Repository/VaultRepositoryInterface' - -@injectable() -export class VaultFileUploadedEventHandler implements DomainEventHandlerInterface { - constructor( - @inject(TYPES.VaultService) private vaultService: VaultServiceInterface, - @inject(TYPES.VaultRepository) private vaultRepository: VaultsRepositoryInterface, - @inject(TYPES.Logger) private logger: Logger, - ) {} - - async handle(event: VaultFileUploadedEvent): Promise { - const vault = await this.vaultService.getVault({ - vaultUuid: event.payload.vaultUuid, - }) - - if (vault === null) { - this.logger.warn(`Could not find vault with uuid: ${event.payload.vaultUuid}`) - - return - } - - vault.fileUploadBytesUsed += event.payload.fileByteSize - - await this.vaultRepository.save(vault) - } -} diff --git a/packages/syncing-server/src/Domain/Item/GetItemsDTO.ts b/packages/syncing-server/src/Domain/Item/GetItemsDTO.ts index ec38e8e1c..27516d119 100644 --- a/packages/syncing-server/src/Domain/Item/GetItemsDTO.ts +++ b/packages/syncing-server/src/Domain/Item/GetItemsDTO.ts @@ -4,5 +4,5 @@ export type GetItemsDTO = { cursorToken?: string | null limit?: number contentType?: string - vaultUuids?: string[] | null + groupUuids?: string[] | null } diff --git a/packages/syncing-server/src/Domain/Item/Item.ts b/packages/syncing-server/src/Domain/Item/Item.ts index aa0073e4e..4ecd9e882 100644 --- a/packages/syncing-server/src/Domain/Item/Item.ts +++ b/packages/syncing-server/src/Domain/Item/Item.ts @@ -15,11 +15,19 @@ export class Item { @Column({ type: 'varchar', - name: 'vault_uuid', + name: 'group_uuid', length: 255, nullable: true, }) - declare vaultUuid: string | null + declare groupUuid: string | null + + @Column({ + type: 'varchar', + name: 'vault_identifier', + length: 255, + nullable: true, + }) + declare vault_identifier: string | null @Column({ type: 'varchar', diff --git a/packages/syncing-server/src/Domain/Item/ItemFactory.ts b/packages/syncing-server/src/Domain/Item/ItemFactory.ts index 1f507eb2e..98cea5d3b 100644 --- a/packages/syncing-server/src/Domain/Item/ItemFactory.ts +++ b/packages/syncing-server/src/Domain/Item/ItemFactory.ts @@ -34,8 +34,8 @@ export class ItemFactory implements ItemFactoryInterface { newItem.contentSize = 0 newItem.createdByUuid = dto.userUuid - if (dto.itemHash.vault_uuid) { - newItem.vaultUuid = dto.itemHash.vault_uuid + if (dto.itemHash.group_uuid) { + newItem.groupUuid = dto.itemHash.group_uuid } else { newItem.userUuid = dto.userUuid } diff --git a/packages/syncing-server/src/Domain/Item/ItemHash.ts b/packages/syncing-server/src/Domain/Item/ItemHash.ts index f6b1fa2c6..7aa71410e 100644 --- a/packages/syncing-server/src/Domain/Item/ItemHash.ts +++ b/packages/syncing-server/src/Domain/Item/ItemHash.ts @@ -10,7 +10,7 @@ export type ItemHash = { auth_hash?: string enc_item_key?: string items_key_id?: string - vault_uuid?: string + group_uuid?: string last_edited_by_uuid?: string | null created_at?: string created_at_timestamp?: number diff --git a/packages/syncing-server/src/Domain/Item/ItemQuery.ts b/packages/syncing-server/src/Domain/Item/ItemQuery.ts index c1a896ed8..ecfbf8ac8 100644 --- a/packages/syncing-server/src/Domain/Item/ItemQuery.ts +++ b/packages/syncing-server/src/Domain/Item/ItemQuery.ts @@ -6,8 +6,8 @@ export type ItemQuery = { lastSyncTime?: number syncTimeComparison?: '>' | '>=' contentType?: string | string[] - includeVaultUuids?: string[] - exclusiveVaultUuids?: string[] | null + includeGroupUuids?: string[] + exclusiveGroupUuids?: string[] | null deleted?: boolean offset?: number limit?: number diff --git a/packages/syncing-server/src/Domain/Item/ItemService.ts b/packages/syncing-server/src/Domain/Item/ItemService.ts index 560c63ac9..cb1e7e5f5 100644 --- a/packages/syncing-server/src/Domain/Item/ItemService.ts +++ b/packages/syncing-server/src/Domain/Item/ItemService.ts @@ -19,9 +19,9 @@ import { ItemSaveValidatorInterface } from './SaveValidator/ItemSaveValidatorInt import { ItemTransferCalculatorInterface } from './ItemTransferCalculatorInterface' import { ProjectorInterface } from '../../Projection/ProjectorInterface' import { ItemProjection } from '../../Projection/ItemProjection' -import { VaultUserRepositoryInterface } from '../VaultUser/Repository/VaultUserRepositoryInterface' +import { GroupUserRepositoryInterface } from '../GroupUser/Repository/GroupUserRepositoryInterface' import { ConflictType } from '../../Tmp/ConflictType' -import { VaultServiceInterface } from '../Vault/Service/VaultServiceInterface' +import { GroupServiceInterface } from '../Group/Service/GroupServiceInterface' export class ItemService implements ItemServiceInterface { private readonly DEFAULT_ITEMS_LIMIT = 150 @@ -39,8 +39,8 @@ export class ItemService implements ItemServiceInterface { private timer: TimerInterface, private itemProjector: ProjectorInterface, private maxItemsSyncLimit: number, - private vaultUsersRepository: VaultUserRepositoryInterface, - private vaultService: VaultServiceInterface, + private groupUsersRepository: GroupUserRepositoryInterface, + private groupService: GroupServiceInterface, private logger: Logger, ) {} @@ -50,16 +50,16 @@ export class ItemService implements ItemServiceInterface { const limit = dto.limit === undefined || dto.limit < 1 ? this.DEFAULT_ITEMS_LIMIT : dto.limit const upperBoundLimit = limit < this.maxItemsSyncLimit ? limit : this.maxItemsSyncLimit - const vaultUsers = await this.vaultUsersRepository.findAllForUser({ userUuid: dto.userUuid }) - const userVaultUuids = vaultUsers.map((vaultUser) => vaultUser.vaultUuid) - const exclusiveVaultUuids = dto.vaultUuids - ? dto.vaultUuids.filter((vaultUuid) => userVaultUuids.includes(vaultUuid)) + const groupUsers = await this.groupUsersRepository.findAllForUser({ userUuid: dto.userUuid }) + const userGroupUuids = groupUsers.map((groupUser) => groupUser.groupUuid) + const exclusiveGroupUuids = dto.groupUuids + ? dto.groupUuids.filter((groupUuid) => userGroupUuids.includes(groupUuid)) : undefined const itemQuery: ItemQuery = { userUuid: dto.userUuid, - includeVaultUuids: !dto.vaultUuids ? userVaultUuids : undefined, - exclusiveVaultUuids: exclusiveVaultUuids, + includeGroupUuids: !dto.groupUuids ? userGroupUuids : undefined, + exclusiveGroupUuids: exclusiveGroupUuids, lastSyncTime, syncTimeComparison, contentType: dto.contentType, @@ -99,7 +99,7 @@ export class ItemService implements ItemServiceInterface { const savedItems: Array = [] const conflicts: Array = [] - await this.dynamicallyCreateVaultsBasedOnIncomingItems(dto) + await this.dynamicallyCreateGroupsBasedOnIncomingItems(dto) const lastUpdatedTimestamp = this.timer.getTimestampInMicroseconds() @@ -187,15 +187,15 @@ export class ItemService implements ItemServiceInterface { return retrievedItems } - private async dynamicallyCreateVaultsBasedOnIncomingItems(dto: SaveItemsDTO): Promise { - const processedVaultUuids = new Set() + private async dynamicallyCreateGroupsBasedOnIncomingItems(dto: SaveItemsDTO): Promise { + const processedGroupUuids = new Set() for (const itemHash of dto.itemHashes) { - if (!itemHash.vault_uuid || processedVaultUuids.has(itemHash.vault_uuid)) { + if (!itemHash.group_uuid || processedGroupUuids.has(itemHash.group_uuid)) { continue } - processedVaultUuids.add(itemHash.vault_uuid) + processedGroupUuids.add(itemHash.group_uuid) const vaultItemsKeyUuid = itemHash.content_type === ContentType.VaultItemsKey ? itemHash.uuid : itemHash.items_key_id @@ -203,9 +203,9 @@ export class ItemService implements ItemServiceInterface { continue } - await this.vaultService.createVault({ + await this.groupService.createGroup({ userUuid: dto.userUuid, - vaultUuid: itemHash.vault_uuid, + groupUuid: itemHash.group_uuid, specifiedItemsKeyUuid: vaultItemsKeyUuid, }) } @@ -239,10 +239,10 @@ export class ItemService implements ItemServiceInterface { dto.existingItem.contentSize = 0 dto.existingItem.lastEditedByUuid = dto.userUuid - if (dto.itemHash.vault_uuid) { - dto.existingItem.vaultUuid = dto.itemHash.vault_uuid + if (dto.itemHash.group_uuid) { + dto.existingItem.groupUuid = dto.itemHash.group_uuid } else { - dto.existingItem.vaultUuid = null + dto.existingItem.groupUuid = null } if (dto.itemHash.content) { diff --git a/packages/syncing-server/src/Domain/Item/SaveRule/GroupFilter.ts b/packages/syncing-server/src/Domain/Item/SaveRule/GroupFilter.ts new file mode 100644 index 000000000..3311021cb --- /dev/null +++ b/packages/syncing-server/src/Domain/Item/SaveRule/GroupFilter.ts @@ -0,0 +1,301 @@ +import { ItemSaveValidationDTO } from '../SaveValidator/ItemSaveValidationDTO' +import { ItemSaveRuleResult } from './ItemSaveRuleResult' +import { ItemSaveRuleInterface } from './ItemSaveRuleInterface' +import { GroupUserServiceInterface } from '../../GroupUser/Service/GroupUserServiceInterface' +import { ItemHash } from '../ItemHash' +import { GroupUserPermission } from '../../GroupUser/Model/GroupUserPermission' +import { GroupServiceInterface } from '../../Group/Service/GroupServiceInterface' +import { ContentType } from '@standardnotes/common' +import { ConflictType } from '../../../Tmp/ConflictType' +import { + GroupSaveOperation, + AddToGroupSaveOperation, + RemoveFromGroupSaveOperation, + MoveToOtherGroupSaveOperation, + SaveToGroupSaveOperation, + CreateToGroupSaveOperation, +} from './GroupSaveOperation' +import { Item } from '../Item' + +export class GroupFilter implements ItemSaveRuleInterface { + constructor(private groupService: GroupServiceInterface, private groupUserService: GroupUserServiceInterface) {} + + async check(dto: ItemSaveValidationDTO): Promise { + if (!dto.existingItem?.groupUuid && !dto.itemHash.group_uuid) { + return { + passed: true, + } + } + + const operation = this.getSaveOperationType(dto) + return this.getResultForOperation(operation) + } + + private async getResultForOperation(operation: GroupSaveOperation): Promise { + if (operation.type === 'add-to-group') { + return this.handleAddToGroupOperation(operation) + } else if (operation.type === 'remove-from-group') { + return this.handleRemoveFromGroupOperation(operation) + } else if (operation.type === 'move-to-other-group') { + return this.handleMoveToOtherGroupOperation(operation) + } else if (operation.type === 'save-to-group') { + return this.handleSaveToGroupOperation(operation) + } else if (operation.type === 'create-to-group') { + return this.handleCreateToGroupOperation(operation) + } + + throw new Error(`Unsupported group operation: ${operation}`) + } + + private getSaveOperationType(dto: ItemSaveValidationDTO): GroupSaveOperation { + const existingItemGroupUuid = dto.existingItem?.groupUuid + const targetItemGroupUuid = dto.itemHash.group_uuid + + const common = { + incomingItem: dto.itemHash, + userUuid: dto.userUuid, + } + + if ( + dto.existingItem && + existingItemGroupUuid && + targetItemGroupUuid && + existingItemGroupUuid !== targetItemGroupUuid + ) { + return { + type: 'move-to-other-group', + groupUuid: existingItemGroupUuid, + targetGroupUuid: targetItemGroupUuid, + existingItem: dto.existingItem, + ...common, + } + } + + if (dto.existingItem && existingItemGroupUuid && !targetItemGroupUuid) { + return { + type: 'remove-from-group', + groupUuid: existingItemGroupUuid, + existingItem: dto.existingItem, + ...common, + } + } + + if (dto.existingItem && !existingItemGroupUuid && targetItemGroupUuid) { + return { + type: 'add-to-group', + groupUuid: targetItemGroupUuid, + existingItem: dto.existingItem, + ...common, + } + } + + if (dto.existingItem && existingItemGroupUuid && existingItemGroupUuid === targetItemGroupUuid) { + return { + type: 'save-to-group', + groupUuid: existingItemGroupUuid, + existingItem: dto.existingItem, + ...common, + } + } + + if (!dto.existingItem && targetItemGroupUuid) { + return { + type: 'create-to-group', + groupUuid: targetItemGroupUuid, + ...common, + } + } + + throw new Error('Invalid save operation') + } + + private isAuthorizedToSaveContentType(contentType: ContentType, permissions: GroupUserPermission): boolean { + if (contentType === ContentType.VaultItemsKey) { + return permissions === 'admin' + } + + return true + } + + private buildFailResult(operation: GroupSaveOperation, type: ConflictType, serverItem?: Item) { + return { + passed: false, + conflict: { + unsavedItem: operation.incomingItem, + serverItem, + type, + }, + } + } + + private buildSuccessValue(): ItemSaveRuleResult { + return { + passed: true, + } + } + + private async handleAddToGroupOperation(operation: AddToGroupSaveOperation): Promise { + const groupPermissions = await this.getGroupPermissions(operation.userUuid, operation.groupUuid) + if (!groupPermissions) { + return this.buildFailResult(operation, ConflictType.GroupNotMemberError) + } + + if (operation.existingItem.deleted || operation.incomingItem.deleted) { + return this.buildFailResult(operation, ConflictType.GroupInvalidState) + } + + if (!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, groupPermissions)) { + return this.buildFailResult(operation, ConflictType.GroupInsufficientPermissionsError) + } + + if (groupPermissions === 'read') { + return this.buildFailResult(operation, ConflictType.GroupInsufficientPermissionsError) + } + + if (operation.existingItem.userUuid !== operation.userUuid) { + return this.buildFailResult(operation, ConflictType.UuidConflict) + } + + const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem) + if (!usesValidKey) { + return this.buildFailResult(operation, ConflictType.GroupInvalidItemsKey, operation.existingItem) + } + + return this.buildSuccessValue() + } + + private async handleRemoveFromGroupOperation(operation: RemoveFromGroupSaveOperation): Promise { + const groupPermissions = await this.getGroupPermissions(operation.userUuid, operation.groupUuid) + if (!groupPermissions) { + return this.buildFailResult(operation, ConflictType.GroupNotMemberError) + } + + if (operation.existingItem.deleted || operation.incomingItem.deleted) { + return this.buildFailResult(operation, ConflictType.GroupInvalidState) + } + + if (operation.existingItem.userUuid === operation.userUuid) { + return this.buildSuccessValue() + } + + if (!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, groupPermissions)) { + return this.buildFailResult(operation, ConflictType.GroupInsufficientPermissionsError) + } + + if (groupPermissions === 'read') { + return this.buildFailResult(operation, ConflictType.GroupInsufficientPermissionsError) + } + + const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem) + if (!usesValidKey) { + return this.buildFailResult(operation, ConflictType.GroupInvalidItemsKey, operation.existingItem) + } + + return this.buildSuccessValue() + } + + private async handleMoveToOtherGroupOperation(operation: MoveToOtherGroupSaveOperation): Promise { + const sourceGroupPermissions = await this.getGroupPermissions(operation.userUuid, operation.groupUuid) + const targetGroupPermissions = await this.getGroupPermissions(operation.userUuid, operation.targetGroupUuid) + + if (!sourceGroupPermissions || !targetGroupPermissions) { + return this.buildFailResult(operation, ConflictType.GroupNotMemberError) + } + + if (operation.existingItem.deleted || operation.incomingItem.deleted) { + return this.buildFailResult(operation, ConflictType.GroupInvalidState) + } + + if (sourceGroupPermissions === 'read' || targetGroupPermissions === 'read') { + return this.buildFailResult(operation, ConflictType.GroupInsufficientPermissionsError) + } + + if ( + !this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, sourceGroupPermissions) || + !this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, targetGroupPermissions) + ) { + return this.buildFailResult(operation, ConflictType.GroupInsufficientPermissionsError) + } + + const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem) + if (!usesValidKey) { + return this.buildFailResult(operation, ConflictType.GroupInvalidItemsKey, operation.existingItem) + } + + return this.buildSuccessValue() + } + + private async handleSaveToGroupOperation(operation: SaveToGroupSaveOperation): Promise { + const groupPermissions = await this.getGroupPermissions(operation.userUuid, operation.groupUuid) + + if (!groupPermissions) { + return this.buildFailResult(operation, ConflictType.GroupNotMemberError) + } + + if (!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, groupPermissions)) { + return this.buildFailResult(operation, ConflictType.GroupInsufficientPermissionsError) + } + + if (groupPermissions === 'read') { + return this.buildFailResult(operation, ConflictType.GroupInsufficientPermissionsError) + } + + const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem) + if (!usesValidKey) { + return this.buildFailResult(operation, ConflictType.GroupInvalidItemsKey, operation.existingItem) + } + + return this.buildSuccessValue() + } + + private async handleCreateToGroupOperation(operation: CreateToGroupSaveOperation): Promise { + const groupPermissions = await this.getGroupPermissions(operation.userUuid, operation.groupUuid) + + if (!groupPermissions) { + return this.buildFailResult(operation, ConflictType.GroupNotMemberError) + } + + if (!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, groupPermissions)) { + return this.buildFailResult(operation, ConflictType.GroupInsufficientPermissionsError) + } + + if (groupPermissions === 'read') { + return this.buildFailResult(operation, ConflictType.GroupInsufficientPermissionsError) + } + + const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem) + if (!usesValidKey) { + return this.buildFailResult(operation, ConflictType.GroupInvalidItemsKey) + } + + return this.buildSuccessValue() + } + + private async incomingItemUsesValidItemsKey(itemHash: ItemHash): Promise { + const isItemNotEncryptedByItemsKey = itemHash.content_type === ContentType.VaultItemsKey + if (isItemNotEncryptedByItemsKey) { + return true + } + + const group = await this.groupService.getGroup({ groupUuid: itemHash.group_uuid as string }) + + if (!group) { + return false + } + + return itemHash.items_key_id === group.specifiedItemsKeyUuid + } + + private async getGroupPermissions(userUuid: string, groupUuid: string): Promise { + const groupUser = await this.groupUserService.getUserForGroup({ + userUuid, + groupUuid: groupUuid, + }) + + if (groupUser) { + return groupUser.permissions + } + + return undefined + } +} diff --git a/packages/syncing-server/src/Domain/Item/SaveRule/GroupSaveOperation.ts b/packages/syncing-server/src/Domain/Item/SaveRule/GroupSaveOperation.ts new file mode 100644 index 000000000..51307e8df --- /dev/null +++ b/packages/syncing-server/src/Domain/Item/SaveRule/GroupSaveOperation.ts @@ -0,0 +1,44 @@ +import { ItemHash } from '../ItemHash' +import { Item } from '../Item' + +type SaveOperationCommon = { + incomingItem: ItemHash + userUuid: string +} + +export type AddToGroupSaveOperation = SaveOperationCommon & { + type: 'add-to-group' + groupUuid: string + existingItem: Item +} + +export type RemoveFromGroupSaveOperation = SaveOperationCommon & { + type: 'remove-from-group' + groupUuid: string + existingItem: Item +} + +export type MoveToOtherGroupSaveOperation = SaveOperationCommon & { + type: 'move-to-other-group' + groupUuid: string + targetGroupUuid: string + existingItem: Item +} + +export type SaveToGroupSaveOperation = SaveOperationCommon & { + type: 'save-to-group' + groupUuid: string + existingItem: Item +} + +export type CreateToGroupSaveOperation = SaveOperationCommon & { + type: 'create-to-group' + groupUuid: string +} + +export type GroupSaveOperation = + | AddToGroupSaveOperation + | RemoveFromGroupSaveOperation + | MoveToOtherGroupSaveOperation + | SaveToGroupSaveOperation + | CreateToGroupSaveOperation diff --git a/packages/syncing-server/src/Domain/Item/SaveRule/VaultSnjsVersionFilter.ts b/packages/syncing-server/src/Domain/Item/SaveRule/GroupSnjsVersionFilter.ts similarity index 74% rename from packages/syncing-server/src/Domain/Item/SaveRule/VaultSnjsVersionFilter.ts rename to packages/syncing-server/src/Domain/Item/SaveRule/GroupSnjsVersionFilter.ts index 777d7939a..841865006 100644 --- a/packages/syncing-server/src/Domain/Item/SaveRule/VaultSnjsVersionFilter.ts +++ b/packages/syncing-server/src/Domain/Item/SaveRule/GroupSnjsVersionFilter.ts @@ -4,20 +4,20 @@ import { ItemSaveRuleInterface } from './ItemSaveRuleInterface' import { lt } from 'semver' import { ConflictType } from '../../../Tmp/ConflictType' -const MINIMUM_SNJS_VERSION_FOR_VAULT_OPERATIONS = '2.200.0' +const MINIMUM_SNJS_VERSION_FOR_GROUP_OPERATIONS = '2.200.0' -export class VaultSnjsVersionFilter implements ItemSaveRuleInterface { +export class GroupSnjsVersionFilter implements ItemSaveRuleInterface { async check(dto: ItemSaveValidationDTO): Promise { const successValue = { passed: true, } - const vaultUuidInvolved = dto.existingItem?.vaultUuid || dto.itemHash.vault_uuid - if (!vaultUuidInvolved) { + const groupUuidInvolved = dto.existingItem?.groupUuid || dto.itemHash.group_uuid + if (!groupUuidInvolved) { return successValue } - const isClientSnjsVersionLessThanMinimum = lt(dto.snjsVersion, MINIMUM_SNJS_VERSION_FOR_VAULT_OPERATIONS) + const isClientSnjsVersionLessThanMinimum = lt(dto.snjsVersion, MINIMUM_SNJS_VERSION_FOR_GROUP_OPERATIONS) if (isClientSnjsVersionLessThanMinimum) { return { passed: false, diff --git a/packages/syncing-server/src/Domain/Item/SaveRule/OwnershipFilter.ts b/packages/syncing-server/src/Domain/Item/SaveRule/OwnershipFilter.ts index 5bc9dc05f..3a8840ea0 100644 --- a/packages/syncing-server/src/Domain/Item/SaveRule/OwnershipFilter.ts +++ b/packages/syncing-server/src/Domain/Item/SaveRule/OwnershipFilter.ts @@ -5,8 +5,8 @@ import { ConflictType } from '../../../Tmp/ConflictType' export class OwnershipFilter implements ItemSaveRuleInterface { async check(dto: ItemSaveValidationDTO): Promise { - const deferToVaultFilter = dto.existingItem?.vaultUuid || dto.itemHash.vault_uuid - if (deferToVaultFilter) { + const deferToGroupFilter = dto.existingItem?.groupUuid || dto.itemHash.group_uuid + if (deferToGroupFilter) { return { passed: true, } diff --git a/packages/syncing-server/src/Domain/Item/SaveRule/VaultFilter.ts b/packages/syncing-server/src/Domain/Item/SaveRule/VaultFilter.ts deleted file mode 100644 index 389c9f386..000000000 --- a/packages/syncing-server/src/Domain/Item/SaveRule/VaultFilter.ts +++ /dev/null @@ -1,301 +0,0 @@ -import { ItemSaveValidationDTO } from '../SaveValidator/ItemSaveValidationDTO' -import { ItemSaveRuleResult } from './ItemSaveRuleResult' -import { ItemSaveRuleInterface } from './ItemSaveRuleInterface' -import { VaultUserServiceInterface } from '../../VaultUser/Service/VaultUserServiceInterface' -import { ItemHash } from '../ItemHash' -import { VaultUserPermission } from '../../VaultUser/Model/VaultUserPermission' -import { VaultServiceInterface } from '../../Vault/Service/VaultServiceInterface' -import { ContentType } from '@standardnotes/common' -import { ConflictType } from '../../../Tmp/ConflictType' -import { - VaultSaveOperation, - AddToVaultSaveOperation, - RemoveFromVaultSaveOperation, - MoveToOtherVaultSaveOperation, - SaveToVaultSaveOperation, - CreateToVaultSaveOperation, -} from './VaultSaveOperation' -import { Item } from '../Item' - -export class VaultFilter implements ItemSaveRuleInterface { - constructor(private vaultService: VaultServiceInterface, private vaultUserService: VaultUserServiceInterface) {} - - async check(dto: ItemSaveValidationDTO): Promise { - if (!dto.existingItem?.vaultUuid && !dto.itemHash.vault_uuid) { - return { - passed: true, - } - } - - const operation = this.getSaveOperationType(dto) - return this.getResultForOperation(operation) - } - - private async getResultForOperation(operation: VaultSaveOperation): Promise { - if (operation.type === 'add-to-vault') { - return this.handleAddToVaultOperation(operation) - } else if (operation.type === 'remove-from-vault') { - return this.handleRemoveFromVaultOperation(operation) - } else if (operation.type === 'move-to-other-vault') { - return this.handleMoveToOtherVaultOperation(operation) - } else if (operation.type === 'save-to-vault') { - return this.handleSaveToVaultOperation(operation) - } else if (operation.type === 'create-to-vault') { - return this.handleCreateToVaultOperation(operation) - } - - throw new Error(`Unsupported vault operation: ${operation}`) - } - - private getSaveOperationType(dto: ItemSaveValidationDTO): VaultSaveOperation { - const existingItemVaultUuid = dto.existingItem?.vaultUuid - const targetItemVaultUuid = dto.itemHash.vault_uuid - - const common = { - incomingItem: dto.itemHash, - userUuid: dto.userUuid, - } - - if ( - dto.existingItem && - existingItemVaultUuid && - targetItemVaultUuid && - existingItemVaultUuid !== targetItemVaultUuid - ) { - return { - type: 'move-to-other-vault', - vaultUuid: existingItemVaultUuid, - targetVaultUuid: targetItemVaultUuid, - existingItem: dto.existingItem, - ...common, - } - } - - if (dto.existingItem && existingItemVaultUuid && !targetItemVaultUuid) { - return { - type: 'remove-from-vault', - vaultUuid: existingItemVaultUuid, - existingItem: dto.existingItem, - ...common, - } - } - - if (dto.existingItem && !existingItemVaultUuid && targetItemVaultUuid) { - return { - type: 'add-to-vault', - vaultUuid: targetItemVaultUuid, - existingItem: dto.existingItem, - ...common, - } - } - - if (dto.existingItem && existingItemVaultUuid && existingItemVaultUuid === targetItemVaultUuid) { - return { - type: 'save-to-vault', - vaultUuid: existingItemVaultUuid, - existingItem: dto.existingItem, - ...common, - } - } - - if (!dto.existingItem && targetItemVaultUuid) { - return { - type: 'create-to-vault', - vaultUuid: targetItemVaultUuid, - ...common, - } - } - - throw new Error('Invalid save operation') - } - - private isAuthorizedToSaveContentType(contentType: ContentType, permissions: VaultUserPermission): boolean { - if (contentType === ContentType.VaultItemsKey) { - return permissions === 'admin' - } - - return true - } - - private buildFailResult(operation: VaultSaveOperation, type: ConflictType, serverItem?: Item) { - return { - passed: false, - conflict: { - unsavedItem: operation.incomingItem, - serverItem, - type, - }, - } - } - - private buildSuccessValue(): ItemSaveRuleResult { - return { - passed: true, - } - } - - private async handleAddToVaultOperation(operation: AddToVaultSaveOperation): Promise { - const vaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.vaultUuid) - if (!vaultPermissions) { - return this.buildFailResult(operation, ConflictType.VaultNotMemberError) - } - - if (operation.existingItem.deleted || operation.incomingItem.deleted) { - return this.buildFailResult(operation, ConflictType.VaultInvalidState) - } - - if (!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, vaultPermissions)) { - return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError) - } - - if (vaultPermissions === 'read') { - return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError) - } - - if (operation.existingItem.userUuid !== operation.userUuid) { - return this.buildFailResult(operation, ConflictType.UuidConflict) - } - - const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem) - if (!usesValidKey) { - return this.buildFailResult(operation, ConflictType.VaultInvalidItemsKey, operation.existingItem) - } - - return this.buildSuccessValue() - } - - private async handleRemoveFromVaultOperation(operation: RemoveFromVaultSaveOperation): Promise { - const vaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.vaultUuid) - if (!vaultPermissions) { - return this.buildFailResult(operation, ConflictType.VaultNotMemberError) - } - - if (operation.existingItem.deleted || operation.incomingItem.deleted) { - return this.buildFailResult(operation, ConflictType.VaultInvalidState) - } - - if (operation.existingItem.userUuid === operation.userUuid) { - return this.buildSuccessValue() - } - - if (!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, vaultPermissions)) { - return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError) - } - - if (vaultPermissions === 'read') { - return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError) - } - - const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem) - if (!usesValidKey) { - return this.buildFailResult(operation, ConflictType.VaultInvalidItemsKey, operation.existingItem) - } - - return this.buildSuccessValue() - } - - private async handleMoveToOtherVaultOperation(operation: MoveToOtherVaultSaveOperation): Promise { - const sourceVaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.vaultUuid) - const targetVaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.targetVaultUuid) - - if (!sourceVaultPermissions || !targetVaultPermissions) { - return this.buildFailResult(operation, ConflictType.VaultNotMemberError) - } - - if (operation.existingItem.deleted || operation.incomingItem.deleted) { - return this.buildFailResult(operation, ConflictType.VaultInvalidState) - } - - if (sourceVaultPermissions === 'read' || targetVaultPermissions === 'read') { - return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError) - } - - if ( - !this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, sourceVaultPermissions) || - !this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, targetVaultPermissions) - ) { - return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError) - } - - const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem) - if (!usesValidKey) { - return this.buildFailResult(operation, ConflictType.VaultInvalidItemsKey, operation.existingItem) - } - - return this.buildSuccessValue() - } - - private async handleSaveToVaultOperation(operation: SaveToVaultSaveOperation): Promise { - const vaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.vaultUuid) - - if (!vaultPermissions) { - return this.buildFailResult(operation, ConflictType.VaultNotMemberError) - } - - if (!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, vaultPermissions)) { - return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError) - } - - if (vaultPermissions === 'read') { - return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError) - } - - const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem) - if (!usesValidKey) { - return this.buildFailResult(operation, ConflictType.VaultInvalidItemsKey, operation.existingItem) - } - - return this.buildSuccessValue() - } - - private async handleCreateToVaultOperation(operation: CreateToVaultSaveOperation): Promise { - const vaultPermissions = await this.getVaultPermissions(operation.userUuid, operation.vaultUuid) - - if (!vaultPermissions) { - return this.buildFailResult(operation, ConflictType.VaultNotMemberError) - } - - if (!this.isAuthorizedToSaveContentType(operation.incomingItem.content_type, vaultPermissions)) { - return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError) - } - - if (vaultPermissions === 'read') { - return this.buildFailResult(operation, ConflictType.VaultInsufficientPermissionsError) - } - - const usesValidKey = await this.incomingItemUsesValidItemsKey(operation.incomingItem) - if (!usesValidKey) { - return this.buildFailResult(operation, ConflictType.VaultInvalidItemsKey) - } - - return this.buildSuccessValue() - } - - private async incomingItemUsesValidItemsKey(itemHash: ItemHash): Promise { - const isItemNotEncryptedByItemsKey = itemHash.content_type === ContentType.VaultItemsKey - if (isItemNotEncryptedByItemsKey) { - return true - } - - const vault = await this.vaultService.getVault({ vaultUuid: itemHash.vault_uuid as string }) - - if (!vault) { - return false - } - - return itemHash.items_key_id === vault.specifiedItemsKeyUuid - } - - private async getVaultPermissions(userUuid: string, vaultUuid: string): Promise { - const vaultUser = await this.vaultUserService.getUserForVault({ - userUuid, - vaultUuid: vaultUuid, - }) - - if (vaultUser) { - return vaultUser.permissions - } - - return undefined - } -} diff --git a/packages/syncing-server/src/Domain/Item/SaveRule/VaultSaveOperation.ts b/packages/syncing-server/src/Domain/Item/SaveRule/VaultSaveOperation.ts deleted file mode 100644 index 802261692..000000000 --- a/packages/syncing-server/src/Domain/Item/SaveRule/VaultSaveOperation.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { ItemHash } from '../ItemHash' -import { Item } from '../Item' - -type SaveOperationCommon = { - incomingItem: ItemHash - userUuid: string -} - -export type AddToVaultSaveOperation = SaveOperationCommon & { - type: 'add-to-vault' - vaultUuid: string - existingItem: Item -} - -export type RemoveFromVaultSaveOperation = SaveOperationCommon & { - type: 'remove-from-vault' - vaultUuid: string - existingItem: Item -} - -export type MoveToOtherVaultSaveOperation = SaveOperationCommon & { - type: 'move-to-other-vault' - vaultUuid: string - targetVaultUuid: string - existingItem: Item -} - -export type SaveToVaultSaveOperation = SaveOperationCommon & { - type: 'save-to-vault' - vaultUuid: string - existingItem: Item -} - -export type CreateToVaultSaveOperation = SaveOperationCommon & { - type: 'create-to-vault' - vaultUuid: string -} - -export type VaultSaveOperation = - | AddToVaultSaveOperation - | RemoveFromVaultSaveOperation - | MoveToOtherVaultSaveOperation - | SaveToVaultSaveOperation - | CreateToVaultSaveOperation diff --git a/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponse20200115.ts b/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponse20200115.ts index 8ee2cc5c2..b30f5ed3c 100644 --- a/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponse20200115.ts +++ b/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponse20200115.ts @@ -1,6 +1,6 @@ import { ContactProjection } from '../../../Projection/ContactProjection' -import { VaultInviteProjection } from '../../../Projection/VaultInviteProjection' -import { VaultProjection } from '../../../Projection/VaultProjection' +import { GroupInviteProjection } from '../../../Projection/GroupInviteProjection' +import { GroupProjection } from '../../../Projection/GroupProjection' import { ItemConflictProjection } from '../../../Projection/ItemConflictProjection' import { ItemProjection } from '../../../Projection/ItemProjection' import { SavedItemProjection } from '../../../Projection/SavedItemProjection' @@ -12,6 +12,6 @@ export type SyncResponse20200115 = { sync_token: string cursor_token?: string contacts: Array - vaults: Array - vault_invites: Array + groups: Array + group_invites: Array } diff --git a/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponseFactory20200115.ts b/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponseFactory20200115.ts index 55c01cd7b..cefcb18c7 100644 --- a/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponseFactory20200115.ts +++ b/packages/syncing-server/src/Domain/Item/SyncResponse/SyncResponseFactory20200115.ts @@ -1,4 +1,4 @@ -import { VaultInvite } from '../../VaultInvite/Model/VaultInvite' +import { GroupInvite } from '../../GroupInvite/Model/GroupInvite' import { Contact } from './../../Contact/Model/Contact' import { ProjectorInterface } from '../../../Projection/ProjectorInterface' import { SyncItemsResponse } from '../../UseCase/SyncItemsResponse' @@ -10,18 +10,18 @@ import { SyncResponse20200115 } from './SyncResponse20200115' import { SyncResponseFactoryInterface } from './SyncResponseFactoryInterface' import { SavedItemProjection } from '../../../Projection/SavedItemProjection' import { ContactProjection } from '../../../Projection/ContactProjection' -import { VaultInviteProjection } from '../../../Projection/VaultInviteProjection' -import { VaultProjection } from '../../../Projection/VaultProjection' -import { Vault } from '../../Vault/Model/Vault' +import { GroupInviteProjection } from '../../../Projection/GroupInviteProjection' +import { GroupProjection } from '../../../Projection/GroupProjection' +import { Group } from '../../Group/Model/Group' export class SyncResponseFactory20200115 implements SyncResponseFactoryInterface { constructor( private itemProjector: ProjectorInterface, private itemConflictProjector: ProjectorInterface, private savedItemProjector: ProjectorInterface, - private vaultInviteProjector: ProjectorInterface, + private groupInviteProjector: ProjectorInterface, private contactProjector: ProjectorInterface, - private vaultProjector: ProjectorInterface, + private groupProjector: ProjectorInterface, ) {} async createResponse(syncItemsResponse: SyncItemsResponse): Promise { @@ -40,14 +40,14 @@ export class SyncResponseFactory20200115 implements SyncResponseFactoryInterface conflicts.push(this.itemConflictProjector.projectFull(itemConflict)) } - const vaults = [] - for (const vault of syncItemsResponse.vaults) { - vaults.push(this.vaultProjector.projectFull(vault)) + const groups = [] + for (const group of syncItemsResponse.groups) { + groups.push(this.groupProjector.projectFull(group)) } - const vaultInvites = [] - for (const vaultInvite of syncItemsResponse.vaultInvites) { - vaultInvites.push(this.vaultInviteProjector.projectFull(vaultInvite)) + const groupInvites = [] + for (const groupInvite of syncItemsResponse.groupInvites) { + groupInvites.push(this.groupInviteProjector.projectFull(groupInvite)) } const contacts = [] @@ -61,8 +61,8 @@ export class SyncResponseFactory20200115 implements SyncResponseFactoryInterface conflicts, sync_token: syncItemsResponse.syncToken, cursor_token: syncItemsResponse.cursorToken, - vaults, - vault_invites: vaultInvites, + groups, + group_invites: groupInvites, contacts, } } diff --git a/packages/syncing-server/src/Domain/RemovedGroupUser/Factory/RemovedGroupUserFactory.ts b/packages/syncing-server/src/Domain/RemovedGroupUser/Factory/RemovedGroupUserFactory.ts new file mode 100644 index 000000000..1fe993c17 --- /dev/null +++ b/packages/syncing-server/src/Domain/RemovedGroupUser/Factory/RemovedGroupUserFactory.ts @@ -0,0 +1,31 @@ +import { TimerInterface } from '@standardnotes/time' +import { RemovedGroupUserFactoryInterface } from './RemovedGroupUserFactoryInterface' +import { RemovedGroupUserHash } from './RemovedGroupUserHash' +import { RemovedGroupUser } from '../Model/RemovedGroupUser' + +export class RemovedGroupUserFactory implements RemovedGroupUserFactoryInterface { + constructor(private timer: TimerInterface) {} + + create(dto: RemovedGroupUserHash): RemovedGroupUser { + const newRemovedGroupUser = new RemovedGroupUser() + newRemovedGroupUser.uuid = dto.uuid + newRemovedGroupUser.userUuid = dto.user_uuid + newRemovedGroupUser.groupUuid = dto.group_uuid + newRemovedGroupUser.removedBy = dto.removed_by + + const now = this.timer.getTimestampInMicroseconds() + newRemovedGroupUser.updatedAtTimestamp = now + newRemovedGroupUser.createdAtTimestamp = now + + if (dto.created_at_timestamp) { + newRemovedGroupUser.createdAtTimestamp = dto.created_at_timestamp + } + + return newRemovedGroupUser + } + + createStub(dto: RemovedGroupUserHash): RemovedGroupUser { + const item = this.create(dto) + return item + } +} diff --git a/packages/syncing-server/src/Domain/RemovedGroupUser/Factory/RemovedGroupUserFactoryInterface.ts b/packages/syncing-server/src/Domain/RemovedGroupUser/Factory/RemovedGroupUserFactoryInterface.ts new file mode 100644 index 000000000..ad01bd4b2 --- /dev/null +++ b/packages/syncing-server/src/Domain/RemovedGroupUser/Factory/RemovedGroupUserFactoryInterface.ts @@ -0,0 +1,7 @@ +import { RemovedGroupUser } from '../Model/RemovedGroupUser' +import { RemovedGroupUserHash } from './RemovedGroupUserHash' + +export interface RemovedGroupUserFactoryInterface { + create(dto: RemovedGroupUserHash): RemovedGroupUser + createStub(dto: RemovedGroupUserHash): RemovedGroupUser +} diff --git a/packages/syncing-server/src/Domain/RemovedVaultUser/Factory/RemovedVaultUserHash.ts b/packages/syncing-server/src/Domain/RemovedGroupUser/Factory/RemovedGroupUserHash.ts similarity index 67% rename from packages/syncing-server/src/Domain/RemovedVaultUser/Factory/RemovedVaultUserHash.ts rename to packages/syncing-server/src/Domain/RemovedGroupUser/Factory/RemovedGroupUserHash.ts index 44d461770..22a6a8ad7 100644 --- a/packages/syncing-server/src/Domain/RemovedVaultUser/Factory/RemovedVaultUserHash.ts +++ b/packages/syncing-server/src/Domain/RemovedGroupUser/Factory/RemovedGroupUserHash.ts @@ -1,8 +1,8 @@ -export type RemovedVaultUserHash = { +export type RemovedGroupUserHash = { uuid: string user_uuid: string removed_by: string - vault_uuid: string + group_uuid: string created_at_timestamp?: number updated_at_timestamp?: number } diff --git a/packages/syncing-server/src/Domain/RemovedVaultUser/Model/RemovedVaultUser.ts b/packages/syncing-server/src/Domain/RemovedGroupUser/Model/RemovedGroupUser.ts similarity index 81% rename from packages/syncing-server/src/Domain/RemovedVaultUser/Model/RemovedVaultUser.ts rename to packages/syncing-server/src/Domain/RemovedGroupUser/Model/RemovedGroupUser.ts index 8f028d685..a7f60eff6 100644 --- a/packages/syncing-server/src/Domain/RemovedVaultUser/Model/RemovedVaultUser.ts +++ b/packages/syncing-server/src/Domain/RemovedGroupUser/Model/RemovedGroupUser.ts @@ -1,15 +1,15 @@ import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm' -@Entity({ name: 'removed_vault_users' }) -export class RemovedVaultUser { +@Entity({ name: 'removed_group_users' }) +export class RemovedGroupUser { @PrimaryGeneratedColumn('uuid') declare uuid: string @Column({ - name: 'vault_uuid', + name: 'group_uuid', length: 36, }) - declare vaultUuid: string + declare groupUuid: string @Column({ name: 'user_uuid', diff --git a/packages/syncing-server/src/Domain/RemovedGroupUser/Repository/RemovedGroupUserRepositoryInterface.ts b/packages/syncing-server/src/Domain/RemovedGroupUser/Repository/RemovedGroupUserRepositoryInterface.ts new file mode 100644 index 000000000..90ea20822 --- /dev/null +++ b/packages/syncing-server/src/Domain/RemovedGroupUser/Repository/RemovedGroupUserRepositoryInterface.ts @@ -0,0 +1,12 @@ +import { RemovedGroupUser } from '../Model/RemovedGroupUser' + +export type RemovedGroupUserFindAllForUserQuery = { + userUuid: string +} + +export interface RemovedGroupUserRepositoryInterface { + create(group: RemovedGroupUser): Promise + remove(group: RemovedGroupUser): Promise + findAllForUser(query: RemovedGroupUserFindAllForUserQuery): Promise + findByUserUuidAndGroupUuid(userUuid: string, groupUuid: string): Promise +} diff --git a/packages/syncing-server/src/Domain/RemovedGroupUser/Repository/TypeORMRemovedGroupUserRepository.ts b/packages/syncing-server/src/Domain/RemovedGroupUser/Repository/TypeORMRemovedGroupUserRepository.ts new file mode 100644 index 000000000..8014ec578 --- /dev/null +++ b/packages/syncing-server/src/Domain/RemovedGroupUser/Repository/TypeORMRemovedGroupUserRepository.ts @@ -0,0 +1,38 @@ +import { RemovedGroupUser } from '../Model/RemovedGroupUser' +import { Repository, SelectQueryBuilder } from 'typeorm' +import { + RemovedGroupUserFindAllForUserQuery, + RemovedGroupUserRepositoryInterface, +} from './RemovedGroupUserRepositoryInterface' + +export class TypeORMRemovedGroupUserRepository implements RemovedGroupUserRepositoryInterface { + constructor(private ormRepository: Repository) {} + + async create(removedGroupUser: RemovedGroupUser): Promise { + return this.ormRepository.save(removedGroupUser) + } + + findByUserUuidAndGroupUuid(userUuid: string, groupUuid: string): Promise { + return this.ormRepository + .createQueryBuilder('removed_group_user') + .where('removed_group_user.user_uuid = :userUuid', { userUuid }) + .andWhere('removed_group_user.group_uuid = :groupUuid', { groupUuid }) + .getOne() + } + + async remove(removedGroupUser: RemovedGroupUser): Promise { + return this.ormRepository.remove(removedGroupUser) + } + + async findAllForUser(query: RemovedGroupUserFindAllForUserQuery): Promise { + return this.createFindAllQueryBuilder(query).getMany() + } + + private createFindAllQueryBuilder(query: RemovedGroupUserFindAllForUserQuery): SelectQueryBuilder { + const queryBuilder = this.ormRepository.createQueryBuilder('removed_group_user') + + queryBuilder.where('removed_group_user.user_uuid = :userUuid', { userUuid: query.userUuid }) + + return queryBuilder + } +} diff --git a/packages/syncing-server/src/Domain/RemovedGroupUser/Service/GetRemovedGroupUsersDTO.ts b/packages/syncing-server/src/Domain/RemovedGroupUser/Service/GetRemovedGroupUsersDTO.ts new file mode 100644 index 000000000..6a64b75a4 --- /dev/null +++ b/packages/syncing-server/src/Domain/RemovedGroupUser/Service/GetRemovedGroupUsersDTO.ts @@ -0,0 +1,3 @@ +export interface GetRemovedGroupUsersDTO { + userUuid: string +} diff --git a/packages/syncing-server/src/Domain/RemovedGroupUser/Service/RemovedGroupUserService.ts b/packages/syncing-server/src/Domain/RemovedGroupUser/Service/RemovedGroupUserService.ts new file mode 100644 index 000000000..7172da6c1 --- /dev/null +++ b/packages/syncing-server/src/Domain/RemovedGroupUser/Service/RemovedGroupUserService.ts @@ -0,0 +1,58 @@ +import { TimerInterface } from '@standardnotes/time' +import { RemovedGroupUser } from '../Model/RemovedGroupUser' +import { RemovedGroupUserFactoryInterface } from '../Factory/RemovedGroupUserFactoryInterface' +import { v4 as uuidv4 } from 'uuid' +import { RemovedGroupUserRepositoryInterface } from '../Repository/RemovedGroupUserRepositoryInterface' +import { RemovedGroupUserServiceInterface } from './RemovedGroupUserServiceInterface' +import { GetRemovedGroupUsersDTO } from './GetRemovedGroupUsersDTO' +import { GroupsRepositoryInterface } from '../../Group/Repository/GroupRepositoryInterface' + +export class RemovedGroupUserService implements RemovedGroupUserServiceInterface { + constructor( + private groupRepository: GroupsRepositoryInterface, + private removedGroupUserRepository: RemovedGroupUserRepositoryInterface, + private removedGroupUserFactory: RemovedGroupUserFactoryInterface, + private timer: TimerInterface, + ) {} + + async addRemovedGroupUser(dto: { + groupUuid: string + userUuid: string + removedBy: string + }): Promise { + const group = await this.groupRepository.findByUuid(dto.groupUuid) + if (!group) { + return null + } + + const timestamp = this.timer.getTimestampInMicroseconds() + const removedGroupUser = this.removedGroupUserFactory.create({ + uuid: uuidv4(), + user_uuid: dto.userUuid, + group_uuid: dto.groupUuid, + removed_by: dto.removedBy, + created_at_timestamp: timestamp, + updated_at_timestamp: timestamp, + }) + + return this.removedGroupUserRepository.create(removedGroupUser) + } + + getAllRemovedGroupUsersForUser(dto: GetRemovedGroupUsersDTO): Promise { + return this.removedGroupUserRepository.findAllForUser(dto) + } + + async deleteRemovedGroupUser(dto: { groupUuid: string; userUuid: string }): Promise { + const removedGroupUser = await this.removedGroupUserRepository.findByUserUuidAndGroupUuid( + dto.userUuid, + dto.groupUuid, + ) + if (!removedGroupUser) { + return false + } + + await this.removedGroupUserRepository.remove(removedGroupUser) + + return true + } +} diff --git a/packages/syncing-server/src/Domain/RemovedGroupUser/Service/RemovedGroupUserServiceInterface.ts b/packages/syncing-server/src/Domain/RemovedGroupUser/Service/RemovedGroupUserServiceInterface.ts new file mode 100644 index 000000000..bb55f364d --- /dev/null +++ b/packages/syncing-server/src/Domain/RemovedGroupUser/Service/RemovedGroupUserServiceInterface.ts @@ -0,0 +1,10 @@ +import { RemovedGroupUser } from '../Model/RemovedGroupUser' +import { GetRemovedGroupUsersDTO } from './GetRemovedGroupUsersDTO' + +export interface RemovedGroupUserServiceInterface { + addRemovedGroupUser(dto: { groupUuid: string; userUuid: string; removedBy: string }): Promise + + getAllRemovedGroupUsersForUser(dto: GetRemovedGroupUsersDTO): Promise + + deleteRemovedGroupUser(dto: { groupUuid: string; userUuid: string }): Promise +} diff --git a/packages/syncing-server/src/Domain/RemovedVaultUser/Factory/RemovedVaultUserFactory.ts b/packages/syncing-server/src/Domain/RemovedVaultUser/Factory/RemovedVaultUserFactory.ts deleted file mode 100644 index a1a2f2d24..000000000 --- a/packages/syncing-server/src/Domain/RemovedVaultUser/Factory/RemovedVaultUserFactory.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { TimerInterface } from '@standardnotes/time' -import { RemovedVaultUserFactoryInterface } from './RemovedVaultUserFactoryInterface' -import { RemovedVaultUserHash } from './RemovedVaultUserHash' -import { RemovedVaultUser } from '../Model/RemovedVaultUser' - -export class RemovedVaultUserFactory implements RemovedVaultUserFactoryInterface { - constructor(private timer: TimerInterface) {} - - create(dto: RemovedVaultUserHash): RemovedVaultUser { - const newRemovedVaultUser = new RemovedVaultUser() - newRemovedVaultUser.uuid = dto.uuid - newRemovedVaultUser.userUuid = dto.user_uuid - newRemovedVaultUser.vaultUuid = dto.vault_uuid - newRemovedVaultUser.removedBy = dto.removed_by - - const now = this.timer.getTimestampInMicroseconds() - newRemovedVaultUser.updatedAtTimestamp = now - newRemovedVaultUser.createdAtTimestamp = now - - if (dto.created_at_timestamp) { - newRemovedVaultUser.createdAtTimestamp = dto.created_at_timestamp - } - - return newRemovedVaultUser - } - - createStub(dto: RemovedVaultUserHash): RemovedVaultUser { - const item = this.create(dto) - return item - } -} diff --git a/packages/syncing-server/src/Domain/RemovedVaultUser/Factory/RemovedVaultUserFactoryInterface.ts b/packages/syncing-server/src/Domain/RemovedVaultUser/Factory/RemovedVaultUserFactoryInterface.ts deleted file mode 100644 index 7d3348156..000000000 --- a/packages/syncing-server/src/Domain/RemovedVaultUser/Factory/RemovedVaultUserFactoryInterface.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { RemovedVaultUser } from '../Model/RemovedVaultUser' -import { RemovedVaultUserHash } from './RemovedVaultUserHash' - -export interface RemovedVaultUserFactoryInterface { - create(dto: RemovedVaultUserHash): RemovedVaultUser - createStub(dto: RemovedVaultUserHash): RemovedVaultUser -} diff --git a/packages/syncing-server/src/Domain/RemovedVaultUser/Repository/RemovedVaultUserRepositoryInterface.ts b/packages/syncing-server/src/Domain/RemovedVaultUser/Repository/RemovedVaultUserRepositoryInterface.ts deleted file mode 100644 index de4625f00..000000000 --- a/packages/syncing-server/src/Domain/RemovedVaultUser/Repository/RemovedVaultUserRepositoryInterface.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { RemovedVaultUser } from '../Model/RemovedVaultUser' - -export type RemovedVaultUserFindAllForUserQuery = { - userUuid: string -} - -export interface RemovedVaultUserRepositoryInterface { - create(vault: RemovedVaultUser): Promise - remove(vault: RemovedVaultUser): Promise - findAllForUser(query: RemovedVaultUserFindAllForUserQuery): Promise - findByUserUuidAndVaultUuid(userUuid: string, vaultUuid: string): Promise -} diff --git a/packages/syncing-server/src/Domain/RemovedVaultUser/Repository/TypeORMRemovedVaultUserRepository.ts b/packages/syncing-server/src/Domain/RemovedVaultUser/Repository/TypeORMRemovedVaultUserRepository.ts deleted file mode 100644 index 9eb053f91..000000000 --- a/packages/syncing-server/src/Domain/RemovedVaultUser/Repository/TypeORMRemovedVaultUserRepository.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { RemovedVaultUser } from '../Model/RemovedVaultUser' -import { Repository, SelectQueryBuilder } from 'typeorm' -import { - RemovedVaultUserFindAllForUserQuery, - RemovedVaultUserRepositoryInterface, -} from './RemovedVaultUserRepositoryInterface' - -export class TypeORMRemovedVaultUserRepository implements RemovedVaultUserRepositoryInterface { - constructor(private ormRepository: Repository) {} - - async create(removedVaultUser: RemovedVaultUser): Promise { - return this.ormRepository.save(removedVaultUser) - } - - findByUserUuidAndVaultUuid(userUuid: string, vaultUuid: string): Promise { - return this.ormRepository - .createQueryBuilder('removed_vault_user') - .where('removed_vault_user.user_uuid = :userUuid', { userUuid }) - .andWhere('removed_vault_user.vault_uuid = :vaultUuid', { vaultUuid }) - .getOne() - } - - async remove(removedVaultUser: RemovedVaultUser): Promise { - return this.ormRepository.remove(removedVaultUser) - } - - async findAllForUser(query: RemovedVaultUserFindAllForUserQuery): Promise { - return this.createFindAllQueryBuilder(query).getMany() - } - - private createFindAllQueryBuilder(query: RemovedVaultUserFindAllForUserQuery): SelectQueryBuilder { - const queryBuilder = this.ormRepository.createQueryBuilder('removed_vault_user') - - queryBuilder.where('removed_vault_user.user_uuid = :userUuid', { userUuid: query.userUuid }) - - return queryBuilder - } -} diff --git a/packages/syncing-server/src/Domain/RemovedVaultUser/Service/GetRemovedVaultUsersDTO.ts b/packages/syncing-server/src/Domain/RemovedVaultUser/Service/GetRemovedVaultUsersDTO.ts deleted file mode 100644 index b9db5dd2f..000000000 --- a/packages/syncing-server/src/Domain/RemovedVaultUser/Service/GetRemovedVaultUsersDTO.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface GetRemovedVaultUsersDTO { - userUuid: string -} diff --git a/packages/syncing-server/src/Domain/RemovedVaultUser/Service/RemovedVaultUserService.ts b/packages/syncing-server/src/Domain/RemovedVaultUser/Service/RemovedVaultUserService.ts deleted file mode 100644 index 33134fc7d..000000000 --- a/packages/syncing-server/src/Domain/RemovedVaultUser/Service/RemovedVaultUserService.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { TimerInterface } from '@standardnotes/time' -import { RemovedVaultUser } from '../Model/RemovedVaultUser' -import { RemovedVaultUserFactoryInterface } from '../Factory/RemovedVaultUserFactoryInterface' -import { v4 as uuidv4 } from 'uuid' -import { RemovedVaultUserRepositoryInterface } from '../Repository/RemovedVaultUserRepositoryInterface' -import { RemovedVaultUserServiceInterface } from './RemovedVaultUserServiceInterface' -import { GetRemovedVaultUsersDTO } from './GetRemovedVaultUsersDTO' -import { VaultsRepositoryInterface } from '../../Vault/Repository/VaultRepositoryInterface' - -export class RemovedVaultUserService implements RemovedVaultUserServiceInterface { - constructor( - private vaultRepository: VaultsRepositoryInterface, - private removedVaultUserRepository: RemovedVaultUserRepositoryInterface, - private removedVaultUserFactory: RemovedVaultUserFactoryInterface, - private timer: TimerInterface, - ) {} - - async addRemovedVaultUser(dto: { - vaultUuid: string - userUuid: string - removedBy: string - }): Promise { - const vault = await this.vaultRepository.findByUuid(dto.vaultUuid) - if (!vault) { - return null - } - - const timestamp = this.timer.getTimestampInMicroseconds() - const removedVaultUser = this.removedVaultUserFactory.create({ - uuid: uuidv4(), - user_uuid: dto.userUuid, - vault_uuid: dto.vaultUuid, - removed_by: dto.removedBy, - created_at_timestamp: timestamp, - updated_at_timestamp: timestamp, - }) - - return this.removedVaultUserRepository.create(removedVaultUser) - } - - getAllRemovedVaultUsersForUser(dto: GetRemovedVaultUsersDTO): Promise { - return this.removedVaultUserRepository.findAllForUser(dto) - } - - async deleteRemovedVaultUser(dto: { vaultUuid: string; userUuid: string }): Promise { - const removedVaultUser = await this.removedVaultUserRepository.findByUserUuidAndVaultUuid( - dto.userUuid, - dto.vaultUuid, - ) - if (!removedVaultUser) { - return false - } - - await this.removedVaultUserRepository.remove(removedVaultUser) - - return true - } -} diff --git a/packages/syncing-server/src/Domain/RemovedVaultUser/Service/RemovedVaultUserServiceInterface.ts b/packages/syncing-server/src/Domain/RemovedVaultUser/Service/RemovedVaultUserServiceInterface.ts deleted file mode 100644 index 4d44034ae..000000000 --- a/packages/syncing-server/src/Domain/RemovedVaultUser/Service/RemovedVaultUserServiceInterface.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { RemovedVaultUser } from '../Model/RemovedVaultUser' -import { GetRemovedVaultUsersDTO } from './GetRemovedVaultUsersDTO' - -export interface RemovedVaultUserServiceInterface { - addRemovedVaultUser(dto: { vaultUuid: string; userUuid: string; removedBy: string }): Promise - - getAllRemovedVaultUsersForUser(dto: GetRemovedVaultUsersDTO): Promise - - deleteRemovedVaultUser(dto: { vaultUuid: string; userUuid: string }): Promise -} diff --git a/packages/syncing-server/src/Domain/UseCase/CreateGroupFileValetToken/CreateGroupFileValetToken.ts b/packages/syncing-server/src/Domain/UseCase/CreateGroupFileValetToken/CreateGroupFileValetToken.ts new file mode 100644 index 000000000..9639cb1df --- /dev/null +++ b/packages/syncing-server/src/Domain/UseCase/CreateGroupFileValetToken/CreateGroupFileValetToken.ts @@ -0,0 +1,104 @@ +import { inject, injectable } from 'inversify' +import { GroupValetTokenData, TokenEncoderInterface, ValetTokenOperation } from '@standardnotes/security' +import { CreateValetTokenResponseData } from '@standardnotes/responses' +import TYPES from '../../../Bootstrap/Types' +import { UseCaseInterface } from '../UseCaseInterface' +import { CreateGroupFileValetTokenDTO } from './CreateGroupFileValetTokenDTO' +import { GroupUserServiceInterface } from '../../GroupUser/Service/GroupUserServiceInterface' +import { GroupServiceInterface } from '../../Group/Service/GroupServiceInterface' + +@injectable() +export class CreateGroupFileValetToken implements UseCaseInterface { + constructor( + @inject(TYPES.GroupService) private groupService: GroupServiceInterface, + @inject(TYPES.GroupUserService) private groupUserService: GroupUserServiceInterface, + @inject(TYPES.ValetTokenEncoder) private tokenEncoder: TokenEncoderInterface, + @inject(TYPES.VALET_TOKEN_TTL) private valetTokenTTL: number, + ) {} + + async execute(dto: CreateGroupFileValetTokenDTO): Promise { + const failValue: CreateValetTokenResponseData = { + success: false, + reason: 'invalid-parameters', + } + + const groupUser = await this.groupUserService.getUserForGroup({ + userUuid: dto.userUuid, + groupUuid: dto.groupUuid, + }) + if (!groupUser) { + return failValue + } + if (groupUser.permissions === 'read' && dto.operation !== 'read') { + return failValue + } + + const group = await this.groupService.getGroup({ + groupUuid: dto.groupUuid, + }) + if (!group) { + return failValue + } + + if (dto.operation === ValetTokenOperation.Move) { + if (!dto.moveOperationType) { + return failValue + } + + if (dto.moveOperationType === 'group-to-group') { + if (!dto.groupToGroupMoveTargetUuid) { + return failValue + } + + const toGroupUser = await this.groupUserService.getUserForGroup({ + userUuid: dto.userUuid, + groupUuid: dto.groupToGroupMoveTargetUuid, + }) + + if (!toGroupUser) { + return failValue + } + + if (toGroupUser.permissions === 'read') { + return failValue + } + } + } + + const moveOperation: GroupValetTokenData['moveOperation'] = !dto.moveOperationType + ? undefined + : { + type: dto.moveOperationType, + fromUuid: + dto.moveOperationType === 'group-to-user' + ? dto.groupUuid + : dto.moveOperationType === 'user-to-group' + ? dto.userUuid + : dto.moveOperationType === 'group-to-group' + ? dto.groupUuid + : 'invalid-uuid', + toUuid: + dto.moveOperationType === 'group-to-user' + ? dto.userUuid + : dto.moveOperationType === 'user-to-group' + ? dto.groupUuid + : dto.moveOperationType === 'group-to-group' + ? (dto.groupToGroupMoveTargetUuid as string) + : 'invalid-uuid', + } + + const tokenData: GroupValetTokenData = { + groupUuid: dto.groupUuid, + permittedOperation: dto.operation, + remoteIdentifier: dto.remoteIdentifier, + uploadBytesUsed: group.fileUploadBytesUsed, + uploadBytesLimit: group.fileUploadBytesLimit, + unencryptedFileSize: dto.unencryptedFileSize, + moveOperation: moveOperation, + } + + const valetToken = this.tokenEncoder.encodeExpirableToken(tokenData, this.valetTokenTTL) + + return { success: true, valetToken } + } +} diff --git a/packages/syncing-server/src/Domain/UseCase/CreateGroupFileValetToken/CreateGroupFileValetTokenDTO.ts b/packages/syncing-server/src/Domain/UseCase/CreateGroupFileValetToken/CreateGroupFileValetTokenDTO.ts new file mode 100644 index 000000000..3ff84565c --- /dev/null +++ b/packages/syncing-server/src/Domain/UseCase/CreateGroupFileValetToken/CreateGroupFileValetTokenDTO.ts @@ -0,0 +1,12 @@ +import { ValetTokenOperation, GroupMoveType } from '@standardnotes/security' + +export type CreateGroupFileValetTokenDTO = { + userUuid: string + groupUuid: string + fileUuid?: string + remoteIdentifier: string + operation: ValetTokenOperation + unencryptedFileSize?: number + moveOperationType?: GroupMoveType + groupToGroupMoveTargetUuid?: string +} diff --git a/packages/syncing-server/src/Domain/UseCase/CreateVaultFileValetToken/CreateVaultFileValetToken.ts b/packages/syncing-server/src/Domain/UseCase/CreateVaultFileValetToken/CreateVaultFileValetToken.ts deleted file mode 100644 index 70e3e192d..000000000 --- a/packages/syncing-server/src/Domain/UseCase/CreateVaultFileValetToken/CreateVaultFileValetToken.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { inject, injectable } from 'inversify' -import { VaultValetTokenData, TokenEncoderInterface, ValetTokenOperation } from '@standardnotes/security' -import { CreateValetTokenResponseData } from '@standardnotes/responses' -import TYPES from '../../../Bootstrap/Types' -import { UseCaseInterface } from '../UseCaseInterface' -import { CreateVaultFileValetTokenDTO } from './CreateVaultFileValetTokenDTO' -import { VaultUserServiceInterface } from '../../VaultUser/Service/VaultUserServiceInterface' -import { VaultServiceInterface } from '../../Vault/Service/VaultServiceInterface' - -@injectable() -export class CreateVaultFileValetToken implements UseCaseInterface { - constructor( - @inject(TYPES.VaultService) private vaultService: VaultServiceInterface, - @inject(TYPES.VaultUserService) private vaultUserService: VaultUserServiceInterface, - @inject(TYPES.ValetTokenEncoder) private tokenEncoder: TokenEncoderInterface, - @inject(TYPES.VALET_TOKEN_TTL) private valetTokenTTL: number, - ) {} - - async execute(dto: CreateVaultFileValetTokenDTO): Promise { - const failValue: CreateValetTokenResponseData = { - success: false, - reason: 'invalid-parameters', - } - - const vaultUser = await this.vaultUserService.getUserForVault({ - userUuid: dto.userUuid, - vaultUuid: dto.vaultUuid, - }) - if (!vaultUser) { - return failValue - } - if (vaultUser.permissions === 'read' && dto.operation !== 'read') { - return failValue - } - - const vault = await this.vaultService.getVault({ - vaultUuid: dto.vaultUuid, - }) - if (!vault) { - return failValue - } - - if (dto.operation === ValetTokenOperation.Move) { - if (!dto.moveOperationType) { - return failValue - } - - if (dto.moveOperationType === 'vault-to-vault') { - if (!dto.vaultToVaultMoveTargetUuid) { - return failValue - } - - const toVaultUser = await this.vaultUserService.getUserForVault({ - userUuid: dto.userUuid, - vaultUuid: dto.vaultToVaultMoveTargetUuid, - }) - - if (!toVaultUser) { - return failValue - } - - if (toVaultUser.permissions === 'read') { - return failValue - } - } - } - - const moveOperation: VaultValetTokenData['moveOperation'] = !dto.moveOperationType - ? undefined - : { - type: dto.moveOperationType, - fromUuid: - dto.moveOperationType === 'vault-to-user' - ? dto.vaultUuid - : dto.moveOperationType === 'user-to-vault' - ? dto.userUuid - : dto.moveOperationType === 'vault-to-vault' - ? dto.vaultUuid - : 'invalid-uuid', - toUuid: - dto.moveOperationType === 'vault-to-user' - ? dto.userUuid - : dto.moveOperationType === 'user-to-vault' - ? dto.vaultUuid - : dto.moveOperationType === 'vault-to-vault' - ? (dto.vaultToVaultMoveTargetUuid as string) - : 'invalid-uuid', - } - - const tokenData: VaultValetTokenData = { - vaultUuid: dto.vaultUuid, - permittedOperation: dto.operation, - remoteIdentifier: dto.remoteIdentifier, - uploadBytesUsed: vault.fileUploadBytesUsed, - uploadBytesLimit: vault.fileUploadBytesLimit, - unencryptedFileSize: dto.unencryptedFileSize, - moveOperation: moveOperation, - } - - const valetToken = this.tokenEncoder.encodeExpirableToken(tokenData, this.valetTokenTTL) - - return { success: true, valetToken } - } -} diff --git a/packages/syncing-server/src/Domain/UseCase/CreateVaultFileValetToken/CreateVaultFileValetTokenDTO.ts b/packages/syncing-server/src/Domain/UseCase/CreateVaultFileValetToken/CreateVaultFileValetTokenDTO.ts deleted file mode 100644 index 4a627fa25..000000000 --- a/packages/syncing-server/src/Domain/UseCase/CreateVaultFileValetToken/CreateVaultFileValetTokenDTO.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { ValetTokenOperation, VaultMoveType } from '@standardnotes/security' - -export type CreateVaultFileValetTokenDTO = { - userUuid: string - vaultUuid: string - fileUuid?: string - remoteIdentifier: string - operation: ValetTokenOperation - unencryptedFileSize?: number - moveOperationType?: VaultMoveType - vaultToVaultMoveTargetUuid?: string -} diff --git a/packages/syncing-server/src/Domain/UseCase/SyncItems.ts b/packages/syncing-server/src/Domain/UseCase/SyncItems.ts index ea2fa52c0..eee8ee566 100644 --- a/packages/syncing-server/src/Domain/UseCase/SyncItems.ts +++ b/packages/syncing-server/src/Domain/UseCase/SyncItems.ts @@ -5,14 +5,14 @@ import { ItemServiceInterface } from '../Item/ItemServiceInterface' import { SyncItemsDTO } from './SyncItemsDTO' import { SyncItemsResponse } from './SyncItemsResponse' import { UseCaseInterface } from './UseCaseInterface' -import { VaultInviteServiceInterface } from '../VaultInvite/Service/VaultInviteServiceInterface' -import { VaultServiceInterface } from '../Vault/Service/VaultServiceInterface' +import { GroupInviteServiceInterface } from '../GroupInvite/Service/GroupInviteServiceInterface' +import { GroupServiceInterface } from '../Group/Service/GroupServiceInterface' export class SyncItems implements UseCaseInterface { constructor( private itemService: ItemServiceInterface, - private vaultService: VaultServiceInterface, - private vaultInviteService: VaultInviteServiceInterface, + private groupService: GroupServiceInterface, + private groupInviteService: GroupInviteServiceInterface, private contactService: ContactServiceInterface, ) {} @@ -20,7 +20,7 @@ export class SyncItems implements UseCaseInterface { const getItemsResult = await this.itemService.getItems({ userUuid: dto.userUuid, syncToken: dto.syncToken, - vaultUuids: dto.vaultUuids, + groupUuids: dto.groupUuids, cursorToken: dto.cursorToken, limit: dto.limit, contentType: dto.contentType, @@ -36,8 +36,8 @@ export class SyncItems implements UseCaseInterface { }) let retrievedItems = this.filterOutSyncConflictsForConsecutiveSyncs(getItemsResult.items, saveItemsResult.conflicts) - const isVaultExclusiveSync = dto.vaultUuids && dto.vaultUuids.length > 0 - if (this.isFirstSync(dto) && !isVaultExclusiveSync) { + const isGroupExclusiveSync = dto.groupUuids && dto.groupUuids.length > 0 + if (this.isFirstSync(dto) && !isGroupExclusiveSync) { retrievedItems = await this.itemService.frontLoadKeysItemsToTop(dto.userUuid, retrievedItems) } @@ -46,12 +46,12 @@ export class SyncItems implements UseCaseInterface { cursorToken: dto.cursorToken, }) - const vaults = await this.vaultService.getVaults({ + const groups = await this.groupService.getGroups({ userUuid: dto.userUuid, lastSyncTime, }) - const vaultInvites = await this.vaultInviteService.getInvitesForUser({ + const groupInvites = await this.groupInviteService.getInvitesForUser({ userUuid: dto.userUuid, lastSyncTime, }) @@ -67,8 +67,8 @@ export class SyncItems implements UseCaseInterface { savedItems: saveItemsResult.savedItems, conflicts: saveItemsResult.conflicts, cursorToken: getItemsResult.cursorToken, - vaults, - vaultInvites, + groups, + groupInvites, contacts, } diff --git a/packages/syncing-server/src/Domain/UseCase/SyncItemsDTO.ts b/packages/syncing-server/src/Domain/UseCase/SyncItemsDTO.ts index 8bf37fd01..e3dd17d47 100644 --- a/packages/syncing-server/src/Domain/UseCase/SyncItemsDTO.ts +++ b/packages/syncing-server/src/Domain/UseCase/SyncItemsDTO.ts @@ -5,7 +5,7 @@ export type SyncItemsDTO = { itemHashes: Array computeIntegrityHash: boolean limit: number - vaultUuids?: string[] | null + groupUuids?: string[] | null syncToken?: string | null cursorToken?: string | null contentType?: string diff --git a/packages/syncing-server/src/Domain/UseCase/SyncItemsResponse.ts b/packages/syncing-server/src/Domain/UseCase/SyncItemsResponse.ts index eb1dbb73c..31ddf4e73 100644 --- a/packages/syncing-server/src/Domain/UseCase/SyncItemsResponse.ts +++ b/packages/syncing-server/src/Domain/UseCase/SyncItemsResponse.ts @@ -1,15 +1,15 @@ -import { VaultInvite } from '../VaultInvite/Model/VaultInvite' +import { GroupInvite } from '../GroupInvite/Model/GroupInvite' import { Contact } from '../Contact/Model/Contact' import { Item } from '../Item/Item' import { ItemConflict } from '../Item/ItemConflict' -import { Vault } from '../Vault/Model/Vault' +import { Group } from '../Group/Model/Group' export type SyncItemsResponse = { retrievedItems: Array savedItems: Array conflicts: Array - vaults: Array - vaultInvites: Array + groups: Array + groupInvites: Array contacts: Array syncToken: string cursorToken?: string diff --git a/packages/syncing-server/src/Domain/Vault/Factory/VaultFactory.ts b/packages/syncing-server/src/Domain/Vault/Factory/VaultFactory.ts deleted file mode 100644 index b488a2af0..000000000 --- a/packages/syncing-server/src/Domain/Vault/Factory/VaultFactory.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { TimerInterface } from '@standardnotes/time' -import { VaultFactoryInterface } from './VaultFactoryInterface' -import { VaultHash } from './VaultHash' -import { Vault } from '../Model/Vault' - -export class VaultFactory implements VaultFactoryInterface { - constructor(private timer: TimerInterface) {} - - create(dto: { userUuid: string; vaultHash: VaultHash }): Vault { - const newVault = new Vault() - newVault.uuid = dto.vaultHash.uuid - newVault.userUuid = dto.userUuid - newVault.specifiedItemsKeyUuid = dto.vaultHash.specified_items_key_uuid - newVault.fileUploadBytesUsed = dto.vaultHash.file_upload_bytes_used - newVault.fileUploadBytesLimit = dto.vaultHash.file_upload_bytes_limit - - const now = this.timer.getTimestampInMicroseconds() - newVault.updatedAtTimestamp = now - newVault.createdAtTimestamp = now - - if (dto.vaultHash.created_at_timestamp) { - newVault.createdAtTimestamp = dto.vaultHash.created_at_timestamp - } - - return newVault - } - - createStub(dto: { userUuid: string; vaultHash: VaultHash }): Vault { - const item = this.create(dto) - return item - } -} diff --git a/packages/syncing-server/src/Domain/Vault/Factory/VaultFactoryInterface.ts b/packages/syncing-server/src/Domain/Vault/Factory/VaultFactoryInterface.ts deleted file mode 100644 index 06eeca20e..000000000 --- a/packages/syncing-server/src/Domain/Vault/Factory/VaultFactoryInterface.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Vault } from '../Model/Vault' -import { VaultHash } from './VaultHash' - -export interface VaultFactoryInterface { - create(dto: { userUuid: string; vaultHash: VaultHash }): Vault - createStub(dto: { userUuid: string; vaultHash: VaultHash }): Vault -} diff --git a/packages/syncing-server/src/Domain/Vault/Repository/TypeORMVaultRepository.ts b/packages/syncing-server/src/Domain/Vault/Repository/TypeORMVaultRepository.ts deleted file mode 100644 index c0ef9c36c..000000000 --- a/packages/syncing-server/src/Domain/Vault/Repository/TypeORMVaultRepository.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Repository, SelectQueryBuilder } from 'typeorm' -import { VaultsRepositoryInterface, UserVaultsQuery } from './VaultRepositoryInterface' -import { Vault } from '../Model/Vault' - -export class TypeORMVaultRepository implements VaultsRepositoryInterface { - constructor(private ormRepository: Repository) {} - - async create(vault: Vault): Promise { - return this.ormRepository.save(vault) - } - - async save(vault: Vault): Promise { - return this.ormRepository.save(vault) - } - - findByUuid(uuid: string): Promise { - return this.ormRepository - .createQueryBuilder('vault') - .where('vault.uuid = :uuid', { - uuid, - }) - .getOne() - } - - async remove(vault: Vault): Promise { - return this.ormRepository.remove(vault) - } - - async findAll(query: UserVaultsQuery): Promise { - return this.createFindAllQueryBuilder(query).getMany() - } - - private createFindAllQueryBuilder(query: UserVaultsQuery): SelectQueryBuilder { - const queryBuilder = this.ormRepository.createQueryBuilder('vault') - - if (!query.userUuid && !query.vaultUuids) { - throw new Error('Either userUuid or vaultUuids must be provided') - } - - if (query.userUuid) { - queryBuilder.where('vault.user_uuid = :userUuid', { userUuid: query.userUuid }) - } - - if (query.vaultUuids) { - queryBuilder.where('vault.uuid IN (:...vaultUuids)', { vaultUuids: query.vaultUuids }) - } - - if (query.lastSyncTime) { - queryBuilder.andWhere('vault.updated_at_timestamp > :lastSyncTime', { - lastSyncTime: query.lastSyncTime, - }) - } - - return queryBuilder - } -} diff --git a/packages/syncing-server/src/Domain/Vault/Repository/VaultRepositoryInterface.ts b/packages/syncing-server/src/Domain/Vault/Repository/VaultRepositoryInterface.ts deleted file mode 100644 index 7cc28973b..000000000 --- a/packages/syncing-server/src/Domain/Vault/Repository/VaultRepositoryInterface.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Vault } from '../Model/Vault' - -export type VaultQuery = { - userUuid: string -} - -export type UserVaultsQuery = { - userUuid?: string - vaultUuids?: string[] - lastSyncTime?: number -} - -export interface VaultsRepositoryInterface { - findByUuid(uuid: string): Promise - create(vault: Vault): Promise - save(vault: Vault): Promise - remove(vault: Vault): Promise - findAll(query: UserVaultsQuery): Promise -} diff --git a/packages/syncing-server/src/Domain/Vault/Service/VaultService.ts b/packages/syncing-server/src/Domain/Vault/Service/VaultService.ts deleted file mode 100644 index 50af510da..000000000 --- a/packages/syncing-server/src/Domain/Vault/Service/VaultService.ts +++ /dev/null @@ -1,101 +0,0 @@ -import { TimerInterface } from '@standardnotes/time' -import { Vault } from '../Model/Vault' -import { VaultsRepositoryInterface } from '../Repository/VaultRepositoryInterface' -import { CreateVaultDTO, VaultServiceInterface, UpdateVaultDTO, CreateVaultResult } from './VaultServiceInterface' -import { VaultFactoryInterface } from '../Factory/VaultFactoryInterface' -import { VaultUserServiceInterface } from '../../VaultUser/Service/VaultUserServiceInterface' -import { VaultInviteServiceInterface } from '../../VaultInvite/Service/VaultInviteServiceInterface' - -export class VaultService implements VaultServiceInterface { - constructor( - private vaultRepository: VaultsRepositoryInterface, - private vaultFactory: VaultFactoryInterface, - private vaultUserService: VaultUserServiceInterface, - private vaultInviteService: VaultInviteServiceInterface, - private timer: TimerInterface, - ) {} - - async createVault(dto: CreateVaultDTO): Promise { - const existingVault = await this.vaultRepository.findByUuid(dto.vaultUuid) - if (existingVault) { - return null - } - - const timestamp = this.timer.getTimestampInMicroseconds() - const vault = this.vaultFactory.create({ - userUuid: dto.userUuid, - vaultHash: { - uuid: dto.vaultUuid, - user_uuid: dto.userUuid, - specified_items_key_uuid: dto.specifiedItemsKeyUuid, - file_upload_bytes_limit: 1_000_000, - file_upload_bytes_used: 0, - created_at_timestamp: timestamp, - updated_at_timestamp: timestamp, - }, - }) - - const savedVault = await this.vaultRepository.create(vault) - - const vaultUser = await this.vaultUserService.addVaultUser({ - vaultUuid: savedVault.uuid, - userUuid: dto.userUuid, - permissions: 'admin', - }) - - return { vault, vaultUser } - } - - async getVault(dto: { vaultUuid: string }): Promise { - const vault = await this.vaultRepository.findByUuid(dto.vaultUuid) - - return vault - } - - async getVaults(dto: { userUuid: string; lastSyncTime?: number }): Promise { - const vaultUsers = await this.vaultUserService.getAllVaultUsersForUser({ - userUuid: dto.userUuid, - }) - - const vaultUuids = vaultUsers.map((vaultUser) => vaultUser.vaultUuid) - - if (vaultUuids.length === 0) { - return [] - } - - return this.vaultRepository.findAll({ vaultUuids, lastSyncTime: dto.lastSyncTime }) - } - - async updateVault(dto: UpdateVaultDTO): Promise { - const vault = await this.vaultRepository.findByUuid(dto.vaultUuid) - if (!vault || vault.userUuid !== dto.originatorUuid) { - return null - } - - vault.specifiedItemsKeyUuid = dto.specifiedItemsKeyUuid - vault.updatedAtTimestamp = this.timer.getTimestampInMicroseconds() - - const savedVault = await this.vaultRepository.save(vault) - - return savedVault - } - - async deleteVault(dto: { vaultUuid: string; originatorUuid: string }): Promise { - const vault = await this.vaultRepository.findByUuid(dto.vaultUuid) - if (!vault || vault.userUuid !== dto.originatorUuid) { - return false - } - - await this.vaultRepository.remove(vault) - await this.vaultUserService.deleteAllVaultUsersForVault({ - vaultUuid: dto.vaultUuid, - originatorUuid: dto.originatorUuid, - }) - await this.vaultInviteService.deleteAllInvitesForVault({ - vaultUuid: dto.vaultUuid, - originatorUuid: dto.originatorUuid, - }) - - return true - } -} diff --git a/packages/syncing-server/src/Domain/Vault/Service/VaultServiceInterface.ts b/packages/syncing-server/src/Domain/Vault/Service/VaultServiceInterface.ts deleted file mode 100644 index 8b9e0c3a5..000000000 --- a/packages/syncing-server/src/Domain/Vault/Service/VaultServiceInterface.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { VaultUser } from './../../VaultUser/Model/VaultUser' -import { Vault } from '../Model/Vault' - -export type CreateVaultDTO = { - userUuid: string - vaultUuid: string - specifiedItemsKeyUuid: string -} - -export type CreateVaultResult = { - vault: Vault - vaultUser: VaultUser -} - -export type UpdateVaultDTO = { - originatorUuid: string - vaultUuid: string - specifiedItemsKeyUuid: string -} - -export interface VaultServiceInterface { - createVault(dto: CreateVaultDTO): Promise - - updateVault(dto: UpdateVaultDTO): Promise - - deleteVault(dto: { vaultUuid: string; originatorUuid: string }): Promise - - getVault(dto: { vaultUuid: string }): Promise - - getVaults(dto: { userUuid: string; lastSyncTime?: number }): Promise -} diff --git a/packages/syncing-server/src/Domain/VaultInvite/Factory/VaultInviteFactory.ts b/packages/syncing-server/src/Domain/VaultInvite/Factory/VaultInviteFactory.ts deleted file mode 100644 index f73685967..000000000 --- a/packages/syncing-server/src/Domain/VaultInvite/Factory/VaultInviteFactory.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { TimerInterface } from '@standardnotes/time' -import { VaultInviteFactoryInterface } from './VaultInviteFactoryInterface' -import { VaultInviteHash } from './VaultInviteHash' -import { VaultInvite } from '../Model/VaultInvite' - -export class VaultInviteFactory implements VaultInviteFactoryInterface { - constructor(private timer: TimerInterface) {} - - create(dto: VaultInviteHash): VaultInvite { - const newVaultInvite = new VaultInvite() - newVaultInvite.uuid = dto.uuid - newVaultInvite.userUuid = dto.user_uuid - newVaultInvite.vaultUuid = dto.vault_uuid - newVaultInvite.inviterUuid = dto.inviter_uuid - newVaultInvite.inviterPublicKey = dto.inviter_public_key - newVaultInvite.encryptedVaultData = dto.encrypted_vault_data - newVaultInvite.inviteType = dto.invite_type - newVaultInvite.permissions = dto.permissions - - const now = this.timer.getTimestampInMicroseconds() - newVaultInvite.updatedAtTimestamp = now - newVaultInvite.createdAtTimestamp = now - - if (dto.created_at_timestamp) { - newVaultInvite.createdAtTimestamp = dto.created_at_timestamp - } - - return newVaultInvite - } - - createStub(dto: VaultInviteHash): VaultInvite { - const item = this.create(dto) - return item - } -} diff --git a/packages/syncing-server/src/Domain/VaultInvite/Factory/VaultInviteFactoryInterface.ts b/packages/syncing-server/src/Domain/VaultInvite/Factory/VaultInviteFactoryInterface.ts deleted file mode 100644 index c160b752d..000000000 --- a/packages/syncing-server/src/Domain/VaultInvite/Factory/VaultInviteFactoryInterface.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { VaultInvite } from '../Model/VaultInvite' -import { VaultInviteHash } from './VaultInviteHash' - -export interface VaultInviteFactoryInterface { - create(dto: VaultInviteHash): VaultInvite - createStub(dto: VaultInviteHash): VaultInvite -} diff --git a/packages/syncing-server/src/Domain/VaultInvite/Factory/VaultInviteHash.ts b/packages/syncing-server/src/Domain/VaultInvite/Factory/VaultInviteHash.ts deleted file mode 100644 index 27e3cec23..000000000 --- a/packages/syncing-server/src/Domain/VaultInvite/Factory/VaultInviteHash.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { VaultUserPermission } from '../../VaultUser/Model/VaultUserPermission' -import { VaultInviteType } from '../Model/VaultInviteType' - -export type VaultInviteHash = { - uuid: string - user_uuid: string - vault_uuid: string - inviter_uuid: string - inviter_public_key: string - encrypted_vault_data: string - invite_type: VaultInviteType - permissions: VaultUserPermission - created_at_timestamp?: number - updated_at_timestamp?: number -} diff --git a/packages/syncing-server/src/Domain/VaultInvite/Repository/TypeORMVaultInviteRepository.ts b/packages/syncing-server/src/Domain/VaultInvite/Repository/TypeORMVaultInviteRepository.ts deleted file mode 100644 index 2d231aea7..000000000 --- a/packages/syncing-server/src/Domain/VaultInvite/Repository/TypeORMVaultInviteRepository.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { VaultInvite } from '../Model/VaultInvite' -import { Repository, SelectQueryBuilder } from 'typeorm' -import { - VaultInviteFindAllForVault, - VaultInviteFindAllForUserQuery, - VaultInviteRepositoryInterface, -} from './VaultInviteRepositoryInterface' - -export class TypeORMVaultInviteRepository implements VaultInviteRepositoryInterface { - constructor(private ormRepository: Repository) {} - - async create(vaultInvite: VaultInvite): Promise { - return this.ormRepository.save(vaultInvite) - } - - async save(vaultInvite: VaultInvite): Promise { - return this.ormRepository.save(vaultInvite) - } - - findByUuid(uuid: string): Promise { - return this.ormRepository - .createQueryBuilder('vault_invite') - .where('vault_invite.uuid = :uuid', { - uuid, - }) - .getOne() - } - - findByUserUuidAndVaultUuid(userUuid: string, vaultUuid: string): Promise { - return this.ormRepository - .createQueryBuilder('vault_invite') - .where('vault_invite.user_uuid = :userUuid', { userUuid }) - .andWhere('vault_invite.vault_uuid = :vaultUuid', { vaultUuid }) - .getOne() - } - - async remove(vaultInvite: VaultInvite): Promise { - return this.ormRepository.remove(vaultInvite) - } - - findAllForVault(query: VaultInviteFindAllForVault): Promise { - return this.ormRepository - .createQueryBuilder('vault_invite') - .where('vault_invite.vault_uuid = :vaultUuid', { vaultUuid: query.vaultUuid }) - .getMany() - } - - async findAll(query: VaultInviteFindAllForUserQuery): Promise { - return this.createFindAllQueryBuilder(query).getMany() - } - - private createFindAllQueryBuilder(query: VaultInviteFindAllForUserQuery): SelectQueryBuilder { - const queryBuilder = this.ormRepository.createQueryBuilder('vault_invite') - - if (query.userUuid) { - queryBuilder.where('vault_invite.user_uuid = :userUuid', { userUuid: query.userUuid }) - } else if (query.inviterUuid) { - queryBuilder.where('vault_invite.inviter_uuid = :inviterUuid', { - inviterUuid: query.inviterUuid, - }) - } - - if (query.lastSyncTime) { - queryBuilder.andWhere('vault_invite.updated_at_timestamp > :lastSyncTime', { - lastSyncTime: query.lastSyncTime, - }) - } - - return queryBuilder - } -} diff --git a/packages/syncing-server/src/Domain/VaultInvite/Repository/VaultInviteRepositoryInterface.ts b/packages/syncing-server/src/Domain/VaultInvite/Repository/VaultInviteRepositoryInterface.ts deleted file mode 100644 index d96f5c9b1..000000000 --- a/packages/syncing-server/src/Domain/VaultInvite/Repository/VaultInviteRepositoryInterface.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { VaultInvite } from '../Model/VaultInvite' - -export type VaultInviteQuery = { - userUuid: string -} - -export type VaultInviteFindAllForUserQuery = { - userUuid?: string - lastSyncTime?: number - inviterUuid?: string -} - -export type VaultInviteFindAllForVault = { - vaultUuid: string -} - -export interface VaultInviteRepositoryInterface { - findByUuid(vaultInviteUuid: string): Promise - create(vault: VaultInvite): Promise - save(vaultInvite: VaultInvite): Promise - remove(vault: VaultInvite): Promise - findAll(query: VaultInviteFindAllForUserQuery): Promise - findAllForVault(query: VaultInviteFindAllForVault): Promise - findByUserUuidAndVaultUuid(userUuid: string, vaultUuid: string): Promise -} diff --git a/packages/syncing-server/src/Domain/VaultInvite/Service/CreateInviteDTO.ts b/packages/syncing-server/src/Domain/VaultInvite/Service/CreateInviteDTO.ts deleted file mode 100644 index 5515e31b3..000000000 --- a/packages/syncing-server/src/Domain/VaultInvite/Service/CreateInviteDTO.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { VaultInviteType } from '../Model/VaultInviteType' -import { VaultUserPermission } from '../../VaultUser/Model/VaultUserPermission' - -export type CreateInviteDTO = { - originatorUuid: string - vaultUuid: string - userUuid: string - inviterPublicKey: string - encryptedVaultData: string - inviteType: VaultInviteType - permissions: VaultUserPermission -} diff --git a/packages/syncing-server/src/Domain/VaultInvite/Service/UpdateInviteDTO.ts b/packages/syncing-server/src/Domain/VaultInvite/Service/UpdateInviteDTO.ts deleted file mode 100644 index bf65db8f2..000000000 --- a/packages/syncing-server/src/Domain/VaultInvite/Service/UpdateInviteDTO.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { VaultUserPermission } from '../../VaultUser/Model/VaultUserPermission' - -export type UpdateInviteDTO = { - originatorUuid: string - inviteUuid: string - inviterPublicKey: string - encryptedVaultData: string - permissions?: VaultUserPermission -} diff --git a/packages/syncing-server/src/Domain/VaultInvite/Service/VaultInviteService.ts b/packages/syncing-server/src/Domain/VaultInvite/Service/VaultInviteService.ts deleted file mode 100644 index b194d99e6..000000000 --- a/packages/syncing-server/src/Domain/VaultInvite/Service/VaultInviteService.ts +++ /dev/null @@ -1,172 +0,0 @@ -import { TimerInterface } from '@standardnotes/time' -import { VaultInvite } from '../Model/VaultInvite' -import { VaultInviteFactoryInterface } from '../Factory/VaultInviteFactoryInterface' -import { v4 as uuidv4 } from 'uuid' -import { VaultInviteRepositoryInterface } from '../Repository/VaultInviteRepositoryInterface' -import { VaultInviteServiceInterface } from './VaultInviteServiceInterface' -import { GetUserVaultInvitesDTO } from './GetUserVaultInvitesDTO' -import { VaultsRepositoryInterface } from '../../Vault/Repository/VaultRepositoryInterface' -import { VaultUserServiceInterface } from '../../VaultUser/Service/VaultUserServiceInterface' -import { VaultInviteType } from '../Model/VaultInviteType' -import { CreateInviteDTO } from './CreateInviteDTO' -import { UpdateInviteDTO } from './UpdateInviteDTO' - -export class VaultInviteService implements VaultInviteServiceInterface { - constructor( - private vaultRepository: VaultsRepositoryInterface, - private vaultInviteRepository: VaultInviteRepositoryInterface, - private vaultInviteFactory: VaultInviteFactoryInterface, - private vaultUserService: VaultUserServiceInterface, - private timer: TimerInterface, - ) {} - - async createInvite(dto: CreateInviteDTO): Promise { - const vault = await this.vaultRepository.findByUuid(dto.vaultUuid) - if (!vault || vault.userUuid !== dto.originatorUuid) { - return null - } - - const timestamp = this.timer.getTimestampInMicroseconds() - - const vaultInvite = this.vaultInviteFactory.create({ - uuid: uuidv4(), - user_uuid: dto.userUuid, - vault_uuid: dto.vaultUuid, - inviter_uuid: dto.originatorUuid, - inviter_public_key: dto.inviterPublicKey, - encrypted_vault_data: dto.encryptedVaultData, - invite_type: dto.inviteType, - permissions: dto.permissions, - created_at_timestamp: timestamp, - updated_at_timestamp: timestamp, - }) - - return this.vaultInviteRepository.create(vaultInvite) - } - - async updateInvite(dto: UpdateInviteDTO): Promise { - const vaultInvite = await this.vaultInviteRepository.findByUuid(dto.inviteUuid) - if (!vaultInvite || vaultInvite.inviterUuid !== dto.originatorUuid) { - return null - } - - vaultInvite.inviterPublicKey = dto.inviterPublicKey - vaultInvite.encryptedVaultData = dto.encryptedVaultData - if (dto.permissions) { - vaultInvite.permissions = dto.permissions - } - vaultInvite.updatedAtTimestamp = this.timer.getTimestampInMicroseconds() - - return this.vaultInviteRepository.save(vaultInvite) - } - - getInvitesForUser(dto: GetUserVaultInvitesDTO): Promise { - return this.vaultInviteRepository.findAll(dto) - } - - getOutboundInvitesForUser(dto: { userUuid: string }): Promise { - return this.vaultInviteRepository.findAll({ - inviterUuid: dto.userUuid, - }) - } - - async deleteAllInboundInvites(dto: { userUuid: string }): Promise { - const inboundInvites = await this.vaultInviteRepository.findAll({ - userUuid: dto.userUuid, - }) - - for (const invite of inboundInvites) { - await this.vaultInviteRepository.remove(invite) - } - } - - async getInvitesForVault(dto: { vaultUuid: string; originatorUuid: string }): Promise { - const vault = await this.vaultRepository.findByUuid(dto.vaultUuid) - if (!vault) { - return undefined - } - - const isUserVaultAdmin = vault && vault.userUuid === dto.originatorUuid - if (!isUserVaultAdmin) { - return undefined - } - - const users = await this.vaultInviteRepository.findAllForVault({ vaultUuid: dto.vaultUuid }) - - return users - } - - async acceptInvite(dto: { originatorUuid: string; inviteUuid: string }): Promise { - const invite = await this.vaultInviteRepository.findByUuid(dto.inviteUuid) - if (!invite) { - return false - } - - const isAuthorized = invite.userUuid === dto.originatorUuid - if (!isAuthorized) { - return false - } - - if (invite.inviteType === VaultInviteType.Join) { - const addedUser = await this.vaultUserService.addVaultUser({ - userUuid: dto.originatorUuid, - vaultUuid: invite.vaultUuid, - permissions: invite.permissions, - }) - - if (!addedUser) { - return false - } - } - - await this.vaultInviteRepository.remove(invite) - - return true - } - - async declineInvite(dto: { originatorUuid: string; inviteUuid: string }): Promise { - const invite = await this.vaultInviteRepository.findByUuid(dto.inviteUuid) - if (!invite) { - return false - } - - const isAuthorized = invite.userUuid === dto.originatorUuid - if (!isAuthorized) { - return false - } - - await this.vaultInviteRepository.remove(invite) - - return true - } - - async deleteInvite(dto: { originatorUuid: string; inviteUuid: string }): Promise { - const invite = await this.vaultInviteRepository.findByUuid(dto.inviteUuid) - if (!invite) { - return false - } - - const isAuthorized = invite.inviterUuid === dto.originatorUuid || invite.userUuid === dto.originatorUuid - if (!isAuthorized) { - return false - } - - await this.vaultInviteRepository.remove(invite) - - return true - } - - async deleteAllInvitesForVault(dto: { originatorUuid: string; vaultUuid: string }): Promise { - const vault = await this.vaultRepository.findByUuid(dto.vaultUuid) - if (!vault || vault.userUuid !== dto.originatorUuid) { - return false - } - - const vaultInvites = await this.vaultInviteRepository.findAllForVault({ vaultUuid: dto.vaultUuid }) - for (const vaultInvite of vaultInvites) { - await this.vaultInviteRepository.remove(vaultInvite) - } - - return true - } -} diff --git a/packages/syncing-server/src/Domain/VaultInvite/Service/VaultInviteServiceInterface.ts b/packages/syncing-server/src/Domain/VaultInvite/Service/VaultInviteServiceInterface.ts deleted file mode 100644 index f44a56a7b..000000000 --- a/packages/syncing-server/src/Domain/VaultInvite/Service/VaultInviteServiceInterface.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { VaultInvite } from '../Model/VaultInvite' -import { CreateInviteDTO } from './CreateInviteDTO' -import { GetUserVaultInvitesDTO } from './GetUserVaultInvitesDTO' -import { UpdateInviteDTO } from './UpdateInviteDTO' - -export interface VaultInviteServiceInterface { - createInvite(dto: CreateInviteDTO): Promise - - updateInvite(dto: UpdateInviteDTO): Promise - - getInvitesForUser(dto: GetUserVaultInvitesDTO): Promise - - getInvitesForVault(dto: { vaultUuid: string; originatorUuid: string }): Promise - - getOutboundInvitesForUser(dto: { userUuid: string }): Promise - - acceptInvite(dto: { originatorUuid: string; inviteUuid: string }): Promise - - declineInvite(dto: { originatorUuid: string; inviteUuid: string }): Promise - - deleteInvite(dto: { originatorUuid: string; vaultUuid: string; inviteUuid: string }): Promise - - deleteAllInvitesForVault(dto: { originatorUuid: string; vaultUuid: string }): Promise - - deleteAllInboundInvites(dto: { userUuid: string }): Promise -} diff --git a/packages/syncing-server/src/Domain/VaultUser/Factory/VaultUserFactory.ts b/packages/syncing-server/src/Domain/VaultUser/Factory/VaultUserFactory.ts deleted file mode 100644 index 226e962ba..000000000 --- a/packages/syncing-server/src/Domain/VaultUser/Factory/VaultUserFactory.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { TimerInterface } from '@standardnotes/time' -import { VaultUserFactoryInterface } from './VaultUserFactoryInterface' -import { VaultUserHash } from './VaultUserHash' -import { VaultUser } from '../Model/VaultUser' - -export class VaultUserFactory implements VaultUserFactoryInterface { - constructor(private timer: TimerInterface) {} - - create(dto: VaultUserHash): VaultUser { - const newVaultUser = new VaultUser() - newVaultUser.uuid = dto.uuid - newVaultUser.userUuid = dto.user_uuid - newVaultUser.vaultUuid = dto.vault_uuid - newVaultUser.permissions = dto.permissions - - const now = this.timer.getTimestampInMicroseconds() - newVaultUser.updatedAtTimestamp = now - newVaultUser.createdAtTimestamp = now - - if (dto.created_at_timestamp) { - newVaultUser.createdAtTimestamp = dto.created_at_timestamp - } - - return newVaultUser - } - - createStub(dto: VaultUserHash): VaultUser { - const item = this.create(dto) - return item - } -} diff --git a/packages/syncing-server/src/Domain/VaultUser/Factory/VaultUserFactoryInterface.ts b/packages/syncing-server/src/Domain/VaultUser/Factory/VaultUserFactoryInterface.ts deleted file mode 100644 index 823830403..000000000 --- a/packages/syncing-server/src/Domain/VaultUser/Factory/VaultUserFactoryInterface.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { VaultUser } from '../Model/VaultUser' -import { VaultUserHash } from './VaultUserHash' - -export interface VaultUserFactoryInterface { - create(dto: VaultUserHash): VaultUser - createStub(dto: VaultUserHash): VaultUser -} diff --git a/packages/syncing-server/src/Domain/VaultUser/Factory/VaultUserHash.ts b/packages/syncing-server/src/Domain/VaultUser/Factory/VaultUserHash.ts deleted file mode 100644 index d7988b332..000000000 --- a/packages/syncing-server/src/Domain/VaultUser/Factory/VaultUserHash.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { VaultUserPermission } from '../Model/VaultUserPermission' - -export type VaultUserHash = { - uuid: string - user_uuid: string - vault_uuid: string - permissions: VaultUserPermission - created_at_timestamp?: number - updated_at_timestamp?: number -} diff --git a/packages/syncing-server/src/Domain/VaultUser/Model/VaultUserPermission.ts b/packages/syncing-server/src/Domain/VaultUser/Model/VaultUserPermission.ts deleted file mode 100644 index 9d75368cd..000000000 --- a/packages/syncing-server/src/Domain/VaultUser/Model/VaultUserPermission.ts +++ /dev/null @@ -1 +0,0 @@ -export type VaultUserPermission = 'read' | 'write' | 'admin' diff --git a/packages/syncing-server/src/Domain/VaultUser/Repository/TypeORMVaultUserRepository.ts b/packages/syncing-server/src/Domain/VaultUser/Repository/TypeORMVaultUserRepository.ts deleted file mode 100644 index 3cb3c44a1..000000000 --- a/packages/syncing-server/src/Domain/VaultUser/Repository/TypeORMVaultUserRepository.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { VaultUser } from '../Model/VaultUser' -import { Repository, SelectQueryBuilder } from 'typeorm' -import { - VaultUserFindAllForVault, - VaultUserFindAllForUserQuery, - VaultUserRepositoryInterface, -} from './VaultUserRepositoryInterface' - -export class TypeORMVaultUserRepository implements VaultUserRepositoryInterface { - constructor(private ormRepository: Repository) {} - - async create(vaultUser: VaultUser): Promise { - return this.ormRepository.save(vaultUser) - } - - async save(vaultUser: VaultUser): Promise { - return this.ormRepository.save(vaultUser) - } - - findByUuid(uuid: string): Promise { - return this.ormRepository - .createQueryBuilder('vault_user') - .where('vault_user.uuid = :uuid', { - uuid, - }) - .getOne() - } - - findByUserUuidAndVaultUuid(userUuid: string, vaultUuid: string): Promise { - return this.ormRepository - .createQueryBuilder('vault_user') - .where('vault_user.user_uuid = :userUuid', { userUuid }) - .andWhere('vault_user.vault_uuid = :vaultUuid', { vaultUuid }) - .getOne() - } - - async remove(vaultUser: VaultUser): Promise { - return this.ormRepository.remove(vaultUser) - } - - findAllForVault(query: VaultUserFindAllForVault): Promise { - return this.ormRepository - .createQueryBuilder('vault_user') - .where('vault_user.vault_uuid = :vaultUuid', { vaultUuid: query.vaultUuid }) - .getMany() - } - - async findAllForUser(query: VaultUserFindAllForUserQuery): Promise { - return this.createFindAllQueryBuilder(query).getMany() - } - - private createFindAllQueryBuilder(query: VaultUserFindAllForUserQuery): SelectQueryBuilder { - const queryBuilder = this.ormRepository.createQueryBuilder('vault_user') - - queryBuilder.where('vault_user.user_uuid = :userUuid', { userUuid: query.userUuid }) - - if (query.lastSyncTime) { - queryBuilder.andWhere('vault_user.updated_at_timestamp > :lastSyncTime', { - lastSyncTime: query.lastSyncTime, - }) - } - - return queryBuilder - } -} diff --git a/packages/syncing-server/src/Domain/VaultUser/Repository/VaultUserRepositoryInterface.ts b/packages/syncing-server/src/Domain/VaultUser/Repository/VaultUserRepositoryInterface.ts deleted file mode 100644 index 943216b0a..000000000 --- a/packages/syncing-server/src/Domain/VaultUser/Repository/VaultUserRepositoryInterface.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { VaultUser } from '../Model/VaultUser' - -export type VaultUserFindAllForUserQuery = { - userUuid: string - lastSyncTime?: number -} - -export type VaultUserFindAllForVault = { - vaultUuid: string -} - -export interface VaultUserRepositoryInterface { - findByUuid(vaultUserUuid: string): Promise - create(vault: VaultUser): Promise - save(vaultUser: VaultUser): Promise - remove(vault: VaultUser): Promise - findAllForUser(query: VaultUserFindAllForUserQuery): Promise - findAllForVault(query: VaultUserFindAllForVault): Promise - findByUserUuidAndVaultUuid(userUuid: string, vaultUuid: string): Promise -} diff --git a/packages/syncing-server/src/Domain/VaultUser/Service/VaultUserService.ts b/packages/syncing-server/src/Domain/VaultUser/Service/VaultUserService.ts deleted file mode 100644 index 92ffdb813..000000000 --- a/packages/syncing-server/src/Domain/VaultUser/Service/VaultUserService.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { RemovedVaultUserServiceInterface } from './../../RemovedVaultUser/Service/RemovedVaultUserServiceInterface' -import { TimerInterface } from '@standardnotes/time' -import { VaultUser } from '../Model/VaultUser' -import { VaultUserFactoryInterface } from '../Factory/VaultUserFactoryInterface' -import { v4 as uuidv4 } from 'uuid' -import { VaultUserRepositoryInterface } from '../Repository/VaultUserRepositoryInterface' -import { VaultUserServiceInterface } from './VaultUserServiceInterface' -import { GetVaultUsersDTO } from './GetVaultUsersDTO' -import { VaultsRepositoryInterface } from '../../Vault/Repository/VaultRepositoryInterface' -import { VaultUserPermission } from '../Model/VaultUserPermission' - -export class VaultUserService implements VaultUserServiceInterface { - constructor( - private vaultRepository: VaultsRepositoryInterface, - private vaultUserRepository: VaultUserRepositoryInterface, - private vaultUserFactory: VaultUserFactoryInterface, - private removedVaultUserService: RemovedVaultUserServiceInterface, - private timer: TimerInterface, - ) {} - - async addVaultUser(dto: { - vaultUuid: string - userUuid: string - permissions: VaultUserPermission - }): Promise { - const vault = await this.vaultRepository.findByUuid(dto.vaultUuid) - if (!vault) { - throw new Error('Attempting to add a vault user to a non-existent vault') - } - - await this.removedVaultUserService.deleteRemovedVaultUser({ - vaultUuid: dto.vaultUuid, - userUuid: dto.userUuid, - }) - - const timestamp = this.timer.getTimestampInMicroseconds() - const vaultUser = this.vaultUserFactory.create({ - uuid: uuidv4(), - user_uuid: dto.userUuid, - vault_uuid: dto.vaultUuid, - permissions: dto.permissions, - created_at_timestamp: timestamp, - updated_at_timestamp: timestamp, - }) - - return this.vaultUserRepository.create(vaultUser) - } - - getAllVaultUsersForUser(dto: GetVaultUsersDTO): Promise { - return this.vaultUserRepository.findAllForUser(dto) - } - - getUserForVault(dto: { userUuid: string; vaultUuid: string }): Promise { - return this.vaultUserRepository.findByUserUuidAndVaultUuid(dto.userUuid, dto.vaultUuid) - } - - async getVaultUsersForVault(dto: { - vaultUuid: string - originatorUuid: string - }): Promise<{ users: VaultUser[]; isAdmin: boolean } | undefined> { - const vault = await this.vaultRepository.findByUuid(dto.vaultUuid) - if (!vault) { - return undefined - } - - const isUserVaultAdmin = vault && vault.userUuid === dto.originatorUuid - const doesUserBelongToVault = await this.vaultUserRepository.findByUserUuidAndVaultUuid( - dto.originatorUuid, - dto.vaultUuid, - ) - - if (!isUserVaultAdmin && !doesUserBelongToVault) { - return undefined - } - - const users = await this.vaultUserRepository.findAllForVault({ vaultUuid: dto.vaultUuid }) - - return { - users, - isAdmin: isUserVaultAdmin, - } - } - - async deleteVaultUser(dto: { originatorUuid: string; vaultUuid: string; userUuid: string }): Promise { - const vault = await this.vaultRepository.findByUuid(dto.vaultUuid) - if (!vault) { - return false - } - - if (vault.userUuid === dto.userUuid) { - return false - } - - const isAuthorized = vault.userUuid === dto.originatorUuid || dto.userUuid === dto.originatorUuid - if (!isAuthorized) { - return false - } - - const vaultUser = await this.vaultUserRepository.findByUserUuidAndVaultUuid(dto.userUuid, dto.vaultUuid) - if (!vaultUser) { - return false - } - - await this.vaultUserRepository.remove(vaultUser) - await this.removedVaultUserService.addRemovedVaultUser({ - vaultUuid: dto.vaultUuid, - userUuid: dto.userUuid, - removedBy: dto.originatorUuid, - }) - - return true - } - - async deleteAllVaultUsersForVault(dto: { originatorUuid: string; vaultUuid: string }): Promise { - const vault = await this.vaultRepository.findByUuid(dto.vaultUuid) - if (!vault || vault.userUuid !== dto.originatorUuid) { - return false - } - - const vaultUsers = await this.vaultUserRepository.findAllForVault({ vaultUuid: dto.vaultUuid }) - for (const vaultUser of vaultUsers) { - await this.vaultUserRepository.remove(vaultUser) - await this.removedVaultUserService.addRemovedVaultUser({ - vaultUuid: dto.vaultUuid, - userUuid: vaultUser.userUuid, - removedBy: dto.originatorUuid, - }) - } - - return true - } -} diff --git a/packages/syncing-server/src/Domain/VaultUser/Service/VaultUserServiceInterface.ts b/packages/syncing-server/src/Domain/VaultUser/Service/VaultUserServiceInterface.ts deleted file mode 100644 index 89cafe302..000000000 --- a/packages/syncing-server/src/Domain/VaultUser/Service/VaultUserServiceInterface.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { VaultUser } from '../Model/VaultUser' -import { VaultUserPermission } from '../Model/VaultUserPermission' -import { GetVaultUsersDTO } from './GetVaultUsersDTO' - -export interface VaultUserServiceInterface { - addVaultUser(dto: { vaultUuid: string; userUuid: string; permissions: VaultUserPermission }): Promise - - getAllVaultUsersForUser(dto: GetVaultUsersDTO): Promise - - getUserForVault(dto: { userUuid: string; vaultUuid: string }): Promise - - getVaultUsersForVault(dto: { - vaultUuid: string - originatorUuid: string - }): Promise<{ users: VaultUser[]; isAdmin: boolean } | undefined> - - deleteVaultUser(dto: { originatorUuid: string; vaultUuid: string; userUuid: string }): Promise - - deleteAllVaultUsersForVault(dto: { originatorUuid: string; vaultUuid: string }): Promise -} diff --git a/packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepository.ts b/packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepository.ts index 4c516b48a..14604a11e 100644 --- a/packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepository.ts +++ b/packages/syncing-server/src/Infra/TypeORM/TypeORMItemRepository.ts @@ -131,17 +131,17 @@ export class TypeORMItemRepository implements ItemRepositoryInterface { queryBuilder.orderBy(`item.${query.sortBy}`, query.sortOrder) } - if (query.includeVaultUuids != undefined && query.includeVaultUuids.length > 0) { + if (query.includeGroupUuids != undefined && query.includeGroupUuids.length > 0) { queryBuilder.where( new Brackets((qb) => { - qb.where('item.vault_uuid IN (:...vaultUuids)', { vaultUuids: query.includeVaultUuids }) + qb.where('item.group_uuid IN (:...groupUuids)', { groupUuids: query.includeGroupUuids }) if (query.userUuid) { qb.orWhere('item.user_uuid = :userUuid', { userUuid: query.userUuid }) } }), ) - } else if (query.exclusiveVaultUuids != undefined && query.exclusiveVaultUuids.length > 0) { - queryBuilder.andWhere('item.vault_uuid IN (:...vaultUuids)', { vaultUuids: query.exclusiveVaultUuids }) + } else if (query.exclusiveGroupUuids != undefined && query.exclusiveGroupUuids.length > 0) { + queryBuilder.andWhere('item.group_uuid IN (:...groupUuids)', { groupUuids: query.exclusiveGroupUuids }) } else if (query.userUuid !== undefined) { queryBuilder.where('item.user_uuid = :userUuid', { userUuid: query.userUuid }) } diff --git a/packages/syncing-server/src/Projection/GroupInviteProjection.ts b/packages/syncing-server/src/Projection/GroupInviteProjection.ts new file mode 100644 index 000000000..d7260185e --- /dev/null +++ b/packages/syncing-server/src/Projection/GroupInviteProjection.ts @@ -0,0 +1,14 @@ +import { GroupInviteType } from '../Domain/GroupInvite/Model/GroupInviteType' + +export type GroupInviteProjection = { + uuid: string + group_uuid: string + user_uuid: string + inviter_uuid: string + inviter_public_key: string + encrypted_group_data: string + invite_type: GroupInviteType + permissions: string + created_at_timestamp: number + updated_at_timestamp: number +} diff --git a/packages/syncing-server/src/Projection/GroupInviteProjector.ts b/packages/syncing-server/src/Projection/GroupInviteProjector.ts new file mode 100644 index 000000000..1d4d4e69d --- /dev/null +++ b/packages/syncing-server/src/Projection/GroupInviteProjector.ts @@ -0,0 +1,33 @@ +import { GroupInvite } from '../Domain/GroupInvite/Model/GroupInvite' +import { ProjectorInterface } from './ProjectorInterface' +import { GroupInviteProjection } from './GroupInviteProjection' + +export class GroupInviteProjector implements ProjectorInterface { + projectSimple(_groupInvite: GroupInvite): GroupInviteProjection { + throw Error('not implemented') + } + + projectCustom(_projectionType: string, groupInvite: GroupInvite): GroupInviteProjection { + const fullProjection = this.projectFull(groupInvite) + + return { + ...fullProjection, + user_uuid: groupInvite.userUuid, + } + } + + projectFull(groupInvite: GroupInvite): GroupInviteProjection { + return { + uuid: groupInvite.uuid, + group_uuid: groupInvite.groupUuid, + user_uuid: groupInvite.userUuid, + inviter_uuid: groupInvite.inviterUuid, + inviter_public_key: groupInvite.inviterPublicKey, + encrypted_group_data: groupInvite.encryptedGroupData, + invite_type: groupInvite.inviteType, + permissions: groupInvite.permissions, + created_at_timestamp: groupInvite.createdAtTimestamp, + updated_at_timestamp: groupInvite.updatedAtTimestamp, + } + } +} diff --git a/packages/syncing-server/src/Projection/VaultProjection.ts b/packages/syncing-server/src/Projection/GroupProjection.ts similarity index 86% rename from packages/syncing-server/src/Projection/VaultProjection.ts rename to packages/syncing-server/src/Projection/GroupProjection.ts index cdefdc985..c8bc26d2a 100644 --- a/packages/syncing-server/src/Projection/VaultProjection.ts +++ b/packages/syncing-server/src/Projection/GroupProjection.ts @@ -1,4 +1,4 @@ -export type VaultProjection = { +export type GroupProjection = { uuid: string user_uuid: string specified_items_key_uuid: string diff --git a/packages/syncing-server/src/Projection/GroupProjector.ts b/packages/syncing-server/src/Projection/GroupProjector.ts new file mode 100644 index 000000000..78cacfd4e --- /dev/null +++ b/packages/syncing-server/src/Projection/GroupProjector.ts @@ -0,0 +1,30 @@ +import { Group } from '../Domain/Group/Model/Group' +import { ProjectorInterface } from './ProjectorInterface' +import { GroupProjection } from './GroupProjection' + +export class GroupProjector implements ProjectorInterface { + projectSimple(_group: Group): GroupProjection { + throw Error('not implemented') + } + + projectCustom(_projectionType: string, group: Group): GroupProjection { + const fullProjection = this.projectFull(group) + + return { + ...fullProjection, + user_uuid: group.userUuid, + } + } + + projectFull(group: Group): GroupProjection { + return { + uuid: group.uuid, + user_uuid: group.userUuid, + specified_items_key_uuid: group.specifiedItemsKeyUuid, + file_upload_bytes_used: group.fileUploadBytesUsed, + file_upload_bytes_limit: group.fileUploadBytesLimit, + created_at_timestamp: group.createdAtTimestamp, + updated_at_timestamp: group.updatedAtTimestamp, + } + } +} diff --git a/packages/syncing-server/src/Projection/VaultUserProjection.ts b/packages/syncing-server/src/Projection/GroupUserProjection.ts similarity index 66% rename from packages/syncing-server/src/Projection/VaultUserProjection.ts rename to packages/syncing-server/src/Projection/GroupUserProjection.ts index 6a9479c58..70f3d6c77 100644 --- a/packages/syncing-server/src/Projection/VaultUserProjection.ts +++ b/packages/syncing-server/src/Projection/GroupUserProjection.ts @@ -1,15 +1,15 @@ -export type VaultUserProjection = { +export type GroupUserProjection = { uuid: string - vault_uuid: string + group_uuid: string user_uuid: string permissions: string created_at_timestamp: number updated_at_timestamp: number } -export type VaultUserListingProjection = { +export type GroupUserListingProjection = { uuid: string - vault_uuid: string + group_uuid: string user_uuid: string permissions?: string created_at_timestamp: number diff --git a/packages/syncing-server/src/Projection/GroupUserProjector.ts b/packages/syncing-server/src/Projection/GroupUserProjector.ts new file mode 100644 index 000000000..7f557cff4 --- /dev/null +++ b/packages/syncing-server/src/Projection/GroupUserProjector.ts @@ -0,0 +1,43 @@ +import { GroupUser } from '../Domain/GroupUser/Model/GroupUser' +import { ProjectorInterface } from './ProjectorInterface' +import { GroupUserProjection, GroupUserListingProjection } from './GroupUserProjection' + +export class GroupUserProjector implements ProjectorInterface { + projectSimple(_groupUser: GroupUser): GroupUserProjection { + throw Error('not implemented') + } + + projectCustom(_projectionType: string, groupUser: GroupUser): GroupUserProjection { + const fullProjection = this.projectFull(groupUser) + + return { + ...fullProjection, + user_uuid: groupUser.userUuid, + } + } + + projectAsDisplayableUserForOtherGroupMembers( + groupUser: GroupUser, + isRequesterGroupAdmin: boolean, + ): GroupUserListingProjection { + return { + uuid: groupUser.uuid, + group_uuid: groupUser.groupUuid, + user_uuid: groupUser.userUuid, + permissions: isRequesterGroupAdmin ? groupUser.permissions : undefined, + created_at_timestamp: groupUser.createdAtTimestamp, + updated_at_timestamp: groupUser.updatedAtTimestamp, + } + } + + projectFull(groupUser: GroupUser): GroupUserProjection { + return { + uuid: groupUser.uuid, + group_uuid: groupUser.groupUuid, + user_uuid: groupUser.userUuid, + permissions: groupUser.permissions, + created_at_timestamp: groupUser.createdAtTimestamp, + updated_at_timestamp: groupUser.updatedAtTimestamp, + } + } +} diff --git a/packages/syncing-server/src/Projection/ItemProjection.ts b/packages/syncing-server/src/Projection/ItemProjection.ts index dd5007ca5..6caf985e5 100644 --- a/packages/syncing-server/src/Projection/ItemProjection.ts +++ b/packages/syncing-server/src/Projection/ItemProjection.ts @@ -3,7 +3,7 @@ export type ItemProjection = { items_key_id: string | null duplicate_of: string | null enc_item_key: string | null - vault_uuid: string | null + group_uuid: string | null content: string | null content_type: string auth_hash: string | null diff --git a/packages/syncing-server/src/Projection/ItemProjector.ts b/packages/syncing-server/src/Projection/ItemProjector.ts index 17ef7da7e..f595e969d 100644 --- a/packages/syncing-server/src/Projection/ItemProjector.ts +++ b/packages/syncing-server/src/Projection/ItemProjector.ts @@ -33,7 +33,7 @@ export class ItemProjector implements ProjectorInterface { updated_at: this.timer.convertMicrosecondsToStringDate(item.updatedAtTimestamp), updated_at_timestamp: item.updatedAtTimestamp, updated_with_session: item.updatedWithSession, - vault_uuid: item.vaultUuid, + group_uuid: item.groupUuid, user_uuid: item.userUuid, created_by_uuid: item.createdByUuid, last_edited_by_uuid: item.lastEditedByUuid, diff --git a/packages/syncing-server/src/Projection/SavedItemProjection.ts b/packages/syncing-server/src/Projection/SavedItemProjection.ts index 164ecda04..1e2f5f098 100644 --- a/packages/syncing-server/src/Projection/SavedItemProjection.ts +++ b/packages/syncing-server/src/Projection/SavedItemProjection.ts @@ -8,6 +8,6 @@ export type SavedItemProjection = { created_at_timestamp: number updated_at: string updated_at_timestamp: number - vault_uuid: string | null + group_uuid: string | null last_edited_by_uuid: string | null } diff --git a/packages/syncing-server/src/Projection/SavedItemProjector.ts b/packages/syncing-server/src/Projection/SavedItemProjector.ts index 3edd9d861..ca3d52f6a 100644 --- a/packages/syncing-server/src/Projection/SavedItemProjector.ts +++ b/packages/syncing-server/src/Projection/SavedItemProjector.ts @@ -27,7 +27,7 @@ export class SavedItemProjector implements ProjectorInterface { - projectSimple(_vaultInvite: VaultInvite): VaultInviteProjection { - throw Error('not implemented') - } - - projectCustom(_projectionType: string, vaultInvite: VaultInvite): VaultInviteProjection { - const fullProjection = this.projectFull(vaultInvite) - - return { - ...fullProjection, - user_uuid: vaultInvite.userUuid, - } - } - - projectFull(vaultInvite: VaultInvite): VaultInviteProjection { - return { - uuid: vaultInvite.uuid, - vault_uuid: vaultInvite.vaultUuid, - user_uuid: vaultInvite.userUuid, - inviter_uuid: vaultInvite.inviterUuid, - inviter_public_key: vaultInvite.inviterPublicKey, - encrypted_vault_data: vaultInvite.encryptedVaultData, - invite_type: vaultInvite.inviteType, - permissions: vaultInvite.permissions, - created_at_timestamp: vaultInvite.createdAtTimestamp, - updated_at_timestamp: vaultInvite.updatedAtTimestamp, - } - } -} diff --git a/packages/syncing-server/src/Projection/VaultProjector.ts b/packages/syncing-server/src/Projection/VaultProjector.ts deleted file mode 100644 index dd2f01c12..000000000 --- a/packages/syncing-server/src/Projection/VaultProjector.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { Vault } from '../Domain/Vault/Model/Vault' -import { ProjectorInterface } from './ProjectorInterface' -import { VaultProjection } from './VaultProjection' - -export class VaultProjector implements ProjectorInterface { - projectSimple(_vault: Vault): VaultProjection { - throw Error('not implemented') - } - - projectCustom(_projectionType: string, vault: Vault): VaultProjection { - const fullProjection = this.projectFull(vault) - - return { - ...fullProjection, - user_uuid: vault.userUuid, - } - } - - projectFull(vault: Vault): VaultProjection { - return { - uuid: vault.uuid, - user_uuid: vault.userUuid, - specified_items_key_uuid: vault.specifiedItemsKeyUuid, - file_upload_bytes_used: vault.fileUploadBytesUsed, - file_upload_bytes_limit: vault.fileUploadBytesLimit, - created_at_timestamp: vault.createdAtTimestamp, - updated_at_timestamp: vault.updatedAtTimestamp, - } - } -} diff --git a/packages/syncing-server/src/Projection/VaultUserProjector.ts b/packages/syncing-server/src/Projection/VaultUserProjector.ts deleted file mode 100644 index 23456bfee..000000000 --- a/packages/syncing-server/src/Projection/VaultUserProjector.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { VaultUser } from '../Domain/VaultUser/Model/VaultUser' -import { ProjectorInterface } from './ProjectorInterface' -import { VaultUserProjection, VaultUserListingProjection } from './VaultUserProjection' - -export class VaultUserProjector implements ProjectorInterface { - projectSimple(_vaultUser: VaultUser): VaultUserProjection { - throw Error('not implemented') - } - - projectCustom(_projectionType: string, vaultUser: VaultUser): VaultUserProjection { - const fullProjection = this.projectFull(vaultUser) - - return { - ...fullProjection, - user_uuid: vaultUser.userUuid, - } - } - - projectAsDisplayableUserForOtherVaultMembers( - vaultUser: VaultUser, - isRequesterVaultAdmin: boolean, - ): VaultUserListingProjection { - return { - uuid: vaultUser.uuid, - vault_uuid: vaultUser.vaultUuid, - user_uuid: vaultUser.userUuid, - permissions: isRequesterVaultAdmin ? vaultUser.permissions : undefined, - created_at_timestamp: vaultUser.createdAtTimestamp, - updated_at_timestamp: vaultUser.updatedAtTimestamp, - } - } - - projectFull(vaultUser: VaultUser): VaultUserProjection { - return { - uuid: vaultUser.uuid, - vault_uuid: vaultUser.vaultUuid, - user_uuid: vaultUser.userUuid, - permissions: vaultUser.permissions, - created_at_timestamp: vaultUser.createdAtTimestamp, - updated_at_timestamp: vaultUser.updatedAtTimestamp, - } - } -} diff --git a/packages/syncing-server/src/Tmp/ConflictType.ts b/packages/syncing-server/src/Tmp/ConflictType.ts index a68bcb096..8c79adb8f 100644 --- a/packages/syncing-server/src/Tmp/ConflictType.ts +++ b/packages/syncing-server/src/Tmp/ConflictType.ts @@ -6,8 +6,8 @@ export enum ConflictType { ReadOnlyError = 'readonly_error', UuidError = 'uuid_error', SnjsVersionError = 'snjs_version_error', - VaultInsufficientPermissionsError = 'vault_insufficient_permissions_error', - VaultNotMemberError = 'vault_not_member_error', - VaultInvalidState = 'vault_invalid_state', - VaultInvalidItemsKey = 'vault_invalid_items_key', + GroupInsufficientPermissionsError = 'group_insufficient_permissions_error', + GroupNotMemberError = 'group_not_member_error', + GroupInvalidState = 'group_invalid_state', + GroupInvalidItemsKey = 'group_invalid_items_key', }