mirror of
https://github.com/standardnotes/server
synced 2026-07-14 00:01:54 -04:00
refactor: rename vaults to groups
This commit is contained in:
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
+2
-2
@@ -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()
|
||||
}
|
||||
@@ -27,8 +27,8 @@ export class SettingService implements SettingServiceInterface {
|
||||
|
||||
async applyDefaultSettingsUponRegistration(user: User): Promise<void> {
|
||||
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()) {
|
||||
|
||||
@@ -66,7 +66,7 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
||||
],
|
||||
])
|
||||
|
||||
private readonly vaultAccountDefaultSettingsOverwrites = new Map<string, SettingDescription>([
|
||||
private readonly privateUsernameAccountDefaultSettingsOverwrites = new Map<string, SettingDescription>([
|
||||
[
|
||||
SettingName.NAMES.LogSessionUserAgent,
|
||||
{
|
||||
@@ -114,16 +114,18 @@ export class SettingsAssociationService implements SettingsAssociationServiceInt
|
||||
return this.defaultSettings
|
||||
}
|
||||
|
||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<string, SettingDescription> {
|
||||
const defaultVaultSettings = new Map(this.defaultSettings)
|
||||
getDefaultSettingsAndValuesForNewPrivateUsernameAccount(): Map<string, SettingDescription> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { SettingDescription } from './SettingDescription'
|
||||
|
||||
export interface SettingsAssociationServiceInterface {
|
||||
getDefaultSettingsAndValuesForNewUser(): Map<string, SettingDescription>
|
||||
getDefaultSettingsAndValuesForNewVaultAccount(): Map<string, SettingDescription>
|
||||
getDefaultSettingsAndValuesForNewPrivateUsernameAccount(): Map<string, SettingDescription>
|
||||
getPermissionAssociatedWithSetting(settingName: SettingName): PermissionName | undefined
|
||||
getEncryptionVersionForSetting(settingName: SettingName): EncryptionVersion
|
||||
getSensitivityForSetting(settingName: SettingName): boolean
|
||||
|
||||
+2
-2
@@ -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('@')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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('@')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { GroupFileRemovedEventPayload } from './GroupFileRemovedEventPayload'
|
||||
|
||||
export interface GroupFileRemovedEvent extends DomainEventInterface {
|
||||
type: 'GROUP_FILE_REMOVED'
|
||||
payload: GroupFileRemovedEventPayload
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface GroupFileRemovedEventPayload {
|
||||
groupUuid: string
|
||||
fileByteSize: number
|
||||
filePath: string
|
||||
fileName: string
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { GroupFileUploadedEventPayload } from './GroupFileUploadedEventPayload'
|
||||
|
||||
export interface GroupFileUploadedEvent extends DomainEventInterface {
|
||||
type: 'GROUP_FILE_UPLOADED'
|
||||
payload: GroupFileUploadedEventPayload
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
export interface GroupFileUploadedEventPayload {
|
||||
groupUuid: string
|
||||
fileByteSize: number
|
||||
filePath: string
|
||||
fileName: string
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { VaultFileRemovedEventPayload } from './VaultFileRemovedEventPayload'
|
||||
|
||||
export interface VaultFileRemovedEvent extends DomainEventInterface {
|
||||
type: 'VAULT_FILE_REMOVED'
|
||||
payload: VaultFileRemovedEventPayload
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export interface VaultFileRemovedEventPayload {
|
||||
vaultUuid: string
|
||||
fileByteSize: number
|
||||
filePath: string
|
||||
fileName: string
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { DomainEventInterface } from './DomainEventInterface'
|
||||
import { VaultFileUploadedEventPayload } from './VaultFileUploadedEventPayload'
|
||||
|
||||
export interface VaultFileUploadedEvent extends DomainEventInterface {
|
||||
type: 'VAULT_FILE_UPLOADED'
|
||||
payload: VaultFileUploadedEventPayload
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export interface VaultFileUploadedEventPayload {
|
||||
vaultUuid: string
|
||||
fileByteSize: number
|
||||
filePath: string
|
||||
fileName: string
|
||||
}
|
||||
@@ -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'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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<ValetTokenAuthMiddleware>(TYPES.ValetTokenAuthMiddleware).to(ValetTokenAuthMiddleware)
|
||||
container.bind<VaultValetTokenAuthMiddleware>(TYPES.VaultValetTokenAuthMiddleware).to(VaultValetTokenAuthMiddleware)
|
||||
container.bind<GroupValetTokenAuthMiddleware>(TYPES.GroupValetTokenAuthMiddleware).to(GroupValetTokenAuthMiddleware)
|
||||
|
||||
// services
|
||||
container
|
||||
|
||||
@@ -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'),
|
||||
|
||||
+16
-16
@@ -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<results.BadRequestErrorMessageResult | results.JsonResult> {
|
||||
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<results.BadRequestErrorMessageResult | results.JsonResult> {
|
||||
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<results.BadRequestErrorMessageResult | results.JsonResult> {
|
||||
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<results.BadRequestErrorMessageResult | results.JsonResult> {
|
||||
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<results.BadRequestErrorMessageResult | results.JsonResult> {
|
||||
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<results.BadRequestErrorMessageResult | (() => 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,
|
||||
+7
-7
@@ -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<VaultValetTokenData>,
|
||||
@inject(TYPES.ValetTokenDecoder) private tokenDecoder: TokenDecoderInterface<GroupValetTokenData>,
|
||||
@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,
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export type FinishUploadSessionDTO = {
|
||||
ownerUuid: string
|
||||
ownerType: 'user' | 'vault'
|
||||
ownerType: 'user' | 'group'
|
||||
resourceRemoteIdentifier: string
|
||||
uploadBytesUsed: number
|
||||
uploadBytesLimit: number
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
}),
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
export type GroupMoveType = 'group-to-user' | 'user-to-group' | 'group-to-group'
|
||||
+4
-4
@@ -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
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
export type VaultMoveType = 'vault-to-user' | 'user-to-vault' | 'vault-to-vault'
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
```
|
||||
@@ -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'
|
||||
|
||||
@@ -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<void> {
|
||||
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<void> {
|
||||
await queryRunner.dropTable('vaults')
|
||||
await queryRunner.dropTable('groups')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<void> {
|
||||
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<void> {
|
||||
await queryRunner.dropTable('vault_users')
|
||||
await queryRunner.dropTable('group_users')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<void> {
|
||||
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<void> {
|
||||
await queryRunner.dropColumn('items', 'vault_uuid')
|
||||
await queryRunner.dropColumn('items', 'group_uuid')
|
||||
await queryRunner.dropColumn('items', 'last_edited_by_uuid')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<void> {
|
||||
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<void> {
|
||||
await queryRunner.dropTable('vault_invites')
|
||||
await queryRunner.dropTable('group_invites')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<void> {
|
||||
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<void> {
|
||||
await queryRunner.dropTable('removed_vault_users')
|
||||
await queryRunner.dropTable('removed_group_users')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<ItemRepositoryInterface>(TYPES.ItemRepository).toDynamicValue((context: interfaces.Context) => {
|
||||
return new TypeORMItemRepository(context.container.get(TYPES.ORMItemRepository))
|
||||
})
|
||||
container.bind<VaultsRepositoryInterface>(TYPES.VaultRepository).toDynamicValue((context: interfaces.Context) => {
|
||||
return new TypeORMVaultRepository(context.container.get(TYPES.ORMVaultRepository))
|
||||
container.bind<GroupsRepositoryInterface>(TYPES.GroupRepository).toDynamicValue((context: interfaces.Context) => {
|
||||
return new TypeORMGroupRepository(context.container.get(TYPES.ORMGroupRepository))
|
||||
})
|
||||
container
|
||||
.bind<VaultUserRepositoryInterface>(TYPES.VaultUserRepository)
|
||||
.bind<GroupUserRepositoryInterface>(TYPES.GroupUserRepository)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new TypeORMVaultUserRepository(context.container.get(TYPES.ORMVaultUserRepository))
|
||||
return new TypeORMGroupUserRepository(context.container.get(TYPES.ORMGroupUserRepository))
|
||||
})
|
||||
container
|
||||
.bind<RemovedVaultUserRepositoryInterface>(TYPES.RemovedVaultUserRepository)
|
||||
.bind<RemovedGroupUserRepositoryInterface>(TYPES.RemovedGroupUserRepository)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new TypeORMRemovedVaultUserRepository(context.container.get(TYPES.ORMRemovedVaultUserRepository))
|
||||
return new TypeORMRemovedGroupUserRepository(context.container.get(TYPES.ORMRemovedGroupUserRepository))
|
||||
})
|
||||
container
|
||||
.bind<VaultInviteRepositoryInterface>(TYPES.VaultInviteRepository)
|
||||
.bind<GroupInviteRepositoryInterface>(TYPES.GroupInviteRepository)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new TypeORMVaultInviteRepository(context.container.get(TYPES.ORMVaultInviteRepository))
|
||||
return new TypeORMGroupInviteRepository(context.container.get(TYPES.ORMGroupInviteRepository))
|
||||
})
|
||||
container
|
||||
.bind<ContactsRepositoryInterface>(TYPES.ContactRepository)
|
||||
@@ -125,16 +125,16 @@ export class CommonContainerConfigLoader {
|
||||
|
||||
// ORM
|
||||
container.bind<Repository<Item>>(TYPES.ORMItemRepository).toDynamicValue(() => AppDataSource.getRepository(Item))
|
||||
container.bind<Repository<Vault>>(TYPES.ORMVaultRepository).toDynamicValue(() => AppDataSource.getRepository(Vault))
|
||||
container.bind<Repository<Group>>(TYPES.ORMGroupRepository).toDynamicValue(() => AppDataSource.getRepository(Group))
|
||||
container
|
||||
.bind<Repository<VaultUser>>(TYPES.ORMVaultUserRepository)
|
||||
.toDynamicValue(() => AppDataSource.getRepository(VaultUser))
|
||||
.bind<Repository<GroupUser>>(TYPES.ORMGroupUserRepository)
|
||||
.toDynamicValue(() => AppDataSource.getRepository(GroupUser))
|
||||
container
|
||||
.bind<Repository<RemovedVaultUser>>(TYPES.ORMRemovedVaultUserRepository)
|
||||
.toDynamicValue(() => AppDataSource.getRepository(RemovedVaultUser))
|
||||
.bind<Repository<RemovedGroupUser>>(TYPES.ORMRemovedGroupUserRepository)
|
||||
.toDynamicValue(() => AppDataSource.getRepository(RemovedGroupUser))
|
||||
container
|
||||
.bind<Repository<VaultInvite>>(TYPES.ORMVaultInviteRepository)
|
||||
.toDynamicValue(() => AppDataSource.getRepository(VaultInvite))
|
||||
.bind<Repository<GroupInvite>>(TYPES.ORMGroupInviteRepository)
|
||||
.toDynamicValue(() => AppDataSource.getRepository(GroupInvite))
|
||||
container
|
||||
.bind<Repository<Contact>>(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<ProjectorInterface<Vault, VaultProjection>>(TYPES.VaultProjector).toDynamicValue(() => {
|
||||
return new VaultProjector()
|
||||
container.bind<ProjectorInterface<Group, GroupProjection>>(TYPES.GroupProjector).toDynamicValue(() => {
|
||||
return new GroupProjector()
|
||||
})
|
||||
container.bind<ProjectorInterface<VaultUser, VaultUserProjection>>(TYPES.VaultUserProjector).toDynamicValue(() => {
|
||||
return new VaultUserProjector()
|
||||
container.bind<ProjectorInterface<GroupUser, GroupUserProjection>>(TYPES.GroupUserProjector).toDynamicValue(() => {
|
||||
return new GroupUserProjector()
|
||||
})
|
||||
container
|
||||
.bind<ProjectorInterface<VaultInvite, VaultInviteProjection>>(TYPES.VaultInviteProjector)
|
||||
.bind<ProjectorInterface<GroupInvite, GroupInviteProjection>>(TYPES.GroupInviteProjector)
|
||||
.toDynamicValue(() => {
|
||||
return new VaultInviteProjector()
|
||||
return new GroupInviteProjector()
|
||||
})
|
||||
container.bind<ProjectorInterface<Contact, ContactProjection>>(TYPES.ContactProjector).toDynamicValue(() => {
|
||||
return new ContactProjector()
|
||||
|
||||
@@ -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: <LoggerOptions>env.get('DB_DEBUG_LEVEL'),
|
||||
|
||||
@@ -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<SyncItems>(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<CreateVaultFileValetToken>(TYPES.CreateVaultFileReadValetToken)
|
||||
.bind<CreateGroupFileValetToken>(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<VaultServiceInterface>(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<GroupServiceInterface>(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<VaultUserServiceInterface>(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<GroupUserServiceInterface>(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<RemovedVaultUserServiceInterface>(TYPES.RemovedVaultUserService)
|
||||
.bind<RemovedGroupUserServiceInterface>(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<VaultInviteServiceInterface>(TYPES.VaultInviteService)
|
||||
.bind<GroupInviteServiceInterface>(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<ItemFactoryInterface>(TYPES.ItemFactory).toDynamicValue((context: interfaces.Context) => {
|
||||
return new ItemFactory(context.container.get(TYPES.Timer), context.container.get(TYPES.ItemProjector))
|
||||
})
|
||||
container.bind<VaultFactoryInterface>(TYPES.VaultFactory).toDynamicValue((context: interfaces.Context) => {
|
||||
return new VaultFactory(context.container.get(TYPES.Timer))
|
||||
container.bind<GroupFactoryInterface>(TYPES.GroupFactory).toDynamicValue((context: interfaces.Context) => {
|
||||
return new GroupFactory(context.container.get(TYPES.Timer))
|
||||
})
|
||||
container.bind<VaultUserFactory>(TYPES.VaultUserFactory).toDynamicValue((context: interfaces.Context) => {
|
||||
return new VaultUserFactory(context.container.get(TYPES.Timer))
|
||||
container.bind<GroupUserFactory>(TYPES.GroupUserFactory).toDynamicValue((context: interfaces.Context) => {
|
||||
return new GroupUserFactory(context.container.get(TYPES.Timer))
|
||||
})
|
||||
container
|
||||
.bind<RemovedVaultUserFactory>(TYPES.RemovedVaultUserFactory)
|
||||
.bind<RemovedGroupUserFactory>(TYPES.RemovedGroupUserFactory)
|
||||
.toDynamicValue((context: interfaces.Context) => {
|
||||
return new RemovedVaultUserFactory(context.container.get(TYPES.Timer))
|
||||
return new RemovedGroupUserFactory(context.container.get(TYPES.Timer))
|
||||
})
|
||||
container.bind<VaultInviteFactory>(TYPES.VaultInviteFactory).toDynamicValue((context: interfaces.Context) => {
|
||||
return new VaultInviteFactory(context.container.get(TYPES.Timer))
|
||||
container.bind<GroupInviteFactory>(TYPES.GroupInviteFactory).toDynamicValue((context: interfaces.Context) => {
|
||||
return new GroupInviteFactory(context.container.get(TYPES.Timer))
|
||||
})
|
||||
container.bind<ContactFactoryInterface>(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<OwnershipFilter>(TYPES.OwnershipFilter).toDynamicValue(() => new OwnershipFilter())
|
||||
container
|
||||
.bind<VaultFilter>(TYPES.VaultFilter)
|
||||
.bind<GroupFilter>(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<TimeDifferenceFilter>(TYPES.TimeDifferenceFilter)
|
||||
@@ -261,16 +261,16 @@ export class ServerContainerConfigLoader extends CommonContainerConfigLoader {
|
||||
container.bind<ContentTypeFilter>(TYPES.ContentTypeFilter).toDynamicValue(() => new ContentTypeFilter())
|
||||
container.bind<ContentFilter>(TYPES.ContentFilter).toDynamicValue(() => new ContentFilter())
|
||||
container
|
||||
.bind<VaultSnjsVersionFilter>(TYPES.VaultSnjsVersionFilter)
|
||||
.toDynamicValue(() => new VaultSnjsVersionFilter())
|
||||
.bind<GroupSnjsVersionFilter>(TYPES.GroupSnjsVersionFilter)
|
||||
.toDynamicValue(() => new GroupSnjsVersionFilter())
|
||||
|
||||
container
|
||||
.bind<ItemSaveValidatorInterface>(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),
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -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<VaultFileUploadedEventHandler>(TYPES.VaultFileUploadedEventHandler).to(VaultFileUploadedEventHandler)
|
||||
container.bind<VaultFileRemovedEventHandler>(TYPES.VaultFileRemovedEventHandler).to(VaultFileRemovedEventHandler)
|
||||
container.bind<GroupFileUploadedEventHandler>(TYPES.GroupFileUploadedEventHandler).to(GroupFileUploadedEventHandler)
|
||||
container.bind<GroupFileRemovedEventHandler>(TYPES.GroupFileRemovedEventHandler).to(GroupFileRemovedEventHandler)
|
||||
|
||||
// Services
|
||||
container.bind<ContentDecoder>(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 =
|
||||
|
||||
+40
-40
@@ -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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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,
|
||||
})
|
||||
|
||||
+15
-15
@@ -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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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,
|
||||
})
|
||||
@@ -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<Group, GroupProjection>,
|
||||
@inject(TYPES.GroupUserProjector) private groupUserProjector: ProjectorInterface<GroupUser, GroupUserProjection>,
|
||||
@inject(TYPES.CreateGroupFileReadValetToken) private createGroupFileReadValetToken: CreateGroupFileValetToken,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpGet('/', TYPES.AuthMiddleware)
|
||||
public async getGroups(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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<Vault, VaultProjection>,
|
||||
@inject(TYPES.VaultUserProjector) private vaultUserProjector: ProjectorInterface<VaultUser, VaultUserProjection>,
|
||||
@inject(TYPES.CreateVaultFileReadValetToken) private createVaultFileReadValetToken: CreateVaultFileValetToken,
|
||||
) {
|
||||
super()
|
||||
}
|
||||
|
||||
@httpGet('/', TYPES.AuthMiddleware)
|
||||
public async getVaults(_request: Request, response: Response): Promise<results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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<results.NotFoundResult | results.JsonResult> {
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
export type VaultHash = {
|
||||
export type GroupHash = {
|
||||
uuid: string
|
||||
user_uuid: string
|
||||
specified_items_key_uuid: string
|
||||
+3
-3
@@ -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({
|
||||
@@ -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<Group | null>
|
||||
create(group: Group): Promise<Group>
|
||||
save(group: Group): Promise<Group>
|
||||
remove(group: Group): Promise<Group>
|
||||
findAll(query: UserGroupsQuery): Promise<Group[]>
|
||||
}
|
||||
@@ -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<Group>) {}
|
||||
|
||||
async create(group: Group): Promise<Group> {
|
||||
return this.ormRepository.save(group)
|
||||
}
|
||||
|
||||
async save(group: Group): Promise<Group> {
|
||||
return this.ormRepository.save(group)
|
||||
}
|
||||
|
||||
findByUuid(uuid: string): Promise<Group | null> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('group')
|
||||
.where('group.uuid = :uuid', {
|
||||
uuid,
|
||||
})
|
||||
.getOne()
|
||||
}
|
||||
|
||||
async remove(group: Group): Promise<Group> {
|
||||
return this.ormRepository.remove(group)
|
||||
}
|
||||
|
||||
async findAll(query: UserGroupsQuery): Promise<Group[]> {
|
||||
return this.createFindAllQueryBuilder(query).getMany()
|
||||
}
|
||||
|
||||
private createFindAllQueryBuilder(query: UserGroupsQuery): SelectQueryBuilder<Group> {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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<CreateGroupResult | null> {
|
||||
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<Group | null> {
|
||||
const group = await this.groupRepository.findByUuid(dto.groupUuid)
|
||||
|
||||
return group
|
||||
}
|
||||
|
||||
async getGroups(dto: { userUuid: string; lastSyncTime?: number }): Promise<Group[]> {
|
||||
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<Group | null> {
|
||||
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<boolean> {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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<CreateGroupResult | null>
|
||||
|
||||
updateGroup(dto: UpdateGroupDTO): Promise<Group | null>
|
||||
|
||||
deleteGroup(dto: { groupUuid: string; originatorUuid: string }): Promise<boolean>
|
||||
|
||||
getGroup(dto: { groupUuid: string }): Promise<Group | null>
|
||||
|
||||
getGroups(dto: { userUuid: string; lastSyncTime?: number }): Promise<Group[]>
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { GroupInvite } from '../Model/GroupInvite'
|
||||
import { GroupInviteHash } from './GroupInviteHash'
|
||||
|
||||
export interface GroupInviteFactoryInterface {
|
||||
create(dto: GroupInviteHash): GroupInvite
|
||||
createStub(dto: GroupInviteHash): GroupInvite
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+10
-10
@@ -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',
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
export enum VaultInviteType {
|
||||
export enum GroupInviteType {
|
||||
Join = 'join',
|
||||
KeyChange = 'key-change',
|
||||
}
|
||||
+25
@@ -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<GroupInvite | null>
|
||||
create(group: GroupInvite): Promise<GroupInvite>
|
||||
save(groupInvite: GroupInvite): Promise<GroupInvite>
|
||||
remove(group: GroupInvite): Promise<GroupInvite>
|
||||
findAll(query: GroupInviteFindAllForUserQuery): Promise<GroupInvite[]>
|
||||
findAllForGroup(query: GroupInviteFindAllForGroup): Promise<GroupInvite[]>
|
||||
findByUserUuidAndGroupUuid(userUuid: string, groupUuid: string): Promise<GroupInvite | null>
|
||||
}
|
||||
+71
@@ -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<GroupInvite>) {}
|
||||
|
||||
async create(groupInvite: GroupInvite): Promise<GroupInvite> {
|
||||
return this.ormRepository.save(groupInvite)
|
||||
}
|
||||
|
||||
async save(groupInvite: GroupInvite): Promise<GroupInvite> {
|
||||
return this.ormRepository.save(groupInvite)
|
||||
}
|
||||
|
||||
findByUuid(uuid: string): Promise<GroupInvite | null> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('group_invite')
|
||||
.where('group_invite.uuid = :uuid', {
|
||||
uuid,
|
||||
})
|
||||
.getOne()
|
||||
}
|
||||
|
||||
findByUserUuidAndGroupUuid(userUuid: string, groupUuid: string): Promise<GroupInvite | null> {
|
||||
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<GroupInvite> {
|
||||
return this.ormRepository.remove(groupInvite)
|
||||
}
|
||||
|
||||
findAllForGroup(query: GroupInviteFindAllForGroup): Promise<GroupInvite[]> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('group_invite')
|
||||
.where('group_invite.group_uuid = :groupUuid', { groupUuid: query.groupUuid })
|
||||
.getMany()
|
||||
}
|
||||
|
||||
async findAll(query: GroupInviteFindAllForUserQuery): Promise<GroupInvite[]> {
|
||||
return this.createFindAllQueryBuilder(query).getMany()
|
||||
}
|
||||
|
||||
private createFindAllQueryBuilder(query: GroupInviteFindAllForUserQuery): SelectQueryBuilder<GroupInvite> {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
export interface GetUserVaultInvitesDTO {
|
||||
export interface GetUserGroupInvitesDTO {
|
||||
userUuid: string
|
||||
lastSyncTime?: number
|
||||
}
|
||||
@@ -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<GroupInvite | null> {
|
||||
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<GroupInvite | null> {
|
||||
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<GroupInvite[]> {
|
||||
return this.groupInviteRepository.findAll(dto)
|
||||
}
|
||||
|
||||
getOutboundInvitesForUser(dto: { userUuid: string }): Promise<GroupInvite[]> {
|
||||
return this.groupInviteRepository.findAll({
|
||||
inviterUuid: dto.userUuid,
|
||||
})
|
||||
}
|
||||
|
||||
async deleteAllInboundInvites(dto: { userUuid: string }): Promise<void> {
|
||||
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<GroupInvite[] | undefined> {
|
||||
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<boolean> {
|
||||
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<boolean> {
|
||||
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<boolean> {
|
||||
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<boolean> {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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<GroupInvite | null>
|
||||
|
||||
updateInvite(dto: UpdateInviteDTO): Promise<GroupInvite | null>
|
||||
|
||||
getInvitesForUser(dto: GetUserGroupInvitesDTO): Promise<GroupInvite[]>
|
||||
|
||||
getInvitesForGroup(dto: { groupUuid: string; originatorUuid: string }): Promise<GroupInvite[] | undefined>
|
||||
|
||||
getOutboundInvitesForUser(dto: { userUuid: string }): Promise<GroupInvite[]>
|
||||
|
||||
acceptInvite(dto: { originatorUuid: string; inviteUuid: string }): Promise<boolean>
|
||||
|
||||
declineInvite(dto: { originatorUuid: string; inviteUuid: string }): Promise<boolean>
|
||||
|
||||
deleteInvite(dto: { originatorUuid: string; groupUuid: string; inviteUuid: string }): Promise<boolean>
|
||||
|
||||
deleteAllInvitesForGroup(dto: { originatorUuid: string; groupUuid: string }): Promise<boolean>
|
||||
|
||||
deleteAllInboundInvites(dto: { userUuid: string }): Promise<void>
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { GroupUserPermission } from '../../GroupUser/Model/GroupUserPermission'
|
||||
|
||||
export type UpdateInviteDTO = {
|
||||
originatorUuid: string
|
||||
inviteUuid: string
|
||||
inviterPublicKey: string
|
||||
encryptedGroupData: string
|
||||
permissions?: GroupUserPermission
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { GroupUser } from '../Model/GroupUser'
|
||||
import { GroupUserHash } from './GroupUserHash'
|
||||
|
||||
export interface GroupUserFactoryInterface {
|
||||
create(dto: GroupUserHash): GroupUser
|
||||
createStub(dto: GroupUserHash): GroupUser
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
+6
-6
@@ -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',
|
||||
@@ -0,0 +1 @@
|
||||
export type GroupUserPermission = 'read' | 'write' | 'admin'
|
||||
+20
@@ -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<GroupUser | null>
|
||||
create(group: GroupUser): Promise<GroupUser>
|
||||
save(groupUser: GroupUser): Promise<GroupUser>
|
||||
remove(group: GroupUser): Promise<GroupUser>
|
||||
findAllForUser(query: GroupUserFindAllForUserQuery): Promise<GroupUser[]>
|
||||
findAllForGroup(query: GroupUserFindAllForGroup): Promise<GroupUser[]>
|
||||
findByUserUuidAndGroupUuid(userUuid: string, groupUuid: string): Promise<GroupUser | null>
|
||||
}
|
||||
@@ -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<GroupUser>) {}
|
||||
|
||||
async create(groupUser: GroupUser): Promise<GroupUser> {
|
||||
return this.ormRepository.save(groupUser)
|
||||
}
|
||||
|
||||
async save(groupUser: GroupUser): Promise<GroupUser> {
|
||||
return this.ormRepository.save(groupUser)
|
||||
}
|
||||
|
||||
findByUuid(uuid: string): Promise<GroupUser | null> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('group_user')
|
||||
.where('group_user.uuid = :uuid', {
|
||||
uuid,
|
||||
})
|
||||
.getOne()
|
||||
}
|
||||
|
||||
findByUserUuidAndGroupUuid(userUuid: string, groupUuid: string): Promise<GroupUser | null> {
|
||||
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<GroupUser> {
|
||||
return this.ormRepository.remove(groupUser)
|
||||
}
|
||||
|
||||
findAllForGroup(query: GroupUserFindAllForGroup): Promise<GroupUser[]> {
|
||||
return this.ormRepository
|
||||
.createQueryBuilder('group_user')
|
||||
.where('group_user.group_uuid = :groupUuid', { groupUuid: query.groupUuid })
|
||||
.getMany()
|
||||
}
|
||||
|
||||
async findAllForUser(query: GroupUserFindAllForUserQuery): Promise<GroupUser[]> {
|
||||
return this.createFindAllQueryBuilder(query).getMany()
|
||||
}
|
||||
|
||||
private createFindAllQueryBuilder(query: GroupUserFindAllForUserQuery): SelectQueryBuilder<GroupUser> {
|
||||
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
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
export interface GetVaultUsersDTO {
|
||||
export interface GetGroupUsersDTO {
|
||||
userUuid: string
|
||||
lastSyncTime?: number
|
||||
}
|
||||
@@ -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<GroupUser> {
|
||||
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<GroupUser[]> {
|
||||
return this.groupUserRepository.findAllForUser(dto)
|
||||
}
|
||||
|
||||
getUserForGroup(dto: { userUuid: string; groupUuid: string }): Promise<GroupUser | null> {
|
||||
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<boolean> {
|
||||
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<boolean> {
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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<GroupUser>
|
||||
|
||||
getAllGroupUsersForUser(dto: GetGroupUsersDTO): Promise<GroupUser[]>
|
||||
|
||||
getUserForGroup(dto: { userUuid: string; groupUuid: string }): Promise<GroupUser | null>
|
||||
|
||||
getGroupUsersForGroup(dto: {
|
||||
groupUuid: string
|
||||
originatorUuid: string
|
||||
}): Promise<{ users: GroupUser[]; isAdmin: boolean } | undefined>
|
||||
|
||||
deleteGroupUser(dto: { originatorUuid: string; groupUuid: string; userUuid: string }): Promise<boolean>
|
||||
|
||||
deleteAllGroupUsersForGroup(dto: { originatorUuid: string; groupUuid: string }): Promise<boolean>
|
||||
}
|
||||
@@ -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<void> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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<void> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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<void> {
|
||||
await this.updatePublicKeyOfAllContacts(event.payload)
|
||||
await this.nullifyPendingInboundVaultInvites(event.payload)
|
||||
await this.nullifyPendingInboundGroupInvites(event.payload)
|
||||
}
|
||||
|
||||
private async updatePublicKeyOfAllContacts(payload: UserCredentialsChangedEventPayload): Promise<void> {
|
||||
@@ -31,13 +31,13 @@ export class UserCredentialsChangedEventHandler implements DomainEventHandlerInt
|
||||
}
|
||||
}
|
||||
|
||||
private async nullifyPendingInboundVaultInvites(payload: UserCredentialsChangedEventPayload): Promise<void> {
|
||||
const inboundInvites = await this.vaultInviteRepository.findAll({
|
||||
private async nullifyPendingInboundGroupInvites(payload: UserCredentialsChangedEventPayload): Promise<void> {
|
||||
const inboundInvites = await this.groupInviteRepository.findAll({
|
||||
userUuid: payload.userUuid,
|
||||
})
|
||||
|
||||
for (const invite of inboundInvites) {
|
||||
await this.vaultInviteRepository.remove(invite)
|
||||
await this.groupInviteRepository.remove(invite)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<void> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -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<void> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
@@ -4,5 +4,5 @@ export type GetItemsDTO = {
|
||||
cursorToken?: string | null
|
||||
limit?: number
|
||||
contentType?: string
|
||||
vaultUuids?: string[] | null
|
||||
groupUuids?: string[] | null
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Item, ItemProjection>,
|
||||
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<Item> = []
|
||||
const conflicts: Array<ItemConflict> = []
|
||||
|
||||
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<void> {
|
||||
const processedVaultUuids = new Set()
|
||||
private async dynamicallyCreateGroupsBasedOnIncomingItems(dto: SaveItemsDTO): Promise<void> {
|
||||
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) {
|
||||
|
||||
@@ -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<ItemSaveRuleResult> {
|
||||
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<ItemSaveRuleResult> {
|
||||
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<ItemSaveRuleResult> {
|
||||
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<ItemSaveRuleResult> {
|
||||
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<ItemSaveRuleResult> {
|
||||
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<ItemSaveRuleResult> {
|
||||
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<ItemSaveRuleResult> {
|
||||
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<boolean> {
|
||||
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<GroupUserPermission | undefined> {
|
||||
const groupUser = await this.groupUserService.getUserForGroup({
|
||||
userUuid,
|
||||
groupUuid: groupUuid,
|
||||
})
|
||||
|
||||
if (groupUser) {
|
||||
return groupUser.permissions
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
+5
-5
@@ -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<ItemSaveRuleResult> {
|
||||
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,
|
||||
@@ -5,8 +5,8 @@ import { ConflictType } from '../../../Tmp/ConflictType'
|
||||
|
||||
export class OwnershipFilter implements ItemSaveRuleInterface {
|
||||
async check(dto: ItemSaveValidationDTO): Promise<ItemSaveRuleResult> {
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -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<ItemSaveRuleResult> {
|
||||
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<ItemSaveRuleResult> {
|
||||
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<ItemSaveRuleResult> {
|
||||
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<ItemSaveRuleResult> {
|
||||
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<ItemSaveRuleResult> {
|
||||
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<ItemSaveRuleResult> {
|
||||
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<ItemSaveRuleResult> {
|
||||
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<boolean> {
|
||||
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<VaultUserPermission | undefined> {
|
||||
const vaultUser = await this.vaultUserService.getUserForVault({
|
||||
userUuid,
|
||||
vaultUuid: vaultUuid,
|
||||
})
|
||||
|
||||
if (vaultUser) {
|
||||
return vaultUser.permissions
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -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<ContactProjection>
|
||||
vaults: Array<VaultProjection>
|
||||
vault_invites: Array<VaultInviteProjection>
|
||||
groups: Array<GroupProjection>
|
||||
group_invites: Array<GroupInviteProjection>
|
||||
}
|
||||
|
||||
+14
-14
@@ -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<Item, ItemProjection>,
|
||||
private itemConflictProjector: ProjectorInterface<ItemConflict, ItemConflictProjection>,
|
||||
private savedItemProjector: ProjectorInterface<Item, SavedItemProjection>,
|
||||
private vaultInviteProjector: ProjectorInterface<VaultInvite, VaultInviteProjection>,
|
||||
private groupInviteProjector: ProjectorInterface<GroupInvite, GroupInviteProjection>,
|
||||
private contactProjector: ProjectorInterface<Contact, ContactProjection>,
|
||||
private vaultProjector: ProjectorInterface<Vault, VaultProjection>,
|
||||
private groupProjector: ProjectorInterface<Group, GroupProjection>,
|
||||
) {}
|
||||
|
||||
async createResponse(syncItemsResponse: SyncItemsResponse): Promise<SyncResponse20200115> {
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
+31
@@ -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
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user