fix: passing key params for backup requests (#867)

This commit is contained in:
Karol Sójko 2023-10-12 15:37:20 +02:00 committed by GitHub
parent 1632c83217
commit 07398169c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 136 additions and 218 deletions

View file

@ -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'],

View file

@ -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<void> => {
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`)

View file

@ -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<void> => {
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}`)

View file

@ -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<winston.Logger>(TYPES.Auth_Logger).toConstantValue(logger)
container.bind<CryptoNode>(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<SQSClient>(TYPES.Auth_SQS).toConstantValue(sqsClient)
}
container.bind(TYPES.Auth_SNS_TOPIC_ARN).toConstantValue(env.get('SNS_TOPIC_ARN', true))
container
.bind<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher)
.toConstantValue(
isConfiguredForHomeServer
? directCallDomainEventPublisher
: new SNSOpenTelemetryDomainEventPublisher(
container.get(TYPES.Auth_SNS),
container.get(TYPES.Auth_SNS_TOPIC_ARN),
),
)
// Mapping
container
.bind<MapperInterface<SessionTrace, TypeORMSessionTrace>>(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<SelectorInterface<ProtocolVersion>>(TYPES.Auth_ProtocolVersionSelector)
.toConstantValue(new DeterministicSelector<ProtocolVersion>())
container.bind<UAParser>(TYPES.Auth_DeviceDetector).toConstantValue(new UAParser())
container.bind<SessionService>(TYPES.Auth_SessionService).to(SessionService)
container.bind<AuthResponseFactory20161215>(TYPES.Auth_AuthResponseFactory20161215).to(AuthResponseFactory20161215)
@ -691,44 +710,61 @@ export class ContainerConfigLoader {
container.bind<DomainEventFactory>(TYPES.Auth_DomainEventFactory).to(DomainEventFactory)
container.bind<AxiosInstance>(TYPES.Auth_HTTPClient).toConstantValue(axios.create())
container.bind<CrypterInterface>(TYPES.Auth_Crypter).to(CrypterNode)
container.bind<SettingServiceInterface>(TYPES.Auth_SettingService).to(SettingService)
container
.bind<SettingsAssociationServiceInterface>(TYPES.Auth_SettingsAssociationService)
.to(SettingsAssociationService)
container.bind<SettingDecrypterInterface>(TYPES.Auth_SettingDecrypter).to(SettingDecrypter)
container
.bind<GetUserKeyParams>(TYPES.Auth_GetUserKeyParams)
.toConstantValue(
new GetUserKeyParams(
container.get<KeyParamsFactoryInterface>(TYPES.Auth_KeyParamsFactory),
container.get<UserRepositoryInterface>(TYPES.Auth_UserRepository),
container.get<PKCERepositoryInterface>(TYPES.Auth_PKCERepository),
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
container
.bind<SettingInterpreterInterface>(TYPES.Auth_SettingInterpreter)
.toConstantValue(
new SettingInterpreter(
container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher),
container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
container.get<SettingRepositoryInterface>(TYPES.Auth_SettingRepository),
container.get<GetUserKeyParams>(TYPES.Auth_GetUserKeyParams),
),
)
container
.bind<SettingServiceInterface>(TYPES.Auth_SettingService)
.toConstantValue(
new SettingService(
container.get<SettingFactoryInterface>(TYPES.Auth_SettingFactory),
container.get<SettingRepositoryInterface>(TYPES.Auth_SettingRepository),
container.get<SettingsAssociationServiceInterface>(TYPES.Auth_SettingsAssociationService),
container.get<SettingInterpreterInterface>(TYPES.Auth_SettingInterpreter),
container.get<SettingDecrypterInterface>(TYPES.Auth_SettingDecrypter),
container.get<winston.Logger>(TYPES.Auth_Logger),
),
)
container
.bind<SubscriptionSettingServiceInterface>(TYPES.Auth_SubscriptionSettingService)
.to(SubscriptionSettingService)
container.bind<OfflineSettingServiceInterface>(TYPES.Auth_OfflineSettingService).to(OfflineSettingService)
container.bind<CryptoNode>(TYPES.Auth_CryptoNode).toConstantValue(new CryptoNode())
container.bind<ContentDecoderInterface>(TYPES.Auth_ContenDecoder).toConstantValue(new ContentDecoder())
container.bind<ClientServiceInterface>(TYPES.Auth_WebSocketsClientService).to(WebSocketsClientService)
container.bind<RoleServiceInterface>(TYPES.Auth_RoleService).to(RoleService)
container.bind<RoleToSubscriptionMapInterface>(TYPES.Auth_RoleToSubscriptionMap).to(RoleToSubscriptionMap)
container
.bind<SettingsAssociationServiceInterface>(TYPES.Auth_SettingsAssociationService)
.to(SettingsAssociationService)
container
.bind<SubscriptionSettingsAssociationServiceInterface>(TYPES.Auth_SubscriptionSettingsAssociationService)
.to(SubscriptionSettingsAssociationService)
container.bind<FeatureServiceInterface>(TYPES.Auth_FeatureService).to(FeatureService)
container.bind<SettingInterpreterInterface>(TYPES.Auth_SettingInterpreter).to(SettingInterpreter)
container.bind<SettingDecrypterInterface>(TYPES.Auth_SettingDecrypter).to(SettingDecrypter)
container
.bind<SelectorInterface<ProtocolVersion>>(TYPES.Auth_ProtocolVersionSelector)
.toConstantValue(new DeterministicSelector<ProtocolVersion>())
container
.bind<SelectorInterface<boolean>>(TYPES.Auth_BooleanSelector)
.toConstantValue(new DeterministicSelector<boolean>())
container.bind<UserSubscriptionServiceInterface>(TYPES.Auth_UserSubscriptionService).to(UserSubscriptionService)
container
.bind<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher)
.toConstantValue(
isConfiguredForHomeServer
? directCallDomainEventPublisher
: new SNSOpenTelemetryDomainEventPublisher(
container.get(TYPES.Auth_SNS),
container.get(TYPES.Auth_SNS_TOPIC_ARN),
),
)
// Middleware
container.bind<SessionMiddleware>(TYPES.Auth_SessionMiddleware).to(SessionMiddleware)
container.bind<LockMiddleware>(TYPES.Auth_LockMiddleware).to(LockMiddleware)
@ -881,7 +917,6 @@ export class ContainerConfigLoader {
container.get(TYPES.Auth_SettingService),
),
)
container.bind<GetUserKeyParams>(TYPES.Auth_GetUserKeyParams).to(GetUserKeyParams)
container.bind<UpdateUser>(TYPES.Auth_UpdateUser).to(UpdateUser)
container.bind<Register>(TYPES.Auth_Register).to(Register)
container.bind<GetActiveSessionsForUser>(TYPES.Auth_GetActiveSessionsForUser).to(GetActiveSessionsForUser)
@ -1311,7 +1346,6 @@ export class ContainerConfigLoader {
.toConstantValue(
new BaseUsersController(
container.get<UpdateUser>(TYPES.Auth_UpdateUser),
container.get<GetUserKeyParams>(TYPES.Auth_GetUserKeyParams),
container.get<DeleteAccount>(TYPES.Auth_DeleteAccount),
container.get<GetUserSubscription>(TYPES.Auth_GetUserSubscription),
container.get<ClearLoginAttempts>(TYPES.Auth_ClearLoginAttempts),

View file

@ -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,
},
}
}

View file

@ -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

View file

@ -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>
getUserKeyParams.execute = jest.fn().mockReturnValue({ keyParams: {} as jest.Mocked<KeyParamsData> })
})
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 () => {

View file

@ -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<string, string> = 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,
),
)
}

