diff --git a/packages/api-gateway/src/Service/Resolver/EndpointResolver.ts b/packages/api-gateway/src/Service/Resolver/EndpointResolver.ts index 61db387d3..38e43247c 100644 --- a/packages/api-gateway/src/Service/Resolver/EndpointResolver.ts +++ b/packages/api-gateway/src/Service/Resolver/EndpointResolver.ts @@ -42,7 +42,6 @@ export class EndpointResolver implements EndpointResolverInterface { // Users Controller ['[PATCH]:users/:userId', 'auth.users.update'], ['[PUT]:users/:userUuid/attributes/credentials', 'auth.users.updateCredentials'], - ['[GET]:users/params', 'auth.users.getKeyParams'], ['[DELETE]:users/:userUuid', 'auth.users.delete'], ['[POST]:listed', 'auth.users.createListedAccount'], ['[POST]:auth', 'auth.users.register'], diff --git a/packages/auth/bin/backup.ts b/packages/auth/bin/backup.ts index 65b4d8047..e4e884e2c 100644 --- a/packages/auth/bin/backup.ts +++ b/packages/auth/bin/backup.ts @@ -21,6 +21,7 @@ import { SettingRepositoryInterface } from '../src/Domain/Setting/SettingReposit import { MuteFailedBackupsEmailsOption, SettingName } from '@standardnotes/settings' import { RoleServiceInterface } from '../src/Domain/Role/RoleServiceInterface' import { PermissionName } from '@standardnotes/features' +import { GetUserKeyParams } from '../src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams' const inputArgs = process.argv.slice(2) const backupProvider = inputArgs[0] @@ -31,6 +32,7 @@ const requestBackups = async ( roleService: RoleServiceInterface, domainEventFactory: DomainEventFactoryInterface, domainEventPublisher: DomainEventPublisherInterface, + getUserKeyParamsUseCase: GetUserKeyParams, ): Promise => { const settingName = SettingName.create(SettingName.NAMES.EmailBackupFrequency).getValue() const permissionName = PermissionName.DailyEmailBackup @@ -64,11 +66,17 @@ const requestBackups = async ( userHasEmailsMuted = emailsMutedSetting.value === muteEmailsSettingValue } + const keyParamsResponse = await getUserKeyParamsUseCase.execute({ + userUuid: setting.setting_user_uuid, + authenticated: false, + }) + await domainEventPublisher.publish( domainEventFactory.createEmailBackupRequestedEvent( setting.setting_user_uuid, emailsMutedSetting?.uuid as string, userHasEmailsMuted, + keyParamsResponse.keyParams, ), ) @@ -96,11 +104,14 @@ void container.load().then((container) => { const roleService: RoleServiceInterface = container.get(TYPES.Auth_RoleService) const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.Auth_DomainEventFactory) const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.Auth_DomainEventPublisher) + const getUserKeyParamsUseCase: GetUserKeyParams = container.get(TYPES.Auth_GetUserKeyParams) const tracer = new OpenTelemetryTracer() tracer.startSpan(ServiceIdentifier.NAMES.AuthScheduledTask, 'backup') - Promise.resolve(requestBackups(settingRepository, roleService, domainEventFactory, domainEventPublisher)) + Promise.resolve( + requestBackups(settingRepository, roleService, domainEventFactory, domainEventPublisher, getUserKeyParamsUseCase), + ) .then(() => { logger.info(`${backupFrequency} ${backupProvider} backup requesting complete`) diff --git a/packages/auth/bin/user_email_backup.ts b/packages/auth/bin/user_email_backup.ts index f00390099..9ef5890a4 100644 --- a/packages/auth/bin/user_email_backup.ts +++ b/packages/auth/bin/user_email_backup.ts @@ -20,6 +20,7 @@ import { MuteFailedBackupsEmailsOption, SettingName } from '@standardnotes/setti import { RoleServiceInterface } from '../src/Domain/Role/RoleServiceInterface' import { PermissionName } from '@standardnotes/features' import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface' +import { GetUserKeyParams } from '../src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams' const inputArgs = process.argv.slice(2) const backupEmail = inputArgs[0] @@ -30,6 +31,7 @@ const requestBackups = async ( roleService: RoleServiceInterface, domainEventFactory: DomainEventFactoryInterface, domainEventPublisher: DomainEventPublisherInterface, + getUserKeyParamsUseCase: GetUserKeyParams, ): Promise => { const permissionName = PermissionName.DailyEmailBackup const muteEmailsSettingName = SettingName.NAMES.MuteFailedBackupsEmails @@ -57,11 +59,17 @@ const requestBackups = async ( userHasEmailsMuted = emailsMutedSetting.value === muteEmailsSettingValue } + const keyParamsResponse = await getUserKeyParamsUseCase.execute({ + userUuid: user.uuid, + authenticated: false, + }) + await domainEventPublisher.publish( domainEventFactory.createEmailBackupRequestedEvent( user.uuid, emailsMutedSetting?.uuid as string, userHasEmailsMuted, + keyParamsResponse.keyParams, ), ) @@ -84,12 +92,20 @@ void container.load().then((container) => { const roleService: RoleServiceInterface = container.get(TYPES.Auth_RoleService) const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.Auth_DomainEventFactory) const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.Auth_DomainEventPublisher) + const getUserKeyParamsUseCase: GetUserKeyParams = container.get(TYPES.Auth_GetUserKeyParams) const tracer = new OpenTelemetryTracer() tracer.startSpan(ServiceIdentifier.NAMES.AuthScheduledTask, 'user_email_backup') Promise.resolve( - requestBackups(userRepository, settingRepository, roleService, domainEventFactory, domainEventPublisher), + requestBackups( + userRepository, + settingRepository, + roleService, + domainEventFactory, + domainEventPublisher, + getUserKeyParamsUseCase, + ), ) .then(() => { logger.info(`Email backup requesting complete for ${backupEmail}`) diff --git a/packages/auth/src/Bootstrap/Container.ts b/packages/auth/src/Bootstrap/Container.ts index 0abcf68d3..e186498fd 100644 --- a/packages/auth/src/Bootstrap/Container.ts +++ b/packages/auth/src/Bootstrap/Container.ts @@ -273,6 +273,8 @@ import { UserRemovedFromSharedVaultEventHandler } from '../Domain/Handler/UserRe import { DesignateSurvivor } from '../Domain/UseCase/DesignateSurvivor/DesignateSurvivor' import { UserDesignatedAsSurvivorInSharedVaultEventHandler } from '../Domain/Handler/UserDesignatedAsSurvivorInSharedVaultEventHandler' import { DisableEmailSettingBasedOnEmailSubscription } from '../Domain/UseCase/DisableEmailSettingBasedOnEmailSubscription/DisableEmailSettingBasedOnEmailSubscription' +import { DomainEventFactoryInterface } from '../Domain/Event/DomainEventFactoryInterface' +import { KeyParamsFactoryInterface } from '../Domain/User/KeyParamsFactoryInterface' export class ContainerConfigLoader { constructor(private mode: 'server' | 'worker' = 'server') {} @@ -306,6 +308,8 @@ export class ContainerConfigLoader { } container.bind(TYPES.Auth_Logger).toConstantValue(logger) + container.bind(TYPES.Auth_CryptoNode).toConstantValue(new CryptoNode()) + const appDataSource = new AppDataSource({ env, runMigrations: this.mode === 'server' }) await appDataSource.initialize() @@ -367,6 +371,19 @@ export class ContainerConfigLoader { container.bind(TYPES.Auth_SQS).toConstantValue(sqsClient) } + container.bind(TYPES.Auth_SNS_TOPIC_ARN).toConstantValue(env.get('SNS_TOPIC_ARN', true)) + + container + .bind(TYPES.Auth_DomainEventPublisher) + .toConstantValue( + isConfiguredForHomeServer + ? directCallDomainEventPublisher + : new SNSOpenTelemetryDomainEventPublisher( + container.get(TYPES.Auth_SNS), + container.get(TYPES.Auth_SNS_TOPIC_ARN), + ), + ) + // Mapping container .bind>(TYPES.Auth_SessionTracePersistenceMapper) @@ -547,7 +564,6 @@ export class ContainerConfigLoader { container .bind(TYPES.Auth_DISABLE_USER_REGISTRATION) .toConstantValue(env.get('DISABLE_USER_REGISTRATION', true) === 'true') - container.bind(TYPES.Auth_SNS_TOPIC_ARN).toConstantValue(env.get('SNS_TOPIC_ARN', true)) container.bind(TYPES.Auth_SNS_AWS_REGION).toConstantValue(env.get('SNS_AWS_REGION', true)) container.bind(TYPES.Auth_SQS_QUEUE_URL).toConstantValue(env.get('SQS_QUEUE_URL', true)) container @@ -649,6 +665,9 @@ export class ContainerConfigLoader { } // Services + container + .bind>(TYPES.Auth_ProtocolVersionSelector) + .toConstantValue(new DeterministicSelector()) container.bind(TYPES.Auth_DeviceDetector).toConstantValue(new UAParser()) container.bind(TYPES.Auth_SessionService).to(SessionService) container.bind(TYPES.Auth_AuthResponseFactory20161215).to(AuthResponseFactory20161215) @@ -691,44 +710,61 @@ export class ContainerConfigLoader { container.bind(TYPES.Auth_DomainEventFactory).to(DomainEventFactory) container.bind(TYPES.Auth_HTTPClient).toConstantValue(axios.create()) container.bind(TYPES.Auth_Crypter).to(CrypterNode) - container.bind(TYPES.Auth_SettingService).to(SettingService) + container + .bind(TYPES.Auth_SettingsAssociationService) + .to(SettingsAssociationService) + container.bind(TYPES.Auth_SettingDecrypter).to(SettingDecrypter) + + container + .bind(TYPES.Auth_GetUserKeyParams) + .toConstantValue( + new GetUserKeyParams( + container.get(TYPES.Auth_KeyParamsFactory), + container.get(TYPES.Auth_UserRepository), + container.get(TYPES.Auth_PKCERepository), + container.get(TYPES.Auth_Logger), + ), + ) + container + .bind(TYPES.Auth_SettingInterpreter) + .toConstantValue( + new SettingInterpreter( + container.get(TYPES.Auth_DomainEventPublisher), + container.get(TYPES.Auth_DomainEventFactory), + container.get(TYPES.Auth_SettingRepository), + container.get(TYPES.Auth_GetUserKeyParams), + ), + ) + + container + .bind(TYPES.Auth_SettingService) + .toConstantValue( + new SettingService( + container.get(TYPES.Auth_SettingFactory), + container.get(TYPES.Auth_SettingRepository), + container.get(TYPES.Auth_SettingsAssociationService), + container.get(TYPES.Auth_SettingInterpreter), + container.get(TYPES.Auth_SettingDecrypter), + container.get(TYPES.Auth_Logger), + ), + ) container .bind(TYPES.Auth_SubscriptionSettingService) .to(SubscriptionSettingService) container.bind(TYPES.Auth_OfflineSettingService).to(OfflineSettingService) - container.bind(TYPES.Auth_CryptoNode).toConstantValue(new CryptoNode()) container.bind(TYPES.Auth_ContenDecoder).toConstantValue(new ContentDecoder()) container.bind(TYPES.Auth_WebSocketsClientService).to(WebSocketsClientService) container.bind(TYPES.Auth_RoleService).to(RoleService) container.bind(TYPES.Auth_RoleToSubscriptionMap).to(RoleToSubscriptionMap) - container - .bind(TYPES.Auth_SettingsAssociationService) - .to(SettingsAssociationService) container .bind(TYPES.Auth_SubscriptionSettingsAssociationService) .to(SubscriptionSettingsAssociationService) container.bind(TYPES.Auth_FeatureService).to(FeatureService) - container.bind(TYPES.Auth_SettingInterpreter).to(SettingInterpreter) - container.bind(TYPES.Auth_SettingDecrypter).to(SettingDecrypter) - container - .bind>(TYPES.Auth_ProtocolVersionSelector) - .toConstantValue(new DeterministicSelector()) container .bind>(TYPES.Auth_BooleanSelector) .toConstantValue(new DeterministicSelector()) container.bind(TYPES.Auth_UserSubscriptionService).to(UserSubscriptionService) - container - .bind(TYPES.Auth_DomainEventPublisher) - .toConstantValue( - isConfiguredForHomeServer - ? directCallDomainEventPublisher - : new SNSOpenTelemetryDomainEventPublisher( - container.get(TYPES.Auth_SNS), - container.get(TYPES.Auth_SNS_TOPIC_ARN), - ), - ) - // Middleware container.bind(TYPES.Auth_SessionMiddleware).to(SessionMiddleware) container.bind(TYPES.Auth_LockMiddleware).to(LockMiddleware) @@ -881,7 +917,6 @@ export class ContainerConfigLoader { container.get(TYPES.Auth_SettingService), ), ) - container.bind(TYPES.Auth_GetUserKeyParams).to(GetUserKeyParams) container.bind(TYPES.Auth_UpdateUser).to(UpdateUser) container.bind(TYPES.Auth_Register).to(Register) container.bind(TYPES.Auth_GetActiveSessionsForUser).to(GetActiveSessionsForUser) @@ -1311,7 +1346,6 @@ export class ContainerConfigLoader { .toConstantValue( new BaseUsersController( container.get(TYPES.Auth_UpdateUser), - container.get(TYPES.Auth_GetUserKeyParams), container.get(TYPES.Auth_DeleteAccount), container.get(TYPES.Auth_GetUserSubscription), container.get(TYPES.Auth_ClearLoginAttempts), diff --git a/packages/auth/src/Domain/Event/DomainEventFactory.ts b/packages/auth/src/Domain/Event/DomainEventFactory.ts index 116e3f085..2e7f0fefc 100644 --- a/packages/auth/src/Domain/Event/DomainEventFactory.ts +++ b/packages/auth/src/Domain/Event/DomainEventFactory.ts @@ -28,6 +28,7 @@ import { inject, injectable } from 'inversify' import TYPES from '../../Bootstrap/Types' import { InviteeIdentifierType } from '../SharedSubscription/InviteeIdentifierType' import { DomainEventFactoryInterface } from './DomainEventFactoryInterface' +import { KeyParamsData } from '@standardnotes/responses' @injectable() export class DomainEventFactory implements DomainEventFactoryInterface { @@ -277,6 +278,7 @@ export class DomainEventFactory implements DomainEventFactoryInterface { userUuid: string, muteEmailsSettingUuid: string, userHasEmailsMuted: boolean, + keyParams: KeyParamsData, ): EmailBackupRequestedEvent { return { type: 'EMAIL_BACKUP_REQUESTED', @@ -292,6 +294,7 @@ export class DomainEventFactory implements DomainEventFactoryInterface { userUuid, userHasEmailsMuted, muteEmailsSettingUuid, + keyParams, }, } } diff --git a/packages/auth/src/Domain/Event/DomainEventFactoryInterface.ts b/packages/auth/src/Domain/Event/DomainEventFactoryInterface.ts index 68e910d1a..086f476a5 100644 --- a/packages/auth/src/Domain/Event/DomainEventFactoryInterface.ts +++ b/packages/auth/src/Domain/Event/DomainEventFactoryInterface.ts @@ -21,6 +21,7 @@ import { TransitionRequestedEvent, } from '@standardnotes/domain-events' import { InviteeIdentifierType } from '../SharedSubscription/InviteeIdentifierType' +import { KeyParamsData } from '@standardnotes/responses' export interface DomainEventFactoryInterface { createWebSocketMessageRequestedEvent(dto: { userUuid: string; message: JSONString }): WebSocketMessageRequestedEvent @@ -41,6 +42,7 @@ export interface DomainEventFactoryInterface { userUuid: string, muteEmailsSettingUuid: string, userHasEmailsMuted: boolean, + keyParams: KeyParamsData, ): EmailBackupRequestedEvent createAccountDeletionRequestedEvent(dto: { userUuid: string diff --git a/packages/auth/src/Domain/Setting/SettingInterpreter.spec.ts b/packages/auth/src/Domain/Setting/SettingInterpreter.spec.ts index 76e169c51..c93ef8384 100644 --- a/packages/auth/src/Domain/Setting/SettingInterpreter.spec.ts +++ b/packages/auth/src/Domain/Setting/SettingInterpreter.spec.ts @@ -19,6 +19,8 @@ import { SettingDecrypterInterface } from './SettingDecrypterInterface' import { SettingInterpreter } from './SettingInterpreter' import { SettingRepositoryInterface } from './SettingRepositoryInterface' +import { GetUserKeyParams } from '../UseCase/GetUserKeyParams/GetUserKeyParams' +import { KeyParamsData } from '@standardnotes/responses' describe('SettingInterpreter', () => { let user: User @@ -27,8 +29,10 @@ describe('SettingInterpreter', () => { let settingRepository: SettingRepositoryInterface let settingDecrypter: SettingDecrypterInterface let logger: Logger + let getUserKeyParams: GetUserKeyParams - const createInterpreter = () => new SettingInterpreter(domainEventPublisher, domainEventFactory, settingRepository) + const createInterpreter = () => + new SettingInterpreter(domainEventPublisher, domainEventFactory, settingRepository, getUserKeyParams) beforeEach(() => { user = { @@ -61,6 +65,9 @@ describe('SettingInterpreter', () => { logger.debug = jest.fn() logger.warn = jest.fn() logger.error = jest.fn() + + getUserKeyParams = {} as jest.Mocked + getUserKeyParams.execute = jest.fn().mockReturnValue({ keyParams: {} as jest.Mocked }) }) it('should trigger session cleanup if user is disabling session user agent logging', async () => { @@ -85,7 +92,7 @@ describe('SettingInterpreter', () => { ) expect(domainEventPublisher.publish).toHaveBeenCalled() - expect(domainEventFactory.createEmailBackupRequestedEvent).toHaveBeenCalledWith('4-5-6', '', false) + expect(domainEventFactory.createEmailBackupRequestedEvent).toHaveBeenCalledWith('4-5-6', '', false, {}) }) it('should trigger backup if email backup setting is created - emails muted', async () => { @@ -102,7 +109,7 @@ describe('SettingInterpreter', () => { ) expect(domainEventPublisher.publish).toHaveBeenCalled() - expect(domainEventFactory.createEmailBackupRequestedEvent).toHaveBeenCalledWith('4-5-6', '6-7-8', true) + expect(domainEventFactory.createEmailBackupRequestedEvent).toHaveBeenCalledWith('4-5-6', '6-7-8', true, {}) }) it('should not trigger backup if email backup setting is disabled', async () => { diff --git a/packages/auth/src/Domain/Setting/SettingInterpreter.ts b/packages/auth/src/Domain/Setting/SettingInterpreter.ts index d71febb31..015a36531 100644 --- a/packages/auth/src/Domain/Setting/SettingInterpreter.ts +++ b/packages/auth/src/Domain/Setting/SettingInterpreter.ts @@ -6,15 +6,13 @@ import { MuteFailedBackupsEmailsOption, SettingName, } from '@standardnotes/settings' -import { inject, injectable } from 'inversify' -import TYPES from '../../Bootstrap/Types' import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface' import { User } from '../User/User' import { SettingInterpreterInterface } from './SettingInterpreterInterface' import { SettingRepositoryInterface } from './SettingRepositoryInterface' +import { GetUserKeyParams } from '../UseCase/GetUserKeyParams/GetUserKeyParams' -@injectable() export class SettingInterpreter implements SettingInterpreterInterface { private readonly emailSettingToSubscriptionRejectionLevelMap: Map = new Map([ [SettingName.NAMES.MuteFailedBackupsEmails, EmailLevel.LEVELS.FailedEmailBackup], @@ -24,9 +22,10 @@ export class SettingInterpreter implements SettingInterpreterInterface { ]) constructor( - @inject(TYPES.Auth_DomainEventPublisher) private domainEventPublisher: DomainEventPublisherInterface, - @inject(TYPES.Auth_DomainEventFactory) private domainEventFactory: DomainEventFactoryInterface, - @inject(TYPES.Auth_SettingRepository) private settingRepository: SettingRepositoryInterface, + private domainEventPublisher: DomainEventPublisherInterface, + private domainEventFactory: DomainEventFactoryInterface, + private settingRepository: SettingRepositoryInterface, + private getUserKeyParams: GetUserKeyParams, ) {} async interpretSettingUpdated( @@ -59,8 +58,18 @@ export class SettingInterpreter implements SettingInterpreterInterface { muteEmailsSettingUuid = muteFailedEmailsBackupSetting.uuid } + const keyParamsResponse = await this.getUserKeyParams.execute({ + authenticated: false, + userUuid, + }) + await this.domainEventPublisher.publish( - this.domainEventFactory.createEmailBackupRequestedEvent(userUuid, muteEmailsSettingUuid, userHasEmailsMuted), + this.domainEventFactory.createEmailBackupRequestedEvent( + userUuid, + muteEmailsSettingUuid, + userHasEmailsMuted, + keyParamsResponse.keyParams, + ), ) } diff --git a/packages/auth/src/Domain/Setting/SettingService.ts b/packages/auth/src/Domain/Setting/SettingService.ts index 264086936..cfd916561 100644 --- a/packages/auth/src/Domain/Setting/SettingService.ts +++ b/packages/auth/src/Domain/Setting/SettingService.ts @@ -1,7 +1,6 @@ import { SettingName } from '@standardnotes/settings' -import { inject, injectable } from 'inversify' import { Logger } from 'winston' -import TYPES from '../../Bootstrap/Types' + import { User } from '../User/User' import { CreateOrReplaceSettingDto } from './CreateOrReplaceSettingDto' import { CreateOrReplaceSettingResponse } from './CreateOrReplaceSettingResponse' @@ -14,16 +13,14 @@ import { SettingInterpreterInterface } from './SettingInterpreterInterface' import { SettingDecrypterInterface } from './SettingDecrypterInterface' import { SettingFactoryInterface } from './SettingFactoryInterface' -@injectable() export class SettingService implements SettingServiceInterface { constructor( - @inject(TYPES.Auth_SettingFactory) private factory: SettingFactoryInterface, - @inject(TYPES.Auth_SettingRepository) private settingRepository: SettingRepositoryInterface, - @inject(TYPES.Auth_SettingsAssociationService) + private factory: SettingFactoryInterface, + private settingRepository: SettingRepositoryInterface, private settingsAssociationService: SettingsAssociationServiceInterface, - @inject(TYPES.Auth_SettingInterpreter) private settingInterpreter: SettingInterpreterInterface, - @inject(TYPES.Auth_SettingDecrypter) private settingDecrypter: SettingDecrypterInterface, - @inject(TYPES.Auth_Logger) private logger: Logger, + private settingInterpreter: SettingInterpreterInterface, + private settingDecrypter: SettingDecrypterInterface, + private logger: Logger, ) {} async applyDefaultSettingsUponRegistration(user: User): Promise { diff --git a/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.ts b/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.ts index b478258ac..17818f6bf 100644 --- a/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.ts +++ b/packages/auth/src/Domain/UseCase/GetUserKeyParams/GetUserKeyParams.ts @@ -1,24 +1,22 @@ -import { inject, injectable } from 'inversify' -import TYPES from '../../../Bootstrap/Types' +import { KeyParamsData } from '@standardnotes/responses' +import { Logger } from 'winston' +import { Username, Uuid } from '@standardnotes/domain-core' + import { KeyParamsFactoryInterface } from '../../User/KeyParamsFactoryInterface' import { UserRepositoryInterface } from '../../User/UserRepositoryInterface' import { GetUserKeyParamsDTO } from './GetUserKeyParamsDTO' import { GetUserKeyParamsResponse } from './GetUserKeyParamsResponse' import { UseCaseInterface } from '../UseCaseInterface' -import { Logger } from 'winston' import { User } from '../../User/User' import { PKCERepositoryInterface } from '../../User/PKCERepositoryInterface' import { GetUserKeyParamsDTOV2Challenged } from './GetUserKeyParamsDTOV2Challenged' -import { KeyParamsData } from '@standardnotes/responses' -import { Username, Uuid } from '@standardnotes/domain-core' -@injectable() export class GetUserKeyParams implements UseCaseInterface { constructor( - @inject(TYPES.Auth_KeyParamsFactory) private keyParamsFactory: KeyParamsFactoryInterface, - @inject(TYPES.Auth_UserRepository) private userRepository: UserRepositoryInterface, - @inject(TYPES.Auth_PKCERepository) private pkceRepository: PKCERepositoryInterface, - @inject(TYPES.Auth_Logger) private logger: Logger, + private keyParamsFactory: KeyParamsFactoryInterface, + private userRepository: UserRepositoryInterface, + private pkceRepository: PKCERepositoryInterface, + private logger: Logger, ) {} async execute(dto: GetUserKeyParamsDTO): Promise { diff --git a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedUsersController.spec.ts b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedUsersController.spec.ts index 26544ca0d..1998395c7 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedUsersController.spec.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedUsersController.spec.ts @@ -8,7 +8,6 @@ import { Result, Username } from '@standardnotes/domain-core' import { DeleteAccount } from '../../Domain/UseCase/DeleteAccount/DeleteAccount' import { ChangeCredentials } from '../../Domain/UseCase/ChangeCredentials/ChangeCredentials' import { ClearLoginAttempts } from '../../Domain/UseCase/ClearLoginAttempts' -import { GetUserKeyParams } from '../../Domain/UseCase/GetUserKeyParams/GetUserKeyParams' import { GetUserSubscription } from '../../Domain/UseCase/GetUserSubscription/GetUserSubscription' import { IncreaseLoginAttempts } from '../../Domain/UseCase/IncreaseLoginAttempts' import { InviteToSharedSubscription } from '../../Domain/UseCase/InviteToSharedSubscription/InviteToSharedSubscription' @@ -18,7 +17,6 @@ import { User } from '../../Domain/User/User' describe('AnnotatedUsersController', () => { let updateUser: UpdateUser let deleteAccount: DeleteAccount - let getUserKeyParams: GetUserKeyParams let getUserSubscription: GetUserSubscription let clearLoginAttempts: ClearLoginAttempts let increaseLoginAttempts: IncreaseLoginAttempts @@ -32,7 +30,6 @@ describe('AnnotatedUsersController', () => { const createController = () => new AnnotatedUsersController( updateUser, - getUserKeyParams, deleteAccount, getUserSubscription, clearLoginAttempts, @@ -51,9 +48,6 @@ describe('AnnotatedUsersController', () => { user.uuid = '123' user.email = 'test@test.te' - getUserKeyParams = {} as jest.Mocked - getUserKeyParams.execute = jest.fn() - getUserSubscription = {} as jest.Mocked getUserSubscription.execute = jest.fn() @@ -213,60 +207,6 @@ describe('AnnotatedUsersController', () => { expect(result.statusCode).toEqual(401) }) - it('should get user key params', async () => { - request.query = { - email: 'test@test.te', - uuid: '1-2-3', - } - - getUserKeyParams.execute = jest.fn().mockReturnValue({ foo: 'bar' }) - - const httpResponse = await createController().keyParams(request) - const result = await httpResponse.executeAsync() - - expect(getUserKeyParams.execute).toHaveBeenCalledWith({ - email: 'test@test.te', - userUuid: '1-2-3', - authenticated: false, - }) - - expect(result.statusCode).toEqual(200) - }) - - it('should get authenticated user key params', async () => { - request.query = { - email: 'test@test.te', - uuid: '1-2-3', - authenticated: 'true', - } - - getUserKeyParams.execute = jest.fn().mockReturnValue({ foo: 'bar' }) - - const httpResponse = await createController().keyParams(request) - const result = await httpResponse.executeAsync() - - expect(getUserKeyParams.execute).toHaveBeenCalledWith({ - email: 'test@test.te', - userUuid: '1-2-3', - authenticated: true, - }) - - expect(result.statusCode).toEqual(200) - }) - - it('should not get user key params if email and user uuid is missing', async () => { - request.query = {} - - getUserKeyParams.execute = jest.fn().mockReturnValue({ foo: 'bar' }) - - const httpResponse = await createController().keyParams(request) - const result = await httpResponse.executeAsync() - - expect(getUserKeyParams.execute).not.toHaveBeenCalled() - - expect(result.statusCode).toEqual(400) - }) - it('should get user subscription', async () => { request.params.userUuid = '1-2-3' response.locals.user = { diff --git a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedUsersController.ts b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedUsersController.ts index 5eb931720..5bc74292d 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedUsersController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/AnnotatedUsersController.ts @@ -11,7 +11,6 @@ import { } from 'inversify-express-utils' import TYPES from '../../Bootstrap/Types' import { DeleteAccount } from '../../Domain/UseCase/DeleteAccount/DeleteAccount' -import { GetUserKeyParams } from '../../Domain/UseCase/GetUserKeyParams/GetUserKeyParams' import { UpdateUser } from '../../Domain/UseCase/UpdateUser' import { GetUserSubscription } from '../../Domain/UseCase/GetUserSubscription/GetUserSubscription' import { ClearLoginAttempts } from '../../Domain/UseCase/ClearLoginAttempts' @@ -23,7 +22,6 @@ import { BaseUsersController } from './Base/BaseUsersController' export class AnnotatedUsersController extends BaseUsersController { constructor( @inject(TYPES.Auth_UpdateUser) override updateUser: UpdateUser, - @inject(TYPES.Auth_GetUserKeyParams) override getUserKeyParams: GetUserKeyParams, @inject(TYPES.Auth_DeleteAccount) override doDeleteAccount: DeleteAccount, @inject(TYPES.Auth_GetUserSubscription) override doGetUserSubscription: GetUserSubscription, @inject(TYPES.Auth_ClearLoginAttempts) override clearLoginAttempts: ClearLoginAttempts, @@ -32,7 +30,6 @@ export class AnnotatedUsersController extends BaseUsersController { ) { super( updateUser, - getUserKeyParams, doDeleteAccount, doGetUserSubscription, clearLoginAttempts, @@ -46,11 +43,6 @@ export class AnnotatedUsersController extends BaseUsersController { return super.update(request, response) } - @httpGet('/params') - override async keyParams(request: Request): Promise { - return super.keyParams(request) - } - @httpDelete('/:userUuid', TYPES.Auth_RequiredCrossServiceTokenMiddleware) override async deleteAccount(request: Request, response: Response): Promise { return super.deleteAccount(request, response) diff --git a/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseUsersController.ts b/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseUsersController.ts index f35e1b5ba..1b67aff30 100644 --- a/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseUsersController.ts +++ b/packages/auth/src/Infra/InversifyExpressUtils/Base/BaseUsersController.ts @@ -5,7 +5,6 @@ import { BaseHttpController, results } from 'inversify-express-utils' import { ChangeCredentials } from '../../../Domain/UseCase/ChangeCredentials/ChangeCredentials' import { ClearLoginAttempts } from '../../../Domain/UseCase/ClearLoginAttempts' import { DeleteAccount } from '../../../Domain/UseCase/DeleteAccount/DeleteAccount' -import { GetUserKeyParams } from '../../../Domain/UseCase/GetUserKeyParams/GetUserKeyParams' import { GetUserSubscription } from '../../../Domain/UseCase/GetUserSubscription/GetUserSubscription' import { IncreaseLoginAttempts } from '../../../Domain/UseCase/IncreaseLoginAttempts' import { UpdateUser } from '../../../Domain/UseCase/UpdateUser' @@ -14,7 +13,6 @@ import { ErrorTag } from '@standardnotes/responses' export class BaseUsersController extends BaseHttpController { constructor( protected updateUser: UpdateUser, - protected getUserKeyParams: GetUserKeyParams, protected doDeleteAccount: DeleteAccount, protected doGetUserSubscription: GetUserSubscription, protected clearLoginAttempts: ClearLoginAttempts, @@ -26,7 +24,6 @@ export class BaseUsersController extends BaseHttpController { if (this.controllerContainer !== undefined) { this.controllerContainer.register('auth.users.update', this.update.bind(this)) - this.controllerContainer.register('auth.users.getKeyParams', this.keyParams.bind(this)) this.controllerContainer.register('auth.users.getSubscription', this.getSubscription.bind(this)) this.controllerContainer.register('auth.users.updateCredentials', this.changeCredentials.bind(this)) this.controllerContainer.register('auth.users.delete', this.deleteAccount.bind(this)) @@ -79,30 +76,6 @@ export class BaseUsersController extends BaseHttpController { ) } - async keyParams(request: Request): Promise { - const email = 'email' in request.query ? request.query.email : undefined - const userUuid = 'uuid' in request.query ? request.query.uuid : undefined - - if (!email && !userUuid) { - return this.json( - { - error: { - message: 'Missing mandatory request query parameters.', - }, - }, - 400, - ) - } - - const result = await this.getUserKeyParams.execute({ - email, - userUuid, - authenticated: request.query.authenticated === 'true', - }) - - return this.json(result.keyParams) - } - async deleteAccount(request: Request, response: Response): Promise { if (request.params.userUuid !== response.locals.user.uuid) { return this.json( diff --git a/packages/domain-events/src/Domain/Event/EmailBackupRequestedEventPayload.ts b/packages/domain-events/src/Domain/Event/EmailBackupRequestedEventPayload.ts index 589362e29..204c319e0 100644 --- a/packages/domain-events/src/Domain/Event/EmailBackupRequestedEventPayload.ts +++ b/packages/domain-events/src/Domain/Event/EmailBackupRequestedEventPayload.ts @@ -2,4 +2,5 @@ export interface EmailBackupRequestedEventPayload { userUuid: string userHasEmailsMuted: boolean muteEmailsSettingUuid: string + keyParams: Record } diff --git a/packages/syncing-server/src/Bootstrap/Container.ts b/packages/syncing-server/src/Bootstrap/Container.ts index 35602f4f2..82c00046f 100644 --- a/packages/syncing-server/src/Bootstrap/Container.ts +++ b/packages/syncing-server/src/Bootstrap/Container.ts @@ -47,7 +47,6 @@ import { DomainEventPublisherInterface, } from '@standardnotes/domain-events' import axios, { AxiosInstance } from 'axios' -import { AuthHttpServiceInterface } from '../Domain/Auth/AuthHttpServiceInterface' import { ExtensionsHttpService } from '../Domain/Extension/ExtensionsHttpService' import { ExtensionsHttpServiceInterface } from '../Domain/Extension/ExtensionsHttpServiceInterface' import { AccountDeletionRequestedEventHandler } from '../Domain/Handler/AccountDeletionRequestedEventHandler' @@ -56,7 +55,6 @@ import { EmailBackupRequestedEventHandler } from '../Domain/Handler/EmailBackupR import { ItemRevisionCreationRequestedEventHandler } from '../Domain/Handler/ItemRevisionCreationRequestedEventHandler' import { ItemBackupServiceInterface } from '../Domain/Item/ItemBackupServiceInterface' import { FSItemBackupService } from '../Infra/FS/FSItemBackupService' -import { AuthHttpService } from '../Infra/HTTP/AuthHttpService' import { S3ItemBackupService } from '../Infra/S3/S3ItemBackupService' import { ControllerContainer, @@ -1104,17 +1102,6 @@ export class ContainerConfigLoader { ], ]) if (!isConfiguredForHomeServer) { - container.bind(TYPES.Sync_AUTH_SERVER_URL).toConstantValue(env.get('AUTH_SERVER_URL')) - - container - .bind(TYPES.Sync_AuthHttpService) - .toDynamicValue((context: interfaces.Context) => { - return new AuthHttpService( - context.container.get(TYPES.Sync_HTTPClient), - context.container.get(TYPES.Sync_AUTH_SERVER_URL), - ) - }) - container .bind(TYPES.Sync_EmailBackupRequestedEventHandler) .toConstantValue( @@ -1123,7 +1110,6 @@ export class ContainerConfigLoader { isSecondaryDatabaseEnabled ? container.get(TYPES.Sync_MongoDBItemRepository) : null, - container.get(TYPES.Sync_AuthHttpService), container.get(TYPES.Sync_ItemBackupService), container.get(TYPES.Sync_DomainEventPublisher), container.get(TYPES.Sync_DomainEventFactory), diff --git a/packages/syncing-server/src/Bootstrap/Types.ts b/packages/syncing-server/src/Bootstrap/Types.ts index b433ad79d..056434bf0 100644 --- a/packages/syncing-server/src/Bootstrap/Types.ts +++ b/packages/syncing-server/src/Bootstrap/Types.ts @@ -36,7 +36,6 @@ const TYPES = { Sync_SQS_AWS_REGION: Symbol.for('Sync_SQS_AWS_REGION'), Sync_AUTH_JWT_SECRET: Symbol.for('Sync_AUTH_JWT_SECRET'), Sync_EXTENSIONS_SERVER_URL: Symbol.for('Sync_EXTENSIONS_SERVER_URL'), - Sync_AUTH_SERVER_URL: Symbol.for('Sync_AUTH_SERVER_URL'), Sync_S3_AWS_REGION: Symbol.for('Sync_S3_AWS_REGION'), Sync_S3_BACKUP_BUCKET_NAME: Symbol.for('Sync_S3_BACKUP_BUCKET_NAME'), Sync_EMAIL_ATTACHMENT_MAX_BYTE_SIZE: Symbol.for('Sync_EMAIL_ATTACHMENT_MAX_BYTE_SIZE'), @@ -115,7 +114,6 @@ const TYPES = { Sync_SyncResponseFactory20161215: Symbol.for('Sync_SyncResponseFactory20161215'), Sync_SyncResponseFactory20200115: Symbol.for('Sync_SyncResponseFactory20200115'), Sync_SyncResponseFactoryResolver: Symbol.for('Sync_SyncResponseFactoryResolver'), - Sync_AuthHttpService: Symbol.for('Sync_AuthHttpService'), Sync_ExtensionsHttpService: Symbol.for('Sync_ExtensionsHttpService'), Sync_ItemBackupService: Symbol.for('Sync_ItemBackupService'), Sync_ItemSaveValidator: Symbol.for('Sync_ItemSaveValidator'), diff --git a/packages/syncing-server/src/Domain/Auth/AuthHttpServiceInterface.ts b/packages/syncing-server/src/Domain/Auth/AuthHttpServiceInterface.ts deleted file mode 100644 index 2b1b0feb7..000000000 --- a/packages/syncing-server/src/Domain/Auth/AuthHttpServiceInterface.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { KeyParamsData } from '@standardnotes/responses' - -export interface AuthHttpServiceInterface { - getUserKeyParams(userUuid: string): Promise -} diff --git a/packages/syncing-server/src/Domain/Handler/EmailBackupRequestedEventHandler.ts b/packages/syncing-server/src/Domain/Handler/EmailBackupRequestedEventHandler.ts index d7c59836c..e586ca4b1 100644 --- a/packages/syncing-server/src/Domain/Handler/EmailBackupRequestedEventHandler.ts +++ b/packages/syncing-server/src/Domain/Handler/EmailBackupRequestedEventHandler.ts @@ -1,4 +1,3 @@ -import { KeyParamsData } from '@standardnotes/responses' import { DomainEventHandlerInterface, DomainEventPublisherInterface, @@ -6,7 +5,6 @@ import { } from '@standardnotes/domain-events' import { EmailLevel } from '@standardnotes/domain-core' import { Logger } from 'winston' -import { AuthHttpServiceInterface } from '../Auth/AuthHttpServiceInterface' import { DomainEventFactoryInterface } from '../Event/DomainEventFactoryInterface' import { ItemBackupServiceInterface } from '../Item/ItemBackupServiceInterface' import { ItemRepositoryInterface } from '../Item/ItemRepositoryInterface' @@ -18,7 +16,6 @@ export class EmailBackupRequestedEventHandler implements DomainEventHandlerInter constructor( private primaryItemRepository: ItemRepositoryInterface, private secondaryItemRepository: ItemRepositoryInterface | null, - private authHttpService: AuthHttpServiceInterface, private itemBackupService: ItemBackupServiceInterface, private domainEventPublisher: DomainEventPublisherInterface, private domainEventFactory: DomainEventFactoryInterface, @@ -40,19 +37,6 @@ export class EmailBackupRequestedEventHandler implements DomainEventHandlerInter event: EmailBackupRequestedEvent, itemRepository: ItemRepositoryInterface, ): Promise { - let authParams: KeyParamsData - try { - authParams = await this.authHttpService.getUserKeyParams(event.payload.userUuid) - } catch (error) { - this.logger.error( - `Could not get user key params from auth service for user ${event.payload.userUuid}: ${ - (error as Error).message - }`, - ) - - return - } - const itemQuery: ItemQuery = { userUuid: event.payload.userUuid, sortBy: 'updated_at_timestamp', @@ -75,7 +59,7 @@ export class EmailBackupRequestedEventHandler implements DomainEventHandlerInter const bundleBackupFileNames = await this.itemBackupService.backup( items, - authParams, + event.payload.keyParams, this.emailAttachmentMaxByteSize, ) @@ -88,11 +72,11 @@ export class EmailBackupRequestedEventHandler implements DomainEventHandlerInter for (const backupFileName of backupFileNames) { await this.domainEventPublisher.publish( this.domainEventFactory.createEmailRequestedEvent({ - body: getBody(authParams.identifier as string), + body: getBody(event.payload.keyParams.identifier as string), level: EmailLevel.LEVELS.System, messageIdentifier: 'DATA_BACKUP', subject: getSubject(bundleIndex++, backupFileNames.length, dateOnly), - userEmail: authParams.identifier as string, + userEmail: event.payload.keyParams.identifier as string, sender: 'backups@standardnotes.org', attachments: [ { diff --git a/packages/syncing-server/src/Infra/HTTP/AuthHttpService.ts b/packages/syncing-server/src/Infra/HTTP/AuthHttpService.ts deleted file mode 100644 index 06f8dd3cf..000000000 --- a/packages/syncing-server/src/Infra/HTTP/AuthHttpService.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { KeyParamsData } from '@standardnotes/responses' -import { AxiosInstance } from 'axios' - -import { AuthHttpServiceInterface } from '../../Domain/Auth/AuthHttpServiceInterface' - -export class AuthHttpService implements AuthHttpServiceInterface { - constructor( - private httpClient: AxiosInstance, - private authServerUrl: string, - ) {} - - async getUserKeyParams(userUuid: string): Promise { - const keyParamsResponse = await this.httpClient.request({ - method: 'GET', - timeout: 10000, - headers: { - Accept: 'application/json', - }, - url: `${this.authServerUrl}/users/params?uuid=${userUuid}`, - validateStatus: - /* istanbul ignore next */ - (status: number) => status >= 200 && status < 500, - }) - - return keyParamsResponse.data - } -}