diff --git a/packages/api-gateway/src/Controller/AuthMiddleware.ts b/packages/api-gateway/src/Controller/AuthMiddleware.ts index 88f73d171..c6d1baa9b 100644 --- a/packages/api-gateway/src/Controller/AuthMiddleware.ts +++ b/packages/api-gateway/src/Controller/AuthMiddleware.ts @@ -27,16 +27,23 @@ export abstract class AuthMiddleware extends BaseMiddleware { } const authHeaderValue = request.headers.authorization as string + const sharedVaultOwnerContextHeaderValue = request.headers['x-shared-vault-owner-context'] as string | undefined + const cacheKey = `${authHeaderValue}${ + sharedVaultOwnerContextHeaderValue ? `:${sharedVaultOwnerContextHeaderValue}` : '' + }` try { let crossServiceTokenFetchedFromCache = true let crossServiceToken = null if (this.crossServiceTokenCacheTTL) { - crossServiceToken = await this.crossServiceTokenCache.get(authHeaderValue) + crossServiceToken = await this.crossServiceTokenCache.get(cacheKey) } if (crossServiceToken === null) { - const authResponse = await this.serviceProxy.validateSession(authHeaderValue) + const authResponse = await this.serviceProxy.validateSession({ + authorization: authHeaderValue, + sharedVaultOwnerContext: sharedVaultOwnerContextHeaderValue, + }) if (!this.handleSessionValidationResponse(authResponse, response, next)) { return @@ -52,7 +59,7 @@ export abstract class AuthMiddleware extends BaseMiddleware { if (this.crossServiceTokenCacheTTL && !crossServiceTokenFetchedFromCache) { await this.crossServiceTokenCache.set({ - authorizationHeaderValue: authHeaderValue, + key: cacheKey, encodedCrossServiceToken: crossServiceToken, expiresAtInSeconds: this.getCrossServiceTokenCacheExpireTimestamp(decodedToken), userUuid: decodedToken.user.uuid, @@ -62,6 +69,7 @@ export abstract class AuthMiddleware extends BaseMiddleware { response.locals.user = decodedToken.user response.locals.session = decodedToken.session response.locals.roles = decodedToken.roles + response.locals.sharedVaultOwnerContext = decodedToken.shared_vault_owner_context } catch (error) { const errorMessage = (error as AxiosError).isAxiosError ? JSON.stringify((error as AxiosError).response?.data) diff --git a/packages/api-gateway/src/Infra/InMemory/InMemoryCrossServiceTokenCache.ts b/packages/api-gateway/src/Infra/InMemory/InMemoryCrossServiceTokenCache.ts index cb77264e8..81c373f23 100644 --- a/packages/api-gateway/src/Infra/InMemory/InMemoryCrossServiceTokenCache.ts +++ b/packages/api-gateway/src/Infra/InMemory/InMemoryCrossServiceTokenCache.ts @@ -12,29 +12,29 @@ export class InMemoryCrossServiceTokenCache implements CrossServiceTokenCacheInt constructor(private timer: TimerInterface) {} async set(dto: { - authorizationHeaderValue: string + key: string encodedCrossServiceToken: string expiresAtInSeconds: number userUuid: string }): Promise { - let userAuthHeaders = [] - const userAuthHeadersJSON = this.crossServiceTokenCache.get(`${this.USER_CST_PREFIX}:${dto.userUuid}`) - if (userAuthHeadersJSON) { - userAuthHeaders = JSON.parse(userAuthHeadersJSON) + let userKeys = [] + const userKeysJSON = this.crossServiceTokenCache.get(`${this.USER_CST_PREFIX}:${dto.userUuid}`) + if (userKeysJSON) { + userKeys = JSON.parse(userKeysJSON) } - userAuthHeaders.push(dto.authorizationHeaderValue) + userKeys.push(dto.key) - this.crossServiceTokenCache.set(`${this.USER_CST_PREFIX}:${dto.userUuid}`, JSON.stringify(userAuthHeaders)) + this.crossServiceTokenCache.set(`${this.USER_CST_PREFIX}:${dto.userUuid}`, JSON.stringify(userKeys)) this.crossServiceTokenTTLCache.set(`${this.USER_CST_PREFIX}:${dto.userUuid}`, dto.expiresAtInSeconds) - this.crossServiceTokenCache.set(`${this.PREFIX}:${dto.authorizationHeaderValue}`, dto.encodedCrossServiceToken) - this.crossServiceTokenTTLCache.set(`${this.PREFIX}:${dto.authorizationHeaderValue}`, dto.expiresAtInSeconds) + this.crossServiceTokenCache.set(`${this.PREFIX}:${dto.key}`, dto.encodedCrossServiceToken) + this.crossServiceTokenTTLCache.set(`${this.PREFIX}:${dto.key}`, dto.expiresAtInSeconds) } - async get(authorizationHeaderValue: string): Promise { + async get(key: string): Promise { this.invalidateExpiredTokens() - const cachedToken = this.crossServiceTokenCache.get(`${this.PREFIX}:${authorizationHeaderValue}`) + const cachedToken = this.crossServiceTokenCache.get(`${this.PREFIX}:${key}`) if (!cachedToken) { return null } @@ -43,15 +43,15 @@ export class InMemoryCrossServiceTokenCache implements CrossServiceTokenCacheInt } async invalidate(userUuid: string): Promise { - let userAuthorizationHeaderValues = [] - const userAuthHeadersJSON = this.crossServiceTokenCache.get(`${this.USER_CST_PREFIX}:${userUuid}`) - if (userAuthHeadersJSON) { - userAuthorizationHeaderValues = JSON.parse(userAuthHeadersJSON) + let userKeyValues = [] + const userKeysJSON = this.crossServiceTokenCache.get(`${this.USER_CST_PREFIX}:${userUuid}`) + if (userKeysJSON) { + userKeyValues = JSON.parse(userKeysJSON) } - for (const authorizationHeaderValue of userAuthorizationHeaderValues) { - this.crossServiceTokenCache.delete(`${this.PREFIX}:${authorizationHeaderValue}`) - this.crossServiceTokenTTLCache.delete(`${this.PREFIX}:${authorizationHeaderValue}`) + for (const key of userKeyValues) { + this.crossServiceTokenCache.delete(`${this.PREFIX}:${key}`) + this.crossServiceTokenTTLCache.delete(`${this.PREFIX}:${key}`) } this.crossServiceTokenCache.delete(`${this.USER_CST_PREFIX}:${userUuid}`) this.crossServiceTokenTTLCache.delete(`${this.USER_CST_PREFIX}:${userUuid}`) diff --git a/packages/api-gateway/src/Infra/Redis/RedisCrossServiceTokenCache.ts b/packages/api-gateway/src/Infra/Redis/RedisCrossServiceTokenCache.ts index 4ffefbeb0..ab5502350 100644 --- a/packages/api-gateway/src/Infra/Redis/RedisCrossServiceTokenCache.ts +++ b/packages/api-gateway/src/Infra/Redis/RedisCrossServiceTokenCache.ts @@ -12,32 +12,32 @@ export class RedisCrossServiceTokenCache implements CrossServiceTokenCacheInterf constructor(@inject(TYPES.ApiGateway_Redis) private redisClient: IORedis.Redis) {} async set(dto: { - authorizationHeaderValue: string + key: string encodedCrossServiceToken: string expiresAtInSeconds: number userUuid: string }): Promise { const pipeline = this.redisClient.pipeline() - pipeline.sadd(`${this.USER_CST_PREFIX}:${dto.userUuid}`, dto.authorizationHeaderValue) + pipeline.sadd(`${this.USER_CST_PREFIX}:${dto.userUuid}`, dto.key) pipeline.expireat(`${this.USER_CST_PREFIX}:${dto.userUuid}`, dto.expiresAtInSeconds) - pipeline.set(`${this.PREFIX}:${dto.authorizationHeaderValue}`, dto.encodedCrossServiceToken) - pipeline.expireat(`${this.PREFIX}:${dto.authorizationHeaderValue}`, dto.expiresAtInSeconds) + pipeline.set(`${this.PREFIX}:${dto.key}`, dto.encodedCrossServiceToken) + pipeline.expireat(`${this.PREFIX}:${dto.key}`, dto.expiresAtInSeconds) await pipeline.exec() } - async get(authorizationHeaderValue: string): Promise { - return this.redisClient.get(`${this.PREFIX}:${authorizationHeaderValue}`) + async get(key: string): Promise { + return this.redisClient.get(`${this.PREFIX}:${key}`) } async invalidate(userUuid: string): Promise { - const userAuthorizationHeaderValues = await this.redisClient.smembers(`${this.USER_CST_PREFIX}:${userUuid}`) + const userKeyValues = await this.redisClient.smembers(`${this.USER_CST_PREFIX}:${userUuid}`) const pipeline = this.redisClient.pipeline() - for (const authorizationHeaderValue of userAuthorizationHeaderValues) { - pipeline.del(`${this.PREFIX}:${authorizationHeaderValue}`) + for (const key of userKeyValues) { + pipeline.del(`${this.PREFIX}:${key}`) } pipeline.del(`${this.USER_CST_PREFIX}:${userUuid}`) diff --git a/packages/api-gateway/src/Service/Cache/CrossServiceTokenCacheInterface.ts b/packages/api-gateway/src/Service/Cache/CrossServiceTokenCacheInterface.ts index 4483459ca..4f7428096 100644 --- a/packages/api-gateway/src/Service/Cache/CrossServiceTokenCacheInterface.ts +++ b/packages/api-gateway/src/Service/Cache/CrossServiceTokenCacheInterface.ts @@ -1,10 +1,10 @@ export interface CrossServiceTokenCacheInterface { set(dto: { - authorizationHeaderValue: string + key: string encodedCrossServiceToken: string expiresAtInSeconds: number userUuid: string }): Promise - get(authorizationHeaderValue: string): Promise + get(key: string): Promise invalidate(userUuid: string): Promise } diff --git a/packages/api-gateway/src/Service/Http/HttpServiceProxy.ts b/packages/api-gateway/src/Service/Http/HttpServiceProxy.ts index e999fe099..dea4fa3c6 100644 --- a/packages/api-gateway/src/Service/Http/HttpServiceProxy.ts +++ b/packages/api-gateway/src/Service/Http/HttpServiceProxy.ts @@ -24,14 +24,16 @@ export class HttpServiceProxy implements ServiceProxyInterface { @inject(TYPES.ApiGateway_Logger) private logger: Logger, ) {} - async validateSession( - authorizationHeaderValue: string, - ): Promise<{ status: number; data: unknown; headers: { contentType: string } }> { + async validateSession(headers: { + authorization: string + sharedVaultOwnerContext?: string + }): Promise<{ status: number; data: unknown; headers: { contentType: string } }> { const authResponse = await this.httpClient.request({ method: 'POST', headers: { - Authorization: authorizationHeaderValue, + Authorization: headers.authorization, Accept: 'application/json', + 'x-shared-vault-owner-context': headers.sharedVaultOwnerContext, }, validateStatus: (status: number) => { return status >= 200 && status < 500 diff --git a/packages/api-gateway/src/Service/Http/ServiceProxyInterface.ts b/packages/api-gateway/src/Service/Http/ServiceProxyInterface.ts index efb404832..e7f51cc28 100644 --- a/packages/api-gateway/src/Service/Http/ServiceProxyInterface.ts +++ b/packages/api-gateway/src/Service/Http/ServiceProxyInterface.ts @@ -50,7 +50,7 @@ export interface ServiceProxyInterface { endpointOrMethodIdentifier: string, payload?: Record | string, ): Promise - validateSession(authorizationHeaderValue: string): Promise<{ + validateSession(headers: { authorization: string; sharedVaultOwnerContext?: string }): Promise<{ status: number data: unknown headers: { diff --git a/packages/api-gateway/src/Service/Proxy/DirectCallServiceProxy.ts b/packages/api-gateway/src/Service/Proxy/DirectCallServiceProxy.ts index b53e0eecf..dd5311ea6 100644 --- a/packages/api-gateway/src/Service/Proxy/DirectCallServiceProxy.ts +++ b/packages/api-gateway/src/Service/Proxy/DirectCallServiceProxy.ts @@ -6,9 +6,10 @@ import { ServiceProxyInterface } from '../Http/ServiceProxyInterface' export class DirectCallServiceProxy implements ServiceProxyInterface { constructor(private serviceContainer: ServiceContainerInterface, private filesServerUrl: string) {} - async validateSession( - authorizationHeaderValue: string, - ): Promise<{ status: number; data: unknown; headers: { contentType: string } }> { + async validateSession(headers: { + authorization: string + sharedVaultOwnerContext?: string + }): Promise<{ status: number; data: unknown; headers: { contentType: string } }> { const authService = this.serviceContainer.get(ServiceIdentifier.create(ServiceIdentifier.NAMES.Auth).getValue()) if (!authService) { throw new Error('Auth service not found') @@ -17,7 +18,8 @@ export class DirectCallServiceProxy implements ServiceProxyInterface { const serviceResponse = (await authService.handleRequest( { headers: { - authorization: authorizationHeaderValue, + authorization: headers.authorization, + 'x-shared-vault-owner-context': headers.sharedVaultOwnerContext, }, } as never, {} as never, diff --git a/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.spec.ts b/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.spec.ts index 232c61b06..de28e172b 100644 --- a/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.spec.ts +++ b/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.spec.ts @@ -8,6 +8,8 @@ import { Role } from '../../Role/Role' import { UserRepositoryInterface } from '../../User/UserRepositoryInterface' import { CreateCrossServiceToken } from './CreateCrossServiceToken' +import { GetSetting } from '../GetSetting/GetSetting' +import { Result } from '@standardnotes/domain-core' describe('CreateCrossServiceToken', () => { let userProjector: ProjectorInterface @@ -15,6 +17,7 @@ describe('CreateCrossServiceToken', () => { let roleProjector: ProjectorInterface let tokenEncoder: TokenEncoderInterface let userRepository: UserRepositoryInterface + let getSettingUseCase: GetSetting const jwtTTL = 60 let session: Session @@ -22,7 +25,15 @@ describe('CreateCrossServiceToken', () => { let role: Role const createUseCase = () => - new CreateCrossServiceToken(userProjector, sessionProjector, roleProjector, tokenEncoder, userRepository, jwtTTL) + new CreateCrossServiceToken( + userProjector, + sessionProjector, + roleProjector, + tokenEncoder, + userRepository, + jwtTTL, + getSettingUseCase, + ) beforeEach(() => { session = {} as jest.Mocked @@ -50,6 +61,9 @@ describe('CreateCrossServiceToken', () => { userRepository = {} as jest.Mocked userRepository.findOneByUuid = jest.fn().mockReturnValue(user) + + getSettingUseCase = {} as jest.Mocked + getSettingUseCase.execute = jest.fn().mockReturnValue(Result.ok({ setting: { value: '100' } })) }) it('should create a cross service token for user', async () => { @@ -125,28 +139,74 @@ describe('CreateCrossServiceToken', () => { it('should throw an error if user does not exist', async () => { userRepository.findOneByUuid = jest.fn().mockReturnValue(null) - let caughtError = null - try { - await createUseCase().execute({ - userUuid: '00000000-0000-0000-0000-000000000000', - }) - } catch (error) { - caughtError = error - } + const result = await createUseCase().execute({ + userUuid: '00000000-0000-0000-0000-000000000000', + }) - expect(caughtError).not.toBeNull() + expect(result.isFailed()).toBeTruthy() }) it('should throw an error if user uuid is invalid', async () => { - let caughtError = null - try { - await createUseCase().execute({ - userUuid: 'invalid', - }) - } catch (error) { - caughtError = error - } + const result = await createUseCase().execute({ + userUuid: 'invalid', + }) - expect(caughtError).not.toBeNull() + expect(result.isFailed()).toBeTruthy() + }) + + describe('shared vault context', () => { + it('should add shared vault context if shared vault owner uuid is provided', async () => { + await createUseCase().execute({ + user, + session, + sharedVaultOwnerContext: '00000000-0000-0000-0000-000000000000', + }) + + expect(tokenEncoder.encodeExpirableToken).toHaveBeenCalledWith( + { + roles: [ + { + name: 'role1', + uuid: '1-3-4', + }, + ], + session: { + test: 'test', + }, + shared_vault_owner_context: { + upload_bytes_limit: 100, + }, + user: { + email: 'test@test.te', + uuid: '00000000-0000-0000-0000-000000000000', + }, + }, + 60, + ) + }) + + it('should throw an error if shared vault owner context is sensitive', async () => { + getSettingUseCase.execute = jest.fn().mockReturnValue(Result.ok({ sensitive: true })) + + const result = await createUseCase().execute({ + user, + session, + sharedVaultOwnerContext: '00000000-0000-0000-0000-000000000000', + }) + + expect(result.isFailed()).toBeTruthy() + }) + + it('should throw an error if it fails to retrieve shared vault owner setting', async () => { + getSettingUseCase.execute = jest.fn().mockReturnValue(Result.fail('Oops')) + + const result = await createUseCase().execute({ + user, + session, + sharedVaultOwnerContext: '00000000-0000-0000-0000-000000000000', + }) + + expect(result.isFailed()).toBeTruthy() + }) }) }) diff --git a/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.ts b/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.ts index 068066239..75a1a5ce7 100644 --- a/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.ts +++ b/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceToken.ts @@ -1,5 +1,6 @@ import { TokenEncoderInterface, CrossServiceTokenData } from '@standardnotes/security' import { inject, injectable } from 'inversify' +import { Result, UseCaseInterface, Uuid } from '@standardnotes/domain-core' import TYPES from '../../../Bootstrap/Types' import { ProjectorInterface } from '../../../Projection/ProjectorInterface' @@ -7,14 +8,13 @@ import { Role } from '../../Role/Role' import { Session } from '../../Session/Session' import { User } from '../../User/User' import { UserRepositoryInterface } from '../../User/UserRepositoryInterface' -import { UseCaseInterface } from '../UseCaseInterface' import { CreateCrossServiceTokenDTO } from './CreateCrossServiceTokenDTO' -import { CreateCrossServiceTokenResponse } from './CreateCrossServiceTokenResponse' -import { Uuid } from '@standardnotes/domain-core' +import { GetSetting } from '../GetSetting/GetSetting' +import { SettingName } from '@standardnotes/settings' @injectable() -export class CreateCrossServiceToken implements UseCaseInterface { +export class CreateCrossServiceToken implements UseCaseInterface { constructor( @inject(TYPES.Auth_UserProjector) private userProjector: ProjectorInterface, @inject(TYPES.Auth_SessionProjector) private sessionProjector: ProjectorInterface, @@ -22,14 +22,16 @@ export class CreateCrossServiceToken implements UseCaseInterface { @inject(TYPES.Auth_CrossServiceTokenEncoder) private tokenEncoder: TokenEncoderInterface, @inject(TYPES.Auth_UserRepository) private userRepository: UserRepositoryInterface, @inject(TYPES.Auth_AUTH_JWT_TTL) private jwtTTL: number, + @inject(TYPES.Auth_GetSetting) + private getSettingUseCase: GetSetting, ) {} - async execute(dto: CreateCrossServiceTokenDTO): Promise { + async execute(dto: CreateCrossServiceTokenDTO): Promise> { let user: User | undefined | null = dto.user if (user === undefined && dto.userUuid !== undefined) { const userUuidOrError = Uuid.create(dto.userUuid) if (userUuidOrError.isFailed()) { - throw new Error(userUuidOrError.getError()) + return Result.fail(userUuidOrError.getError()) } const userUuid = userUuidOrError.getValue() @@ -37,7 +39,7 @@ export class CreateCrossServiceToken implements UseCaseInterface { } if (!user) { - throw new Error(`Could not find user with uuid ${dto.userUuid}`) + return Result.fail(`Could not find user with uuid ${dto.userUuid}`) } const roles = await user.roles @@ -45,15 +47,33 @@ export class CreateCrossServiceToken implements UseCaseInterface { const authTokenData: CrossServiceTokenData = { user: this.projectUser(user), roles: this.projectRoles(roles), + shared_vault_owner_context: undefined, + } + + if (dto.sharedVaultOwnerContext !== undefined) { + const uploadBytesLimitSettingOrError = await this.getSettingUseCase.execute({ + settingName: SettingName.NAMES.FileUploadBytesLimit, + userUuid: dto.sharedVaultOwnerContext, + }) + if (uploadBytesLimitSettingOrError.isFailed()) { + return Result.fail(uploadBytesLimitSettingOrError.getError()) + } + const uploadBytesLimitSetting = uploadBytesLimitSettingOrError.getValue() + if (uploadBytesLimitSetting.sensitive) { + return Result.fail('Shared vault owner upload bytes limit setting is sensitive!') + } + const uploadBytesLimit = parseInt(uploadBytesLimitSetting.setting.value as string) + + authTokenData.shared_vault_owner_context = { + upload_bytes_limit: uploadBytesLimit, + } } if (dto.session !== undefined) { authTokenData.session = this.projectSession(dto.session) } - return { - token: this.tokenEncoder.encodeExpirableToken(authTokenData, this.jwtTTL), - } + return Result.ok(this.tokenEncoder.encodeExpirableToken(authTokenData, this.jwtTTL)) } private projectUser(user: User): { uuid: string; email: string } { diff --git a/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceTokenDTO.ts b/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceTokenDTO.ts index c79b7ee1a..346fdb773 100644 --- a/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceTokenDTO.ts +++ b/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceTokenDTO.ts @@ -6,6 +6,7 @@ export type CreateCrossServiceTokenDTO = Either< { user: User session?: Session + sharedVaultOwnerContext?: string }, { userUuid: string diff --git a/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceTokenResponse.ts b/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceTokenResponse.ts deleted file mode 100644 index ef3dcbaad..000000000 --- a/packages/auth/src/Domain/UseCase/CreateCrossServiceToken/CreateCrossServiceTokenResponse.ts +++ /dev/null @@ -1,3 +0,0 @@ -export type CreateCrossServiceTokenResponse = { - token: string -} diff --git a/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.spec.ts b/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.spec.ts index d0d28d75a..16bf899fe 100644 --- a/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.spec.ts +++ b/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.spec.ts @@ -73,35 +73,30 @@ describe('GetSetting', () => { describe('no subscription', () => { it('should find a setting for user', async () => { - expect( - await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.NAMES.DropboxBackupFrequency }), - ).toEqual({ - success: true, + const result = await createUseCase().execute({ + userUuid: '1-2-3', + settingName: SettingName.NAMES.DropboxBackupFrequency, + }) + expect(result.isFailed()).toBeFalsy() + expect(result.getValue()).toEqual({ userUuid: '1-2-3', setting: { foo: 'bar' }, }) }) it('should not find a setting if the setting name is invalid', async () => { - expect(await createUseCase().execute({ userUuid: '1-2-3', settingName: 'invalid' })).toEqual({ - success: false, - error: { - message: 'Invalid setting name: invalid', - }, - }) + const result = await createUseCase().execute({ userUuid: '1-2-3', settingName: 'invalid' }) + expect(result.isFailed()).toBeTruthy() }) it('should not get a setting for user if it does not exist', async () => { settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(null) - expect( - await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.NAMES.DropboxBackupFrequency }), - ).toEqual({ - success: false, - error: { - message: 'Setting DROPBOX_BACKUP_FREQUENCY for user 1-2-3 not found!', - }, + const result = await createUseCase().execute({ + userUuid: '1-2-3', + settingName: SettingName.NAMES.DropboxBackupFrequency, }) + expect(result.isFailed()).toBeTruthy() }) it('should not retrieve a sensitive setting for user', async () => { @@ -112,21 +107,19 @@ describe('GetSetting', () => { settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(setting) - expect(await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.NAMES.MfaSecret })).toEqual({ - success: true, + const result = await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.NAMES.MfaSecret }) + expect(result.isFailed()).toBeFalsy() + expect(result.getValue()).toEqual({ sensitive: true, }) }) it('should not retrieve a subscription setting for user', async () => { - expect( - await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.NAMES.MuteSignInEmails }), - ).toEqual({ - success: false, - error: { - message: 'No subscription found.', - }, + const result = await createUseCase().execute({ + userUuid: '1-2-3', + settingName: SettingName.NAMES.MuteSignInEmails, }) + expect(result.isFailed()).toBeTruthy() }) it('should retrieve a sensitive setting for user if explicitly told to', async () => { @@ -137,14 +130,13 @@ describe('GetSetting', () => { settingService.findSettingWithDecryptedValue = jest.fn().mockReturnValue(setting) - expect( - await createUseCase().execute({ - userUuid: '1-2-3', - settingName: SettingName.NAMES.MfaSecret, - allowSensitiveRetrieval: true, - }), - ).toEqual({ - success: true, + const result = await createUseCase().execute({ + userUuid: '1-2-3', + settingName: SettingName.NAMES.MfaSecret, + allowSensitiveRetrieval: true, + }) + expect(result.isFailed()).toBeFalsy() + expect(result.getValue()).toEqual({ userUuid: '1-2-3', setting: { foo: 'bar' }, }) @@ -159,10 +151,12 @@ describe('GetSetting', () => { }) it('should find a setting for user', async () => { - expect( - await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.NAMES.MuteSignInEmails }), - ).toEqual({ - success: true, + const result = await createUseCase().execute({ + userUuid: '1-2-3', + settingName: SettingName.NAMES.MuteSignInEmails, + }) + expect(result.isFailed()).toBeFalsy() + expect(result.getValue()).toEqual({ userUuid: '1-2-3', setting: { foo: 'sub-bar' }, }) @@ -171,14 +165,11 @@ describe('GetSetting', () => { it('should not get a suscription setting for user if it does not exist', async () => { subscriptionSettingService.findSubscriptionSettingWithDecryptedValue = jest.fn().mockReturnValue(null) - expect( - await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.NAMES.MuteSignInEmails }), - ).toEqual({ - success: false, - error: { - message: 'Subscription setting MUTE_SIGN_IN_EMAILS for user 1-2-3 not found!', - }, + const result = await createUseCase().execute({ + userUuid: '1-2-3', + settingName: SettingName.NAMES.MuteSignInEmails, }) + expect(result.isFailed()).toBeTruthy() }) it('should not retrieve a sensitive subscription setting for user', async () => { @@ -188,10 +179,12 @@ describe('GetSetting', () => { .fn() .mockReturnValue(subscriptionSetting) - expect( - await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.NAMES.MuteSignInEmails }), - ).toEqual({ - success: true, + const result = await createUseCase().execute({ + userUuid: '1-2-3', + settingName: SettingName.NAMES.MuteSignInEmails, + }) + expect(result.isFailed()).toBeFalsy() + expect(result.getValue()).toEqual({ sensitive: true, }) }) @@ -205,10 +198,12 @@ describe('GetSetting', () => { }) it('should find a setting for user', async () => { - expect( - await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.NAMES.MuteSignInEmails }), - ).toEqual({ - success: true, + const result = await createUseCase().execute({ + userUuid: '1-2-3', + settingName: SettingName.NAMES.MuteSignInEmails, + }) + expect(result.isFailed()).toBeFalsy() + expect(result.getValue()).toEqual({ userUuid: '1-2-3', setting: { foo: 'sub-bar' }, }) @@ -221,10 +216,12 @@ describe('GetSetting', () => { }) it('should find a regular subscription only setting for user', async () => { - expect( - await createUseCase().execute({ userUuid: '1-2-3', settingName: SettingName.NAMES.FileUploadBytesLimit }), - ).toEqual({ - success: true, + const result = await createUseCase().execute({ + userUuid: '1-2-3', + settingName: SettingName.NAMES.FileUploadBytesLimit, + }) + expect(result.isFailed()).toBeFalsy() + expect(result.getValue()).toEqual({ userUuid: '1-2-3', setting: { foo: 'sub-bar' }, }) diff --git a/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.ts b/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.ts index e606b87a4..3890b8716 100644 --- a/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.ts +++ b/packages/auth/src/Domain/UseCase/GetSetting/GetSetting.ts @@ -1,7 +1,7 @@ import { SettingName } from '@standardnotes/settings' import { inject, injectable } from 'inversify' +import { Result, UseCaseInterface } from '@standardnotes/domain-core' -import { UseCaseInterface } from '../UseCaseInterface' import TYPES from '../../../Bootstrap/Types' import { SettingProjector } from '../../../Projection/SettingProjector' import { SettingServiceInterface } from '../../Setting/SettingServiceInterface' @@ -14,7 +14,7 @@ import { GetSettingResponse } from './GetSettingResponse' import { UserSubscription } from '../../Subscription/UserSubscription' @injectable() -export class GetSetting implements UseCaseInterface { +export class GetSetting implements UseCaseInterface { constructor( @inject(TYPES.Auth_SettingProjector) private settingProjector: SettingProjector, @inject(TYPES.Auth_SubscriptionSettingProjector) private subscriptionSettingProjector: SubscriptionSettingProjector, @@ -24,15 +24,10 @@ export class GetSetting implements UseCaseInterface { @inject(TYPES.Auth_UserSubscriptionService) private userSubscriptionService: UserSubscriptionServiceInterface, ) {} - async execute(dto: GetSettingDto): Promise { + async execute(dto: GetSettingDto): Promise> { const settingNameOrError = SettingName.create(dto.settingName) if (settingNameOrError.isFailed()) { - return { - success: false, - error: { - message: settingNameOrError.getError(), - }, - } + return Result.fail(settingNameOrError.getError()) } const settingName = settingNameOrError.getValue() @@ -47,12 +42,7 @@ export class GetSetting implements UseCaseInterface { } if (!subscription) { - return { - success: false, - error: { - message: 'No subscription found.', - }, - } + return Result.fail('No subscription found.') } const subscriptionSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({ @@ -62,28 +52,21 @@ export class GetSetting implements UseCaseInterface { }) if (subscriptionSetting === null) { - return { - success: false, - error: { - message: `Subscription setting ${settingName.value} for user ${dto.userUuid} not found!`, - }, - } + return Result.fail(`Subscription setting ${settingName.value} for user ${dto.userUuid} not found!`) } if (subscriptionSetting.sensitive && !dto.allowSensitiveRetrieval) { - return { - success: true, + return Result.ok({ sensitive: true, - } + }) } const simpleSubscriptionSetting = await this.subscriptionSettingProjector.projectSimple(subscriptionSetting) - return { - success: true, + return Result.ok({ userUuid: dto.userUuid, setting: simpleSubscriptionSetting, - } + }) } const setting = await this.settingService.findSettingWithDecryptedValue({ @@ -92,27 +75,20 @@ export class GetSetting implements UseCaseInterface { }) if (setting === null) { - return { - success: false, - error: { - message: `Setting ${settingName.value} for user ${dto.userUuid} not found!`, - }, - } + return Result.fail(`Setting ${settingName.value} for user ${dto.userUuid} not found!`) } if (setting.sensitive && !dto.allowSensitiveRetrieval) { - return { - success: true, + return Result.ok({ sensitive: true, - } + }) } const simpleSetting = await this.settingProjector.projectSimple(setting) - return { - success: true, + return Result.ok({ userUuid: dto.userUuid, setting: simpleSetting, - } + }) } } diff --git a/packages/auth/src/Domain/UseCase/GetSetting/GetSettingResponse.ts b/packages/auth/src/Domain/UseCase/GetSetting/GetSettingResponse.ts index 48ea4f74d..ed812dbd4 100644 --- a/packages/auth/src/Domain/UseCase/GetSetting/GetSettingResponse.ts +++ b/packages/auth/src/Domain/UseCase/GetSetting/GetSettingResponse.ts @@ -1,18 +1,13 @@ +import { Either } from '@standardnotes/common' + import { SimpleSetting } from '../../Setting/SimpleSetting' -export type GetSettingResponse = - | { - success: true - userUuid: string - setting: SimpleSetting - } - | { - success: true - sensitive: true - } - | { - success: false - error: { - message: string - } - } +export type GetSettingResponse = Either< + { + userUuid: string + setting: SimpleSetting + }, + { + sensitive: true + } +> diff --git a/packages/auth/src/Domain/UseCase/UpdateStorageQuotaUsedForUser/UpdateStorageQuotaUsedForUser.ts b/packages/auth/src/Domain/UseCase/UpdateStorageQuotaUsedForUser/UpdateStorageQuotaUsedForUser.ts index d858d3f55..abf6550dd 100644 --- a/packages/auth/src/Domain/UseCase/UpdateStorageQuotaUsedForUser/UpdateStorageQuotaUsedForUser.ts +++ b/packages/auth/src/Domain/UseCase/UpdateStorageQuotaUsedForUser/UpdateStorageQuotaUsedForUser.ts @@ -7,7 +7,6 @@ import { UserSubscription } from '../../Subscription/UserSubscription' import { UserSubscriptionServiceInterface } from '../../Subscription/UserSubscriptionServiceInterface' import { UserRepositoryInterface } from '../../User/UserRepositoryInterface' import { UpdateStorageQuotaUsedForUserDTO } from './UpdateStorageQuotaUsedForUserDTO' -import { User } from '../../User/User' export class UpdateStorageQuotaUsedForUser implements UseCaseInterface { constructor( @@ -34,23 +33,20 @@ export class UpdateStorageQuotaUsedForUser implements UseCaseInterface { return Result.fail(`Could not find regular user subscription for user with uuid: ${userUuid.value}`) } - await this.updateUploadBytesUsedSetting(regularSubscription, user, dto.bytesUsed) + await this.updateUploadBytesUsedSetting(regularSubscription, dto.bytesUsed) if (sharedSubscription !== null) { - await this.updateUploadBytesUsedSetting(sharedSubscription, user, dto.bytesUsed) + await this.updateUploadBytesUsedSetting(sharedSubscription, dto.bytesUsed) } return Result.ok() } - private async updateUploadBytesUsedSetting( - subscription: UserSubscription, - user: User, - bytesUsed: number, - ): Promise { + private async updateUploadBytesUsedSetting(subscription: UserSubscription, bytesUsed: number): Promise { let bytesAlreadyUsed = '0' + const subscriptionUser = await subscription.user const bytesUsedSetting = await this.subscriptionSettingService.findSubscriptionSettingWithDecryptedValue({ - userUuid: (await subscription.user).uuid, + userUuid: subscriptionUser.uuid, userSubscriptionUuid: subscription.uuid, subscriptionSettingName: SettingName.create(SettingName.NAMES.FileUploadBytesUsed).getValue(), }) @@ -60,7 +56,7 @@ export class UpdateStorageQuotaUsedForUser implements UseCaseInterface { await this.subscriptionSettingService.createOrReplace({ userSubscription: subscription, - user, + user: subscriptionUser, props: { name: SettingName.NAMES.FileUploadBytesUsed, unencryptedValue: (+bytesAlreadyUsed + bytesUsed).toString(), diff --git a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedInternalController.spec.ts b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedInternalController.spec.ts index b7581f1aa..cbffa7570 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedInternalController.spec.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedInternalController.spec.ts @@ -7,6 +7,7 @@ import { results } from 'inversify-express-utils' import { User } from '../../Domain/User/User' import { GetUserFeatures } from '../../Domain/UseCase/GetUserFeatures/GetUserFeatures' import { GetSetting } from '../../Domain/UseCase/GetSetting/GetSetting' +import { Result } from '@standardnotes/domain-core' describe('AnnotatedInternalController', () => { let getUserFeatures: GetUserFeatures @@ -73,7 +74,7 @@ describe('AnnotatedInternalController', () => { request.params.userUuid = '1-2-3' request.params.settingName = 'foobar' - getSetting.execute = jest.fn().mockReturnValue({ success: true }) + getSetting.execute = jest.fn().mockReturnValue(Result.ok()) const httpResponse = await createController().getSetting(request) const result = await httpResponse.executeAsync() @@ -91,7 +92,7 @@ describe('AnnotatedInternalController', () => { request.params.userUuid = '1-2-3' request.params.settingName = 'foobar' - getSetting.execute = jest.fn().mockReturnValue({ success: false }) + getSetting.execute = jest.fn().mockReturnValue(Result.fail('Oops')) const httpResponse = await createController().getSetting(request) const result = await httpResponse.executeAsync() diff --git a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedInternalController.ts b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedInternalController.ts index be916ccc2..3ace51c69 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedInternalController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedInternalController.ts @@ -36,16 +36,26 @@ export class AnnotatedInternalController extends BaseHttpController { @httpGet('/users/:userUuid/settings/:settingName') async getSetting(request: Request): Promise { - const result = await this.doGetSetting.execute({ + const resultOrError = await this.doGetSetting.execute({ userUuid: request.params.userUuid, settingName: request.params.settingName, allowSensitiveRetrieval: true, }) - if (result.success) { - return this.json(result) + if (resultOrError.isFailed()) { + return this.json( + { + error: { + message: resultOrError.getError(), + }, + }, + 400, + ) } - return this.json(result, 400) + return this.json({ + success: true, + ...resultOrError.getValue(), + }) } } diff --git a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedSessionsController.spec.ts b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedSessionsController.spec.ts index c3a1ffe1d..094bc5939 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedSessionsController.spec.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedSessionsController.spec.ts @@ -11,6 +11,7 @@ import { CreateCrossServiceToken } from '../../Domain/UseCase/CreateCrossService import { GetActiveSessionsForUser } from '../../Domain/UseCase/GetActiveSessionsForUser' import { ProjectorInterface } from '../../Projection/ProjectorInterface' import { Session } from '../../Domain/Session/Session' +import { Result } from '@standardnotes/domain-core' describe('AnnotatedSessionsController', () => { let getActiveSessionsForUser: GetActiveSessionsForUser @@ -45,7 +46,7 @@ describe('AnnotatedSessionsController', () => { sessionProjector.projectCustom = jest.fn().mockReturnValue({ foo: 'bar' }) createCrossServiceToken = {} as jest.Mocked - createCrossServiceToken.execute = jest.fn().mockReturnValue({ token: 'foobar' }) + createCrossServiceToken.execute = jest.fn().mockReturnValue(Result.ok('foobar')) request = { params: {}, diff --git a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedSettingsController.spec.ts b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedSettingsController.spec.ts index 97a6bbc98..740bd510c 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedSettingsController.spec.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedSettingsController.spec.ts @@ -10,6 +10,7 @@ import { GetSetting } from '../../Domain/UseCase/GetSetting/GetSetting' import { GetSettings } from '../../Domain/UseCase/GetSettings/GetSettings' import { UpdateSetting } from '../../Domain/UseCase/UpdateSetting/UpdateSetting' import { User } from '../../Domain/User/User' +import { Result } from '@standardnotes/domain-core' describe('AnnotatedSettingsController', () => { let deleteSetting: DeleteSetting @@ -85,7 +86,7 @@ describe('AnnotatedSettingsController', () => { uuid: '1-2-3', } - getSetting.execute = jest.fn().mockReturnValue({ success: true }) + getSetting.execute = jest.fn().mockReturnValue(Result.ok()) const httpResponse = await createController().getSetting(request, response) const result = await httpResponse.executeAsync() @@ -119,7 +120,7 @@ describe('AnnotatedSettingsController', () => { uuid: '1-2-3', } - getSetting.execute = jest.fn().mockReturnValue({ success: false }) + getSetting.execute = jest.fn().mockReturnValue(Result.fail('Oops')) const httpResponse = await createController().getSetting(request, response) const result = await httpResponse.executeAsync() diff --git a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedSubscriptionSettingsController.spec.ts b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedSubscriptionSettingsController.spec.ts index b152b6b76..522ddf353 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedSubscriptionSettingsController.spec.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedSubscriptionSettingsController.spec.ts @@ -6,6 +6,7 @@ import { results } from 'inversify-express-utils' import { AnnotatedSubscriptionSettingsController } from './AnnotatedSubscriptionSettingsController' import { User } from '../../Domain/User/User' import { GetSetting } from '../../Domain/UseCase/GetSetting/GetSetting' +import { Result } from '@standardnotes/domain-core' describe('AnnotatedSubscriptionSettingsController', () => { let getSetting: GetSetting @@ -41,7 +42,7 @@ describe('AnnotatedSubscriptionSettingsController', () => { uuid: '1-2-3', } - getSetting.execute = jest.fn().mockReturnValue({ success: true }) + getSetting.execute = jest.fn().mockReturnValue(Result.ok()) const httpResponse = await createController().getSubscriptionSetting(request, response) const result = await httpResponse.executeAsync() @@ -58,7 +59,7 @@ describe('AnnotatedSubscriptionSettingsController', () => { uuid: '1-2-3', } - getSetting.execute = jest.fn().mockReturnValue({ success: false }) + getSetting.execute = jest.fn().mockReturnValue(Result.fail('Oops')) const httpResponse = await createController().getSubscriptionSetting(request, response) const result = await httpResponse.executeAsync() diff --git a/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseSessionsController.ts b/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseSessionsController.ts index d9ca8b770..82d2e22df 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseSessionsController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseSessionsController.ts @@ -45,12 +45,25 @@ export class BaseSessionsController extends BaseHttpController { const user = authenticateRequestResponse.user as User - const result = await this.createCrossServiceToken.execute({ + const sharedVaultOwnerContext = request.headers['x-shared-vault-owner-context'] as string | undefined + + const resultOrError = await this.createCrossServiceToken.execute({ user, session: authenticateRequestResponse.session, + sharedVaultOwnerContext, }) + if (resultOrError.isFailed()) { + return this.json( + { + error: { + message: resultOrError.getError(), + }, + }, + 400, + ) + } - return this.json({ authToken: result.token }) + return this.json({ authToken: resultOrError.getValue() }) } async getSessions(_request: Request, response: Response): Promise { diff --git a/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseSettingsController.ts b/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseSettingsController.ts index 5087c97ec..015e3ab5f 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseSettingsController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseSettingsController.ts @@ -58,13 +58,22 @@ export class BaseSettingsController extends BaseHttpController { } const { userUuid, settingName } = request.params - const result = await this.doGetSetting.execute({ userUuid, settingName: settingName.toUpperCase() }) - - if (result.success) { - return this.json(result) + const resultOrError = await this.doGetSetting.execute({ userUuid, settingName: settingName.toUpperCase() }) + if (resultOrError.isFailed()) { + return this.json( + { + error: { + message: resultOrError.getError(), + }, + }, + 400, + ) } - return this.json(result, 400) + return this.json({ + success: true, + ...resultOrError.getValue(), + }) } async updateSetting(request: Request, response: Response): Promise { diff --git a/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseSubscriptionSettingsController.ts b/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseSubscriptionSettingsController.ts index 93905ab2b..025fe6ff4 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseSubscriptionSettingsController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseSubscriptionSettingsController.ts @@ -14,15 +14,25 @@ export class BaseSubscriptionSettingsController extends BaseHttpController { } async getSubscriptionSetting(request: Request, response: Response): Promise { - const result = await this.doGetSetting.execute({ + const resultOrError = await this.doGetSetting.execute({ userUuid: response.locals.user.uuid, settingName: request.params.subscriptionSettingName.toUpperCase(), }) - if (result.success) { - return this.json(result) + if (resultOrError.isFailed()) { + return this.json( + { + error: { + message: resultOrError.getError(), + }, + }, + 400, + ) } - return this.json(result, 400) + return this.json({ + success: true, + ...resultOrError.getValue(), + }) } } diff --git a/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseWebSocketsController.ts b/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseWebSocketsController.ts index 9e079c249..0b80aaedc 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseWebSocketsController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseWebSocketsController.ts @@ -46,10 +46,20 @@ export class BaseWebSocketsController extends BaseHttpController { ) } - const result = await this.createCrossServiceToken.execute({ + const resultOrError = await this.createCrossServiceToken.execute({ userUuid: token.userUuid, }) + if (resultOrError.isFailed()) { + return this.json( + { + error: { + message: resultOrError.getError(), + }, + }, + 400, + ) + } - return this.json({ authToken: result.token }) + return this.json({ authToken: resultOrError.getValue() }) } } diff --git a/packages/files/src/Infra/InversifyExpress/AnnotatedSharedVaultFilesController.ts b/packages/files/src/Infra/InversifyExpress/AnnotatedSharedVaultFilesController.ts index 07e3d3933..07f6d7340 100644 --- a/packages/files/src/Infra/InversifyExpress/AnnotatedSharedVaultFilesController.ts +++ b/packages/files/src/Infra/InversifyExpress/AnnotatedSharedVaultFilesController.ts @@ -121,6 +121,10 @@ export class AnnotatedSharedVaultFilesController extends BaseHttpController { return this.badRequest('Not permitted for this operation') } + if (locals.uploadBytesLimit === undefined) { + return this.badRequest('Missing upload bytes limit') + } + const result = await this.finishUploadSession.execute({ userUuid: locals.vaultOwnerUuid, sharedVaultUuid: locals.sharedVaultUuid, diff --git a/packages/files/src/Infra/InversifyExpress/Middleware/SharedVaultValetTokenAuthMiddleware.ts b/packages/files/src/Infra/InversifyExpress/Middleware/SharedVaultValetTokenAuthMiddleware.ts index 71473cc49..4be3e9f98 100644 --- a/packages/files/src/Infra/InversifyExpress/Middleware/SharedVaultValetTokenAuthMiddleware.ts +++ b/packages/files/src/Infra/InversifyExpress/Middleware/SharedVaultValetTokenAuthMiddleware.ts @@ -1,4 +1,4 @@ -import { SharedVaultValetTokenData, TokenDecoderInterface } from '@standardnotes/security' +import { SharedVaultValetTokenData, TokenDecoderInterface, ValetTokenOperation } from '@standardnotes/security' import { Uuid } from '@standardnotes/domain-core' import { NextFunction, Request, Response } from 'express' import { inject, injectable } from 'inversify' @@ -61,6 +61,17 @@ export class SharedVaultValetTokenAuthMiddleware extends BaseMiddleware { return } + if (this.userHasNoSpaceToUpload(valetTokenData)) { + response.status(403).send({ + error: { + tag: 'no-space', + message: 'The file you are trying to upload is too big. Please ask the vault owner to upgrade subscription', + }, + }) + + return + } + const whitelistedData: SharedVaultValetTokenData = { sharedVaultUuid: valetTokenData.sharedVaultUuid, vaultOwnerUuid: valetTokenData.vaultOwnerUuid, @@ -79,4 +90,32 @@ export class SharedVaultValetTokenAuthMiddleware extends BaseMiddleware { return next(error) } } + + private userHasNoSpaceToUpload(valetTokenData: SharedVaultValetTokenData) { + if (![ValetTokenOperation.Write, ValetTokenOperation.Move].includes(valetTokenData.permittedOperation)) { + return false + } + + if (valetTokenData.uploadBytesLimit === -1) { + return false + } + + const isMovingToNonSharedVault = + valetTokenData.permittedOperation === ValetTokenOperation.Move && + valetTokenData.moveOperation?.type === 'shared-vault-to-user' + + if (isMovingToNonSharedVault) { + return false + } + + if (valetTokenData.uploadBytesLimit === undefined) { + return true + } + + const remainingUploadSpace = valetTokenData.uploadBytesLimit - valetTokenData.uploadBytesUsed + + const consideredUploadSize = valetTokenData.unencryptedFileSize as number + + return remainingUploadSpace - consideredUploadSize <= 0 + } } diff --git a/packages/security/src/Domain/Token/CrossServiceTokenData.ts b/packages/security/src/Domain/Token/CrossServiceTokenData.ts index e29820a10..0c307d9ed 100644 --- a/packages/security/src/Domain/Token/CrossServiceTokenData.ts +++ b/packages/security/src/Domain/Token/CrossServiceTokenData.ts @@ -5,6 +5,9 @@ export type CrossServiceTokenData = { uuid: string email: string } + shared_vault_owner_context?: { + upload_bytes_limit: number + } roles: Array session?: { uuid: string diff --git a/packages/security/src/Domain/Token/SharedVaultValetTokenData.ts b/packages/security/src/Domain/Token/SharedVaultValetTokenData.ts index ba3948b40..eb3f3953b 100644 --- a/packages/security/src/Domain/Token/SharedVaultValetTokenData.ts +++ b/packages/security/src/Domain/Token/SharedVaultValetTokenData.ts @@ -8,7 +8,7 @@ export interface SharedVaultValetTokenData { remoteIdentifier: string unencryptedFileSize?: number uploadBytesUsed: number - uploadBytesLimit: number + uploadBytesLimit?: number moveOperation?: { type: SharedVaultMoveType fromUuid: string diff --git a/packages/syncing-server/migrations/mysql/1692619430384-remove-shared-vault-limit.ts b/packages/syncing-server/migrations/mysql/1692619430384-remove-shared-vault-limit.ts new file mode 100644 index 000000000..9cfdcbef0 --- /dev/null +++ b/packages/syncing-server/migrations/mysql/1692619430384-remove-shared-vault-limit.ts @@ -0,0 +1,13 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class RemoveSharedVaultLimit1692619430384 implements MigrationInterface { + name = 'RemoveSharedVaultLimit1692619430384' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query('ALTER TABLE `shared_vaults` DROP COLUMN `file_upload_bytes_limit`') + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query('ALTER TABLE `shared_vaults` ADD `file_upload_bytes_limit` int NOT NULL') + } +} diff --git a/packages/syncing-server/migrations/sqlite/1692619677621-remove-shared-vault-limit.ts b/packages/syncing-server/migrations/sqlite/1692619677621-remove-shared-vault-limit.ts new file mode 100644 index 000000000..09083b046 --- /dev/null +++ b/packages/syncing-server/migrations/sqlite/1692619677621-remove-shared-vault-limit.ts @@ -0,0 +1,20 @@ +import { MigrationInterface, QueryRunner } from 'typeorm' + +export class RemoveSharedVaultLimit1692619677621 implements MigrationInterface { + name = 'RemoveSharedVaultLimit1692619677621' + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query('ALTER TABLE "shared_vaults" RENAME TO "temporary_shared_vaults"') + await queryRunner.query( + 'CREATE TABLE "shared_vaults" ("uuid" varchar PRIMARY KEY NOT NULL, "user_uuid" varchar(36) NOT NULL, "file_upload_bytes_used" integer NOT NULL, "created_at_timestamp" bigint NOT NULL, "updated_at_timestamp" bigint NOT NULL)', + ) + await queryRunner.query( + 'INSERT INTO "shared_vaults"("uuid", "user_uuid", "file_upload_bytes_used", "created_at_timestamp", "updated_at_timestamp") SELECT "uuid", "user_uuid", "file_upload_bytes_used", "created_at_timestamp", "updated_at_timestamp" FROM "temporary_shared_vaults"', + ) + await queryRunner.query('DROP TABLE "temporary_shared_vaults"') + } + + public async down(): Promise { + return + } +} diff --git a/packages/syncing-server/src/Bootstrap/Container.ts b/packages/syncing-server/src/Bootstrap/Container.ts index c1eeb90c2..0367902fd 100644 --- a/packages/syncing-server/src/Bootstrap/Container.ts +++ b/packages/syncing-server/src/Bootstrap/Container.ts @@ -78,8 +78,6 @@ import { SaveItems } from '../Domain/UseCase/Syncing/SaveItems/SaveItems' import { ItemHashHttpMapper } from '../Mapping/Http/ItemHashHttpMapper' import { ItemHash } from '../Domain/Item/ItemHash' import { ItemHashHttpRepresentation } from '../Mapping/Http/ItemHashHttpRepresentation' -import { TypeORMKeySystemAssociation } from '../Infra/TypeORM/TypeORMKeySystemAssociation' -import { TypeORMSharedVaultAssociation } from '../Infra/TypeORM/TypeORMSharedVaultAssociation' import { BaseSharedVaultInvitesController } from '../Infra/InversifyExpressUtils/Base/BaseSharedVaultInvitesController' import { InviteUserToSharedVault } from '../Domain/UseCase/SharedVaults/InviteUserToSharedVault/InviteUserToSharedVault' import { TypeORMSharedVaultRepository } from '../Infra/TypeORM/TypeORMSharedVaultRepository' @@ -361,12 +359,6 @@ export class ContainerConfigLoader { container .bind>(TYPES.Sync_ORMItemRepository) .toDynamicValue(() => appDataSource.getRepository(TypeORMItem)) - container - .bind>(TYPES.Sync_ORMSharedVaultAssociationRepository) - .toConstantValue(appDataSource.getRepository(TypeORMSharedVaultAssociation)) - container - .bind>(TYPES.Sync_ORMKeySystemAssociationRepository) - .toConstantValue(appDataSource.getRepository(TypeORMKeySystemAssociation)) container .bind>(TYPES.Sync_ORMSharedVaultRepository) .toConstantValue(appDataSource.getRepository(TypeORMSharedVault)) diff --git a/packages/syncing-server/src/Bootstrap/DataSource.ts b/packages/syncing-server/src/Bootstrap/DataSource.ts index a9fa0c9fe..b6c796ede 100644 --- a/packages/syncing-server/src/Bootstrap/DataSource.ts +++ b/packages/syncing-server/src/Bootstrap/DataSource.ts @@ -4,8 +4,6 @@ import { Env } from './Env' import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions' import { TypeORMItem } from '../Infra/TypeORM/TypeORMItem' import { TypeORMNotification } from '../Infra/TypeORM/TypeORMNotification' -import { TypeORMSharedVaultAssociation } from '../Infra/TypeORM/TypeORMSharedVaultAssociation' -import { TypeORMKeySystemAssociation } from '../Infra/TypeORM/TypeORMKeySystemAssociation' import { TypeORMSharedVault } from '../Infra/TypeORM/TypeORMSharedVault' import { TypeORMSharedVaultUser } from '../Infra/TypeORM/TypeORMSharedVaultUser' import { TypeORMSharedVaultInvite } from '../Infra/TypeORM/TypeORMSharedVaultInvite' @@ -79,8 +77,6 @@ export class AppDataSource { entities: [ TypeORMItem, TypeORMNotification, - TypeORMSharedVaultAssociation, - TypeORMKeySystemAssociation, TypeORMSharedVault, TypeORMSharedVaultUser, TypeORMSharedVaultInvite, diff --git a/packages/syncing-server/src/Bootstrap/Types.ts b/packages/syncing-server/src/Bootstrap/Types.ts index 217d78d0c..bd57efe8a 100644 --- a/packages/syncing-server/src/Bootstrap/Types.ts +++ b/packages/syncing-server/src/Bootstrap/Types.ts @@ -17,8 +17,6 @@ const TYPES = { Sync_MessageRepository: Symbol.for('Sync_MessageRepository'), // ORM Sync_ORMItemRepository: Symbol.for('Sync_ORMItemRepository'), - Sync_ORMSharedVaultAssociationRepository: Symbol.for('Sync_ORMSharedVaultAssociationRepository'), - Sync_ORMKeySystemAssociationRepository: Symbol.for('Sync_ORMKeySystemAssociationRepository'), Sync_ORMSharedVaultRepository: Symbol.for('Sync_ORMSharedVaultRepository'), Sync_ORMSharedVaultInviteRepository: Symbol.for('Sync_ORMSharedVaultInviteRepository'), Sync_ORMSharedVaultUserRepository: Symbol.for('Sync_ORMSharedVaultUserRepository'), diff --git a/packages/syncing-server/src/Domain/Item/Item.spec.ts b/packages/syncing-server/src/Domain/Item/Item.spec.ts index 216345732..cc4ec2674 100644 --- a/packages/syncing-server/src/Domain/Item/Item.spec.ts +++ b/packages/syncing-server/src/Domain/Item/Item.spec.ts @@ -110,9 +110,7 @@ describe('Item', () => { updatedWithSession: null, dates: Dates.create(new Date(123), new Date(123)).getValue(), timestamps: Timestamps.create(123, 123).getValue(), - keySystemAssociation: KeySystemAssociation.create({ - keySystemIdentifier: 'key-system-identifier', - }).getValue(), + keySystemAssociation: KeySystemAssociation.create('key-system-identifier').getValue(), }) expect(entityOrError.isFailed()).toBeFalsy() @@ -138,106 +136,6 @@ describe('Item', () => { expect(entityOrError.getValue().isAssociatedWithKeySystem('key-system-identifier')).toBeFalsy() }) - it('should set shared vault association', () => { - const sharedVaultAssociation = SharedVaultAssociation.create({ - sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), - lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), - }).getValue() - - const entity = Item.create({ - duplicateOf: null, - itemsKeyId: 'items-key-id', - content: 'content', - contentType: ContentType.create(ContentType.TYPES.Note).getValue(), - encItemKey: 'enc-item-key', - authHash: 'auth-hash', - userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), - deleted: false, - updatedWithSession: null, - dates: Dates.create(new Date(123), new Date(123)).getValue(), - timestamps: Timestamps.create(123, 123).getValue(), - }).getValue() - - entity.setSharedVaultAssociation(sharedVaultAssociation) - - expect(entity.props.sharedVaultAssociation).toEqual(sharedVaultAssociation) - expect(entity.getChanges()).toHaveLength(1) - }) - - it('should unset a shared vault association', () => { - const entity = Item.create({ - duplicateOf: null, - itemsKeyId: 'items-key-id', - content: 'content', - contentType: ContentType.create(ContentType.TYPES.Note).getValue(), - encItemKey: 'enc-item-key', - authHash: 'auth-hash', - userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), - deleted: false, - updatedWithSession: null, - dates: Dates.create(new Date(123), new Date(123)).getValue(), - timestamps: Timestamps.create(123, 123).getValue(), - sharedVaultAssociation: SharedVaultAssociation.create({ - sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), - lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), - }).getValue(), - }).getValue() - - entity.unsetSharedVaultAssociation() - - expect(entity.props.sharedVaultAssociation).toBeUndefined() - expect(entity.getChanges()).toHaveLength(1) - }) - - it('should set key system association', () => { - const keySystemAssociation = KeySystemAssociation.create({ - keySystemIdentifier: 'key-system-identifier', - }).getValue() - - const entity = Item.create({ - duplicateOf: null, - itemsKeyId: 'items-key-id', - content: 'content', - contentType: ContentType.create(ContentType.TYPES.Note).getValue(), - encItemKey: 'enc-item-key', - authHash: 'auth-hash', - userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), - deleted: false, - updatedWithSession: null, - dates: Dates.create(new Date(123), new Date(123)).getValue(), - timestamps: Timestamps.create(123, 123).getValue(), - }).getValue() - - entity.setKeySystemAssociation(keySystemAssociation) - - expect(entity.props.keySystemAssociation).toEqual(keySystemAssociation) - expect(entity.getChanges()).toHaveLength(1) - }) - - it('should unset a key system association', () => { - const entity = Item.create({ - duplicateOf: null, - itemsKeyId: 'items-key-id', - content: 'content', - contentType: ContentType.create(ContentType.TYPES.Note).getValue(), - encItemKey: 'enc-item-key', - authHash: 'auth-hash', - userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), - deleted: false, - updatedWithSession: null, - dates: Dates.create(new Date(123), new Date(123)).getValue(), - timestamps: Timestamps.create(123, 123).getValue(), - keySystemAssociation: KeySystemAssociation.create({ - keySystemIdentifier: 'key-system-identifier', - }).getValue(), - }).getValue() - - entity.unsetKeySystemAssociation() - - expect(entity.props.keySystemAssociation).toBeUndefined() - expect(entity.getChanges()).toHaveLength(1) - }) - it('should tell if an item is identical to another item', () => { const entity = Item.create( { diff --git a/packages/syncing-server/src/Domain/Item/Item.ts b/packages/syncing-server/src/Domain/Item/Item.ts index 8b6200ece..d839d5aaa 100644 --- a/packages/syncing-server/src/Domain/Item/Item.ts +++ b/packages/syncing-server/src/Domain/Item/Item.ts @@ -1,8 +1,6 @@ -import { Aggregate, Change, Result, UniqueEntityId, Uuid } from '@standardnotes/domain-core' +import { Aggregate, Result, UniqueEntityId, Uuid } from '@standardnotes/domain-core' import { ItemProps } from './ItemProps' -import { SharedVaultAssociation } from '../SharedVault/SharedVaultAssociation' -import { KeySystemAssociation } from '../KeySystem/KeySystemAssociation' export class Item extends Aggregate { private constructor(props: ItemProps, id?: UniqueEntityId) { @@ -55,60 +53,6 @@ export class Item extends Aggregate { return this.props.keySystemAssociation.props.keySystemIdentifier === keySystemIdentifier } - setSharedVaultAssociation(sharedVaultAssociation: SharedVaultAssociation): void { - this.addChange( - Change.create({ - aggregateRootUuid: this.uuid.value, - changeType: this.props.sharedVaultAssociation ? Change.TYPES.Modify : Change.TYPES.Add, - changeData: sharedVaultAssociation, - }).getValue(), - ) - - this.props.sharedVaultAssociation = sharedVaultAssociation - } - - unsetSharedVaultAssociation(): void { - if (!this.props.sharedVaultAssociation) { - return - } - - this.addChange( - Change.create({ - aggregateRootUuid: this.uuid.value, - changeType: Change.TYPES.Remove, - changeData: this.props.sharedVaultAssociation, - }).getValue(), - ) - this.props.sharedVaultAssociation = undefined - } - - setKeySystemAssociation(keySystemAssociation: KeySystemAssociation): void { - this.addChange( - Change.create({ - aggregateRootUuid: this.uuid.value, - changeType: this.props.keySystemAssociation ? Change.TYPES.Modify : Change.TYPES.Add, - changeData: keySystemAssociation, - }).getValue(), - ) - - this.props.keySystemAssociation = keySystemAssociation - } - - unsetKeySystemAssociation(): void { - if (!this.props.keySystemAssociation) { - return - } - - this.addChange( - Change.create({ - aggregateRootUuid: this.uuid.value, - changeType: Change.TYPES.Remove, - changeData: this.props.keySystemAssociation, - }).getValue(), - ) - this.props.keySystemAssociation = undefined - } - isIdenticalTo(item: Item): boolean { if (this._id.toString() !== item._id.toString()) { return false diff --git a/packages/syncing-server/src/Domain/KeySystem/KeySystemAssociation.spec.ts b/packages/syncing-server/src/Domain/KeySystem/KeySystemAssociation.spec.ts index 5cee78aa6..a50be92d1 100644 --- a/packages/syncing-server/src/Domain/KeySystem/KeySystemAssociation.spec.ts +++ b/packages/syncing-server/src/Domain/KeySystem/KeySystemAssociation.spec.ts @@ -1,12 +1,15 @@ import { KeySystemAssociation } from './KeySystemAssociation' describe('KeySystemAssociation', () => { - it('should create an entity', () => { - const entityOrError = KeySystemAssociation.create({ - keySystemIdentifier: '00000000-0000-0000-0000-000000000000', - }) + it('should create a value object', () => { + const entityOrError = KeySystemAssociation.create('00000000-0000-0000-0000-000000000000') expect(entityOrError.isFailed()).toBeFalsy() - expect(entityOrError.getValue().id).not.toBeNull() + }) + + it('should fail to create a value object with an empty key system identifier', () => { + const entityOrError = KeySystemAssociation.create('') + + expect(entityOrError.isFailed()).toBeTruthy() }) }) diff --git a/packages/syncing-server/src/Domain/KeySystem/KeySystemAssociation.ts b/packages/syncing-server/src/Domain/KeySystem/KeySystemAssociation.ts index b8f7f2015..b0eea3f88 100644 --- a/packages/syncing-server/src/Domain/KeySystem/KeySystemAssociation.ts +++ b/packages/syncing-server/src/Domain/KeySystem/KeySystemAssociation.ts @@ -1,13 +1,18 @@ -import { Entity, Result, UniqueEntityId } from '@standardnotes/domain-core' +import { Result, Validator, ValueObject } from '@standardnotes/domain-core' import { KeySystemAssociationProps } from './KeySystemAssocationProps' -export class KeySystemAssociation extends Entity { - private constructor(props: KeySystemAssociationProps, id?: UniqueEntityId) { - super(props, id) +export class KeySystemAssociation extends ValueObject { + private constructor(props: KeySystemAssociationProps) { + super(props) } - static create(props: KeySystemAssociationProps, id?: UniqueEntityId): Result { - return Result.ok(new KeySystemAssociation(props, id)) + static create(keySystemIdentifier: string): Result { + const validationResult = Validator.isNotEmptyString(keySystemIdentifier) + if (validationResult.isFailed()) { + return Result.fail(validationResult.getError()) + } + + return Result.ok(new KeySystemAssociation({ keySystemIdentifier })) } } diff --git a/packages/syncing-server/src/Domain/SharedVault/SharedVault.spec.ts b/packages/syncing-server/src/Domain/SharedVault/SharedVault.spec.ts index e808a1597..fb6a090ab 100644 --- a/packages/syncing-server/src/Domain/SharedVault/SharedVault.spec.ts +++ b/packages/syncing-server/src/Domain/SharedVault/SharedVault.spec.ts @@ -5,7 +5,6 @@ import { SharedVault } from './SharedVault' describe('SharedVault', () => { it('should create an entity', () => { const entityOrError = SharedVault.create({ - fileUploadBytesLimit: 1_000_000, fileUploadBytesUsed: 0, timestamps: Timestamps.create(123456789, 123456789).getValue(), userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), @@ -19,7 +18,6 @@ describe('SharedVault', () => { it('should throw an error if id cannot be cast to uuid', () => { const entityOrError = SharedVault.create( { - fileUploadBytesLimit: 1_000_000, fileUploadBytesUsed: 0, timestamps: Timestamps.create(123456789, 123456789).getValue(), userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), diff --git a/packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociation.spec.ts b/packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociation.spec.ts index 395f5d68f..592c1fcdb 100644 --- a/packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociation.spec.ts +++ b/packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociation.spec.ts @@ -3,13 +3,12 @@ import { Uuid } from '@standardnotes/domain-core' import { SharedVaultAssociation } from './SharedVaultAssociation' describe('SharedVaultAssociation', () => { - it('should create an entity', () => { + it('should create a value object', () => { const entityOrError = SharedVaultAssociation.create({ lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), }) expect(entityOrError.isFailed()).toBeFalsy() - expect(entityOrError.getValue().id).not.toBeNull() }) }) diff --git a/packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociation.ts b/packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociation.ts index b7ad1064b..87da0e1d9 100644 --- a/packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociation.ts +++ b/packages/syncing-server/src/Domain/SharedVault/SharedVaultAssociation.ts @@ -1,13 +1,13 @@ -import { Aggregate, Result, UniqueEntityId } from '@standardnotes/domain-core' +import { Result, ValueObject } from '@standardnotes/domain-core' import { SharedVaultAssociationProps } from './SharedVaultAssociationProps' -export class SharedVaultAssociation extends Aggregate { - private constructor(props: SharedVaultAssociationProps, id?: UniqueEntityId) { - super(props, id) +export class SharedVaultAssociation extends ValueObject { + private constructor(props: SharedVaultAssociationProps) { + super(props) } - static create(props: SharedVaultAssociationProps, id?: UniqueEntityId): Result { - return Result.ok(new SharedVaultAssociation(props, id)) + static create(props: SharedVaultAssociationProps): Result { + return Result.ok(new SharedVaultAssociation(props)) } } diff --git a/packages/syncing-server/src/Domain/SharedVault/SharedVaultProps.ts b/packages/syncing-server/src/Domain/SharedVault/SharedVaultProps.ts index 678f1146c..ad2bbdb60 100644 --- a/packages/syncing-server/src/Domain/SharedVault/SharedVaultProps.ts +++ b/packages/syncing-server/src/Domain/SharedVault/SharedVaultProps.ts @@ -3,6 +3,5 @@ import { Uuid, Timestamps } from '@standardnotes/domain-core' export interface SharedVaultProps { userUuid: Uuid fileUploadBytesUsed: number - fileUploadBytesLimit: number timestamps: Timestamps } diff --git a/packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVault/CreateSharedVault.ts b/packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVault/CreateSharedVault.ts index bb9675e8a..34cb01a30 100644 --- a/packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVault/CreateSharedVault.ts +++ b/packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVault/CreateSharedVault.ts @@ -7,16 +7,15 @@ import { Uuid, Validator, } from '@standardnotes/domain-core' +import { TimerInterface } from '@standardnotes/time' + import { CreateSharedVaultResult } from './CreateSharedVaultResult' import { CreateSharedVaultDTO } from './CreateSharedVaultDTO' -import { TimerInterface } from '@standardnotes/time' import { SharedVaultRepositoryInterface } from '../../../SharedVault/SharedVaultRepositoryInterface' import { AddUserToSharedVault } from '../AddUserToSharedVault/AddUserToSharedVault' import { SharedVault } from '../../../SharedVault/SharedVault' export class CreateSharedVault implements UseCaseInterface { - private readonly FILE_UPLOAD_BYTES_LIMIT = 1_000_000_000 - constructor( private addUserToSharedVault: AddUserToSharedVault, private sharedVaultRepository: SharedVaultRepositoryInterface, @@ -49,7 +48,6 @@ export class CreateSharedVault implements UseCaseInterface { beforeEach(() => { sharedVault = SharedVault.create({ - fileUploadBytesLimit: 100, fileUploadBytesUsed: 2, userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), timestamps: Timestamps.create(123, 123).getValue(), diff --git a/packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVaultFileValetToken/CreateSharedVaultFileValetToken.ts b/packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVaultFileValetToken/CreateSharedVaultFileValetToken.ts index 8abad3e0e..1f0a4d4a9 100644 --- a/packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVaultFileValetToken/CreateSharedVaultFileValetToken.ts +++ b/packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVaultFileValetToken/CreateSharedVaultFileValetToken.ts @@ -89,7 +89,7 @@ export class CreateSharedVaultFileValetToken implements UseCaseInterface permittedOperation: dto.operation, remoteIdentifier: dto.remoteIdentifier, uploadBytesUsed: sharedVault.props.fileUploadBytesUsed, - uploadBytesLimit: sharedVault.props.fileUploadBytesLimit, + uploadBytesLimit: dto.sharedVaultOwnerUploadBytesLimit, unencryptedFileSize: dto.unencryptedFileSize, moveOperation: this.createMoveOperationData(dto), } diff --git a/packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVaultFileValetToken/CreateSharedVaultFileValetTokenDTO.ts b/packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVaultFileValetToken/CreateSharedVaultFileValetTokenDTO.ts index 95a919635..0db1a598f 100644 --- a/packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVaultFileValetToken/CreateSharedVaultFileValetTokenDTO.ts +++ b/packages/syncing-server/src/Domain/UseCase/SharedVaults/CreateSharedVaultFileValetToken/CreateSharedVaultFileValetTokenDTO.ts @@ -3,6 +3,7 @@ import { SharedVaultMoveType, ValetTokenOperation } from '@standardnotes/securit export interface CreateSharedVaultFileValetTokenDTO { userUuid: string sharedVaultUuid: string + sharedVaultOwnerUploadBytesLimit?: number fileUuid?: string remoteIdentifier: string operation: ValetTokenOperation diff --git a/packages/syncing-server/src/Domain/UseCase/SharedVaults/DeleteSharedVault/DeleteSharedVault.spec.ts b/packages/syncing-server/src/Domain/UseCase/SharedVaults/DeleteSharedVault/DeleteSharedVault.spec.ts index 178b55d3d..831c449e2 100644 --- a/packages/syncing-server/src/Domain/UseCase/SharedVaults/DeleteSharedVault/DeleteSharedVault.spec.ts +++ b/packages/syncing-server/src/Domain/UseCase/SharedVaults/DeleteSharedVault/DeleteSharedVault.spec.ts @@ -26,7 +26,6 @@ describe('DeleteSharedVault', () => { beforeEach(() => { sharedVault = SharedVault.create({ - fileUploadBytesLimit: 100, fileUploadBytesUsed: 2, userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), timestamps: Timestamps.create(123, 123).getValue(), @@ -110,7 +109,6 @@ describe('DeleteSharedVault', () => { it('should return error when originator of the delete request is not the owner of the shared vault', async () => { sharedVault = SharedVault.create({ - fileUploadBytesLimit: 100, fileUploadBytesUsed: 2, userUuid: Uuid.create('00000000-0000-0000-0000-000000000001').getValue(), timestamps: Timestamps.create(123, 123).getValue(), diff --git a/packages/syncing-server/src/Domain/UseCase/SharedVaults/GetSharedVaultUsers/GetSharedVaultUsers.spec.ts b/packages/syncing-server/src/Domain/UseCase/SharedVaults/GetSharedVaultUsers/GetSharedVaultUsers.spec.ts index 81c364103..3828f02fe 100644 --- a/packages/syncing-server/src/Domain/UseCase/SharedVaults/GetSharedVaultUsers/GetSharedVaultUsers.spec.ts +++ b/packages/syncing-server/src/Domain/UseCase/SharedVaults/GetSharedVaultUsers/GetSharedVaultUsers.spec.ts @@ -15,7 +15,6 @@ describe('GetSharedVaultUsers', () => { beforeEach(() => { sharedVault = SharedVault.create({ - fileUploadBytesLimit: 100, fileUploadBytesUsed: 2, userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), timestamps: Timestamps.create(123, 123).getValue(), @@ -61,7 +60,6 @@ describe('GetSharedVaultUsers', () => { it('returns error when originator is not the owner of the shared vault', async () => { sharedVault = SharedVault.create({ - fileUploadBytesLimit: 100, fileUploadBytesUsed: 2, userUuid: Uuid.create('00000000-0000-0000-0000-000000000001').getValue(), timestamps: Timestamps.create(123, 123).getValue(), diff --git a/packages/syncing-server/src/Domain/UseCase/SharedVaults/GetSharedVaults/GetSharedVaults.spec.ts b/packages/syncing-server/src/Domain/UseCase/SharedVaults/GetSharedVaults/GetSharedVaults.spec.ts index ffbe761ae..a7f674e13 100644 --- a/packages/syncing-server/src/Domain/UseCase/SharedVaults/GetSharedVaults/GetSharedVaults.spec.ts +++ b/packages/syncing-server/src/Domain/UseCase/SharedVaults/GetSharedVaults/GetSharedVaults.spec.ts @@ -26,7 +26,6 @@ describe('GetSharedVaults', () => { sharedVault = SharedVault.create({ userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), timestamps: Timestamps.create(123, 123).getValue(), - fileUploadBytesLimit: 123, fileUploadBytesUsed: 123, }).getValue() sharedVaultRepository = {} as jest.Mocked diff --git a/packages/syncing-server/src/Domain/UseCase/SharedVaults/InviteUserToSharedVault/InviteUserToSharedVault.spec.ts b/packages/syncing-server/src/Domain/UseCase/SharedVaults/InviteUserToSharedVault/InviteUserToSharedVault.spec.ts index 493d59de1..e07707130 100644 --- a/packages/syncing-server/src/Domain/UseCase/SharedVaults/InviteUserToSharedVault/InviteUserToSharedVault.spec.ts +++ b/packages/syncing-server/src/Domain/UseCase/SharedVaults/InviteUserToSharedVault/InviteUserToSharedVault.spec.ts @@ -22,7 +22,6 @@ describe('InviteUserToSharedVault', () => { beforeEach(() => { sharedVault = SharedVault.create({ - fileUploadBytesLimit: 100, fileUploadBytesUsed: 2, userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), timestamps: Timestamps.create(123, 123).getValue(), @@ -179,7 +178,6 @@ describe('InviteUserToSharedVault', () => { const useCase = createUseCase() sharedVault = SharedVault.create({ - fileUploadBytesLimit: 100, fileUploadBytesUsed: 2, userUuid: Uuid.create('10000000-0000-0000-0000-000000000000').getValue(), timestamps: Timestamps.create(123, 123).getValue(), diff --git a/packages/syncing-server/src/Domain/UseCase/SharedVaults/RemoveUserFromSharedVault/RemoveUserFromSharedVault.spec.ts b/packages/syncing-server/src/Domain/UseCase/SharedVaults/RemoveUserFromSharedVault/RemoveUserFromSharedVault.spec.ts index e5cd205c3..fad9339bd 100644 --- a/packages/syncing-server/src/Domain/UseCase/SharedVaults/RemoveUserFromSharedVault/RemoveUserFromSharedVault.spec.ts +++ b/packages/syncing-server/src/Domain/UseCase/SharedVaults/RemoveUserFromSharedVault/RemoveUserFromSharedVault.spec.ts @@ -19,7 +19,6 @@ describe('RemoveUserFromSharedVault', () => { beforeEach(() => { sharedVault = SharedVault.create({ - fileUploadBytesLimit: 100, fileUploadBytesUsed: 2, userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), timestamps: Timestamps.create(123, 123).getValue(), @@ -84,7 +83,6 @@ describe('RemoveUserFromSharedVault', () => { it('should return error when user is not owner of shared vault', async () => { sharedVault = SharedVault.create({ - fileUploadBytesLimit: 100, fileUploadBytesUsed: 2, userUuid: Uuid.create('00000000-0000-0000-0000-000000000002').getValue(), timestamps: Timestamps.create(123, 123).getValue(), @@ -104,7 +102,6 @@ describe('RemoveUserFromSharedVault', () => { it('should remove shared vault user if user is owner and is being force removed', async () => { sharedVault = SharedVault.create({ - fileUploadBytesLimit: 100, fileUploadBytesUsed: 2, userUuid: Uuid.create('00000000-0000-0000-0000-000000000002').getValue(), timestamps: Timestamps.create(123, 123).getValue(), diff --git a/packages/syncing-server/src/Domain/UseCase/SharedVaults/UpdateStorageQuotaUsedInSharedVault/UpdateStorageQuotaUsedInSharedVault.spec.ts b/packages/syncing-server/src/Domain/UseCase/SharedVaults/UpdateStorageQuotaUsedInSharedVault/UpdateStorageQuotaUsedInSharedVault.spec.ts index fa86f8a11..6dccbfeea 100644 --- a/packages/syncing-server/src/Domain/UseCase/SharedVaults/UpdateStorageQuotaUsedInSharedVault/UpdateStorageQuotaUsedInSharedVault.spec.ts +++ b/packages/syncing-server/src/Domain/UseCase/SharedVaults/UpdateStorageQuotaUsedInSharedVault/UpdateStorageQuotaUsedInSharedVault.spec.ts @@ -11,7 +11,6 @@ describe('UpdateStorageQuotaUsedInSharedVault', () => { beforeEach(() => { sharedVault = SharedVault.create({ - fileUploadBytesLimit: 100, fileUploadBytesUsed: 2, userUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), timestamps: Timestamps.create(123, 123).getValue(), diff --git a/packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.ts b/packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.ts index 0b60530cd..08dadea03 100644 --- a/packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.ts +++ b/packages/syncing-server/src/Domain/UseCase/Syncing/SaveNewItem/SaveNewItem.ts @@ -123,7 +123,7 @@ export class SaveNewItem implements UseCaseInterface { if (sharedVaultAssociationOrError.isFailed()) { return Result.fail(sharedVaultAssociationOrError.getError()) } - newItem.setSharedVaultAssociation(sharedVaultAssociationOrError.getValue()) + newItem.props.sharedVaultAssociation = sharedVaultAssociationOrError.getValue() } if (dto.itemHash.hasDedicatedKeySystemAssociation()) { @@ -133,13 +133,11 @@ export class SaveNewItem implements UseCaseInterface { } const keySystemIdentifier = dto.itemHash.props.key_system_identifier as string - const keySystemAssociationOrError = KeySystemAssociation.create({ - keySystemIdentifier, - }) + const keySystemAssociationOrError = KeySystemAssociation.create(keySystemIdentifier) if (keySystemAssociationOrError.isFailed()) { return Result.fail(keySystemAssociationOrError.getError()) } - newItem.setKeySystemAssociation(keySystemAssociationOrError.getValue()) + newItem.props.keySystemAssociation = keySystemAssociationOrError.getValue() } const itemRepository = this.itemRepositoryResolver.resolve(roleNames) diff --git a/packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.spec.ts b/packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.spec.ts index 00c53e0ae..deacfcd61 100644 --- a/packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.spec.ts +++ b/packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.spec.ts @@ -382,13 +382,10 @@ describe('UpdateExistingItem', () => { shared_vault_uuid: '00000000-0000-0000-0000-000000000000', }).getValue() - item1.setSharedVaultAssociation( - SharedVaultAssociation.create({ - sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), - lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), - }).getValue(), - ) - const idBefore = item1.props.sharedVaultAssociation?.id.toString() + item1.props.sharedVaultAssociation = SharedVaultAssociation.create({ + sharedVaultUuid: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), + lastEditedBy: Uuid.create('00000000-0000-0000-0000-000000000000').getValue(), + }).getValue() const result = await useCase.execute({ existingItem: item1, @@ -401,7 +398,6 @@ describe('UpdateExistingItem', () => { expect(result.isFailed()).toBeFalsy() expect(item1.props.sharedVaultAssociation).not.toBeUndefined() - expect((item1.props.sharedVaultAssociation as SharedVaultAssociation).id.toString()).toEqual(idBefore) }) it('should return error if shared vault association could not be created', async () => { @@ -567,12 +563,7 @@ describe('UpdateExistingItem', () => { key_system_identifier: '00000000-0000-0000-0000-000000000000', }).getValue() - item1.setKeySystemAssociation( - KeySystemAssociation.create({ - keySystemIdentifier: '00000000-0000-0000-0000-000000000000', - }).getValue(), - ) - const idBefore = item1.props.keySystemAssociation?.id.toString() + item1.props.keySystemAssociation = KeySystemAssociation.create('00000000-0000-0000-0000-000000000000').getValue() const result = await useCase.execute({ existingItem: item1, @@ -585,7 +576,6 @@ describe('UpdateExistingItem', () => { expect(result.isFailed()).toBeFalsy() expect(item1.props.keySystemAssociation).not.toBeUndefined() - expect((item1.props.keySystemAssociation as KeySystemAssociation).id.toString()).toEqual(idBefore) }) it('should return error if key system identifier is invalid', async () => { diff --git a/packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.ts b/packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.ts index 587994b94..2535b892f 100644 --- a/packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.ts +++ b/packages/syncing-server/src/Domain/UseCase/Syncing/UpdateExistingItem/UpdateExistingItem.ts @@ -6,7 +6,6 @@ import { Result, RoleNameCollection, Timestamps, - UniqueEntityId, UseCaseInterface, Uuid, Validator, @@ -131,23 +130,16 @@ export class UpdateExistingItem implements UseCaseInterface { let sharedVaultOperation: SharedVaultOperationOnItem | null = null if (dto.itemHash.representsASharedVaultItem()) { - const sharedVaultAssociationOrError = SharedVaultAssociation.create( - { - lastEditedBy: userUuid, - sharedVaultUuid: dto.itemHash.sharedVaultUuid as Uuid, - }, - new UniqueEntityId( - dto.existingItem.props.sharedVaultAssociation - ? dto.existingItem.props.sharedVaultAssociation.id.toString() - : undefined, - ), - ) + const sharedVaultAssociationOrError = SharedVaultAssociation.create({ + lastEditedBy: userUuid, + sharedVaultUuid: dto.itemHash.sharedVaultUuid as Uuid, + }) if (sharedVaultAssociationOrError.isFailed()) { return Result.fail(sharedVaultAssociationOrError.getError()) } - dto.existingItem.setSharedVaultAssociation(sharedVaultAssociationOrError.getValue()) + dto.existingItem.props.sharedVaultAssociation = sharedVaultAssociationOrError.getValue() const sharedVaultOperationOrError = await this.determineSharedVaultOperationOnItem.execute({ existingItem: dto.existingItem, @@ -159,7 +151,7 @@ export class UpdateExistingItem implements UseCaseInterface { } sharedVaultOperation = sharedVaultOperationOrError.getValue() } else { - dto.existingItem.unsetSharedVaultAssociation() + dto.existingItem.props.sharedVaultAssociation = undefined } if (dto.itemHash.hasDedicatedKeySystemAssociation()) { @@ -169,23 +161,14 @@ export class UpdateExistingItem implements UseCaseInterface { } const keySystemIdentifier = dto.itemHash.props.key_system_identifier as string - const keySystemAssociationOrError = KeySystemAssociation.create( - { - keySystemIdentifier, - }, - new UniqueEntityId( - dto.existingItem.props.keySystemAssociation - ? dto.existingItem.props.keySystemAssociation.id.toString() - : undefined, - ), - ) + const keySystemAssociationOrError = KeySystemAssociation.create(keySystemIdentifier) if (keySystemAssociationOrError.isFailed()) { return Result.fail(keySystemAssociationOrError.getError()) } - dto.existingItem.setKeySystemAssociation(keySystemAssociationOrError.getValue()) + dto.existingItem.props.keySystemAssociation = keySystemAssociationOrError.getValue() } else { - dto.existingItem.unsetKeySystemAssociation() + dto.existingItem.props.keySystemAssociation = undefined } if (dto.itemHash.props.deleted === true) { diff --git a/packages/syncing-server/src/Infra/InversifyExpressUtils/Base/BaseSharedVaultsController.ts b/packages/syncing-server/src/Infra/InversifyExpressUtils/Base/BaseSharedVaultsController.ts index 9e643b4aa..9f11e15ea 100644 --- a/packages/syncing-server/src/Infra/InversifyExpressUtils/Base/BaseSharedVaultsController.ts +++ b/packages/syncing-server/src/Infra/InversifyExpressUtils/Base/BaseSharedVaultsController.ts @@ -104,6 +104,7 @@ export class BaseSharedVaultsController extends BaseHttpController { const result = await this.createSharedVaultFileValetTokenUseCase.execute({ userUuid: response.locals.user.uuid, sharedVaultUuid: request.params.sharedVaultUuid, + sharedVaultOwnerUploadBytesLimit: response.locals.sharedVaultOwnerContext?.upload_bytes_limit, fileUuid: request.body.file_uuid, remoteIdentifier: request.body.remote_identifier, operation: request.body.operation, diff --git a/packages/syncing-server/src/Infra/InversifyExpressUtils/Middleware/InversifyExpressAuthMiddleware.ts b/packages/syncing-server/src/Infra/InversifyExpressUtils/Middleware/InversifyExpressAuthMiddleware.ts index 32b8046d6..3b634de5f 100644 --- a/packages/syncing-server/src/Infra/InversifyExpressUtils/Middleware/InversifyExpressAuthMiddleware.ts +++ b/packages/syncing-server/src/Infra/InversifyExpressUtils/Middleware/InversifyExpressAuthMiddleware.ts @@ -25,6 +25,7 @@ export class InversifyExpressAuthMiddleware extends BaseMiddleware { response.locals.roles = decodedToken.roles response.locals.session = decodedToken.session response.locals.readOnlyAccess = decodedToken.session?.readonly_access ?? false + response.locals.sharedVaultOwnerContext = decodedToken.shared_vault_owner_context return next() } catch (error) { diff --git a/packages/syncing-server/src/Infra/TypeORM/TypeORMKeySystemAssociation.ts b/packages/syncing-server/src/Infra/TypeORM/TypeORMKeySystemAssociation.ts deleted file mode 100644 index 6d1bcddc8..000000000 --- a/packages/syncing-server/src/Infra/TypeORM/TypeORMKeySystemAssociation.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm' - -@Entity({ name: 'key_system_associations' }) -export class TypeORMKeySystemAssociation { - @PrimaryGeneratedColumn('uuid') - declare uuid: string - - @Column({ - name: 'key_system_identifier', - length: 36, - }) - @Index('key_system_identifier_on_key_system_associations') - declare keySystemIdentifier: string - - @Column({ - name: 'item_uuid', - length: 36, - }) - @Index('item_uuid_on_key_system_associations') - declare itemUuid: string - - @Column({ - name: 'created_at_timestamp', - type: 'bigint', - }) - declare createdAtTimestamp: number - - @Column({ - name: 'updated_at_timestamp', - type: 'bigint', - }) - declare updatedAtTimestamp: number -} diff --git a/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVault.ts b/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVault.ts index 672f198be..bda85d82b 100644 --- a/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVault.ts +++ b/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVault.ts @@ -18,12 +18,6 @@ export class TypeORMSharedVault { }) declare fileUploadBytesUsed: number - @Column({ - name: 'file_upload_bytes_limit', - type: 'int', - }) - declare fileUploadBytesLimit: number - @Column({ name: 'created_at_timestamp', type: 'bigint', diff --git a/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultAssociation.ts b/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultAssociation.ts deleted file mode 100644 index d9f3b34a6..000000000 --- a/packages/syncing-server/src/Infra/TypeORM/TypeORMSharedVaultAssociation.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { Column, Entity, Index, PrimaryGeneratedColumn } from 'typeorm' - -@Entity({ name: 'shared_vault_associations' }) -export class TypeORMSharedVaultAssociation { - @PrimaryGeneratedColumn('uuid') - declare uuid: string - - @Column({ - name: 'shared_vault_uuid', - length: 36, - }) - @Index('shared_vault_uuid_on_shared_vault_associations') - declare sharedVaultUuid: string - - @Column({ - name: 'item_uuid', - length: 36, - }) - @Index('item_uuid_on_shared_vault_associations') - declare itemUuid: string - - @Column({ - name: 'last_edited_by', - type: 'varchar', - length: 36, - }) - declare lastEditedBy: string - - @Column({ - name: 'created_at_timestamp', - type: 'bigint', - }) - declare createdAtTimestamp: number - - @Column({ - name: 'updated_at_timestamp', - type: 'bigint', - }) - declare updatedAtTimestamp: number -} diff --git a/packages/syncing-server/src/Mapping/Http/SharedVaultHttpMapper.ts b/packages/syncing-server/src/Mapping/Http/SharedVaultHttpMapper.ts index b9de2d09f..9feca034a 100644 --- a/packages/syncing-server/src/Mapping/Http/SharedVaultHttpMapper.ts +++ b/packages/syncing-server/src/Mapping/Http/SharedVaultHttpMapper.ts @@ -12,7 +12,6 @@ export class SharedVaultHttpMapper implements MapperInterface