浏览代码

fix(auth): generate new recovery codes when enabling mfa (#964)

Karol Sójko 1 年之前
父节点
当前提交
031fa71e7d

+ 1 - 0
packages/auth/src/Bootstrap/Container.ts

@@ -1269,6 +1269,7 @@ export class ContainerConfigLoader {
           container.get<DomainEventPublisherInterface>(TYPES.Auth_DomainEventPublisher),
           container.get<DomainEventFactoryInterface>(TYPES.Auth_DomainEventFactory),
           container.get<TriggerEmailBackupForUser>(TYPES.Auth_TriggerEmailBackupForUser),
+          container.get<GenerateRecoveryCodes>(TYPES.Auth_GenerateRecoveryCodes),
         ),
       )
     container

+ 22 - 1
packages/auth/src/Domain/UseCase/TriggerPostSettingUpdateActions/TriggerPostSettingUpdateActions.spec.ts

@@ -7,6 +7,7 @@ import {
 import { EmailBackupFrequency, LogSessionUserAgentOption, MuteMarketingEmailsOption } from '@standardnotes/settings'
 import { SettingName, Result } from '@standardnotes/domain-core'
 
+import { GenerateRecoveryCodes } from '../GenerateRecoveryCodes/GenerateRecoveryCodes'
 import { TriggerPostSettingUpdateActions } from './TriggerPostSettingUpdateActions'
 import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
 import { TriggerEmailBackupForUser } from '../TriggerEmailBackupForUser/TriggerEmailBackupForUser'
@@ -15,11 +16,20 @@ describe('TriggerPostSettingUpdateActions', () => {
   let domainEventPublisher: DomainEventPublisherInterface
   let domainEventFactory: DomainEventFactoryInterface
   let triggerEmailBackupForUser: TriggerEmailBackupForUser
+  let generateRecoveryCodes: GenerateRecoveryCodes
 
   const createUseCase = () =>
-    new TriggerPostSettingUpdateActions(domainEventPublisher, domainEventFactory, triggerEmailBackupForUser)
+    new TriggerPostSettingUpdateActions(
+      domainEventPublisher,
+      domainEventFactory,
+      triggerEmailBackupForUser,
+      generateRecoveryCodes,
+    )
 
   beforeEach(() => {
+    generateRecoveryCodes = {} as jest.Mocked<GenerateRecoveryCodes>
+    generateRecoveryCodes.execute = jest.fn().mockReturnValue(Result.ok())
+
     triggerEmailBackupForUser = {} as jest.Mocked<TriggerEmailBackupForUser>
     triggerEmailBackupForUser.execute = jest.fn().mockReturnValue(Result.ok())
 
@@ -101,4 +111,15 @@ describe('TriggerPostSettingUpdateActions', () => {
       username: 'test@test.te',
     })
   })
+
+  it('should generate new recovery codes upon enabling mfa setting', async () => {
+    await createUseCase().execute({
+      updatedSettingName: SettingName.NAMES.MfaSecret,
+      userUuid: '4-5-6',
+      userEmail: 'test@test.te',
+      unencryptedValue: '123',
+    })
+
+    expect(generateRecoveryCodes.execute).toHaveBeenCalled()
+  })
 })

+ 12 - 0
packages/auth/src/Domain/UseCase/TriggerPostSettingUpdateActions/TriggerPostSettingUpdateActions.ts

@@ -5,6 +5,7 @@ import { EmailBackupFrequency, LogSessionUserAgentOption } from '@standardnotes/
 import { TriggerPostSettingUpdateActionsDTO } from './TriggerPostSettingUpdateActionsDTO'
 import { DomainEventFactoryInterface } from '../../Event/DomainEventFactoryInterface'
 import { TriggerEmailBackupForUser } from '../TriggerEmailBackupForUser/TriggerEmailBackupForUser'
+import { GenerateRecoveryCodes } from '../GenerateRecoveryCodes/GenerateRecoveryCodes'
 
 export class TriggerPostSettingUpdateActions implements UseCaseInterface<void> {
   private readonly emailSettingToSubscriptionRejectionLevelMap: Map<string, string> = new Map([
@@ -18,6 +19,7 @@ export class TriggerPostSettingUpdateActions implements UseCaseInterface<void> {
     private domainEventPublisher: DomainEventPublisherInterface,
     private domainEventFactory: DomainEventFactoryInterface,
     private triggerEmailBackupForUser: TriggerEmailBackupForUser,
+    private generateRecoveryCodes: GenerateRecoveryCodes,
   ) {}
 
   async execute(dto: TriggerPostSettingUpdateActionsDTO): Promise<Result<void>> {
@@ -35,6 +37,12 @@ export class TriggerPostSettingUpdateActions implements UseCaseInterface<void> {
       await this.triggerSessionUserAgentCleanup(dto.userEmail, dto.userUuid)
     }
 
+    if (this.isEnablingMFASetting(dto.updatedSettingName, dto.unencryptedValue)) {
+      await this.generateRecoveryCodes.execute({
+        userUuid: dto.userUuid,
+      })
+    }
+
     return Result.ok()
   }
 
@@ -54,6 +62,10 @@ export class TriggerPostSettingUpdateActions implements UseCaseInterface<void> {
     )
   }
 
+  private isEnablingMFASetting(settingName: string, newValue: string | null): boolean {
+    return settingName === SettingName.NAMES.MfaSecret && newValue !== null
+  }
+
   private isDisablingSessionUserAgentLogging(settingName: string, newValue: string | null): boolean {
     return SettingName.NAMES.LogSessionUserAgent === settingName && LogSessionUserAgentOption.Disabled === newValue
   }