View file

@ -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<void> {

View file

@ -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<GetUserKeyParamsResponse> {

View file

@ -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>
getUserKeyParams.execute = jest.fn()
getUserSubscription = {} as jest.Mocked<GetUserSubscription>
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 = <results.JsonResult>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 = <results.JsonResult>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 = <results.JsonResult>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 = {

View file

@ -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<results.JsonResult> {
return super.keyParams(request)
}
@httpDelete('/:userUuid', TYPES.Auth_RequiredCrossServiceTokenMiddleware)
override async deleteAccount(request: Request, response: Response): Promise<results.JsonResult> {
return super.deleteAccount(request, response)

View file

@ -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<results.JsonResult> {
const email = 'email' in request.query ? <string>request.query.email : undefined
const userUuid = 'uuid' in request.query ? <string>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<results.JsonResult> {
if (request.params.userUuid !== response.locals.user.uuid) {
return this.json(

View file

@ -2,4 +2,5 @@ export interface EmailBackupRequestedEventPayload {
userUuid: string
userHasEmailsMuted: boolean
muteEmailsSettingUuid: string
keyParams: Record<string, unknown>
}

View file

@ -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<AuthHttpServiceInterface>(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<EmailBackupRequestedEventHandler>(TYPES.Sync_EmailBackupRequestedEventHandler)
.toConstantValue(
@ -1123,7 +1110,6 @@ export class ContainerConfigLoader {
isSecondaryDatabaseEnabled
? container.get<ItemRepositoryInterface>(TYPES.Sync_MongoDBItemRepository)
: null,
container.get<AuthHttpServiceInterface>(TYPES.Sync_AuthHttpService),
container.get<ItemBackupServiceInterface>(TYPES.Sync_ItemBackupService),
container.get<DomainEventPublisherInterface>(TYPES.Sync_DomainEventPublisher),
container.get<DomainEventFactoryInterface>(TYPES.Sync_DomainEventFactory),

View file

@ -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'),

View file

@ -1,5 +0,0 @@
import { KeyParamsData } from '@standardnotes/responses'
export interface AuthHttpServiceInterface {
getUserKeyParams(userUuid: string): Promise<KeyParamsData>
}

View file

@ -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<void> {
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: [
{

View file

@ -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<KeyParamsData> {
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
}
}