feat(snjs): add getting recovery codes (#2132)
This commit is contained in:
Binary file not shown.
@@ -97,6 +97,7 @@ import { SessionStorageMapper } from '@Lib/Services/Mapping/SessionStorageMapper
|
|||||||
import { LegacySessionStorageMapper } from '@Lib/Services/Mapping/LegacySessionStorageMapper'
|
import { LegacySessionStorageMapper } from '@Lib/Services/Mapping/LegacySessionStorageMapper'
|
||||||
import { SignInWithRecoveryCodes } from '@Lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes'
|
import { SignInWithRecoveryCodes } from '@Lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes'
|
||||||
import { UseCaseContainerInterface } from '@Lib/Domain/UseCase/UseCaseContainerInterface'
|
import { UseCaseContainerInterface } from '@Lib/Domain/UseCase/UseCaseContainerInterface'
|
||||||
|
import { GetRecoveryCodes } from '@Lib/Domain/UseCase/GetRecoveryCodes/GetRecoveryCodes'
|
||||||
|
|
||||||
/** How often to automatically sync, in milliseconds */
|
/** How often to automatically sync, in milliseconds */
|
||||||
const DEFAULT_AUTO_SYNC_INTERVAL = 30_000
|
const DEFAULT_AUTO_SYNC_INTERVAL = 30_000
|
||||||
@@ -177,6 +178,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
|||||||
private declare authManager: AuthClientInterface
|
private declare authManager: AuthClientInterface
|
||||||
|
|
||||||
private declare _signInWithRecoveryCodes: SignInWithRecoveryCodes
|
private declare _signInWithRecoveryCodes: SignInWithRecoveryCodes
|
||||||
|
private declare _getRecoveryCodes: GetRecoveryCodes
|
||||||
|
|
||||||
private internalEventBus!: ExternalServices.InternalEventBusInterface
|
private internalEventBus!: ExternalServices.InternalEventBusInterface
|
||||||
|
|
||||||
@@ -263,6 +265,10 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
|||||||
return this._signInWithRecoveryCodes
|
return this._signInWithRecoveryCodes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get getRecoveryCodes(): UseCaseInterface<string> {
|
||||||
|
return this._getRecoveryCodes
|
||||||
|
}
|
||||||
|
|
||||||
public get files(): FilesClientInterface {
|
public get files(): FilesClientInterface {
|
||||||
return this.fileService
|
return this.fileService
|
||||||
}
|
}
|
||||||
@@ -1218,6 +1224,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
|||||||
;(this.authenticatorManager as unknown) = undefined
|
;(this.authenticatorManager as unknown) = undefined
|
||||||
;(this.authManager as unknown) = undefined
|
;(this.authManager as unknown) = undefined
|
||||||
;(this._signInWithRecoveryCodes as unknown) = undefined
|
;(this._signInWithRecoveryCodes as unknown) = undefined
|
||||||
|
;(this._getRecoveryCodes as unknown) = undefined
|
||||||
|
|
||||||
this.services = []
|
this.services = []
|
||||||
}
|
}
|
||||||
@@ -1776,5 +1783,7 @@ export class SNApplication implements ApplicationInterface, AppGroupManagedAppli
|
|||||||
this.sessionManager,
|
this.sessionManager,
|
||||||
this.internalEventBus,
|
this.internalEventBus,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
this._getRecoveryCodes = new GetRecoveryCodes(this.authManager, this.settingsService)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import { AuthClientInterface } from '@standardnotes/services'
|
||||||
|
import { SettingsClientInterface } from '@Lib/Services/Settings/SettingsClientInterface'
|
||||||
|
|
||||||
|
import { GetRecoveryCodes } from './GetRecoveryCodes'
|
||||||
|
|
||||||
|
describe('GetRecoveryCodes', () => {
|
||||||
|
let authClient: AuthClientInterface
|
||||||
|
let settingsClient: SettingsClientInterface
|
||||||
|
|
||||||
|
const createUseCase = () => new GetRecoveryCodes(authClient, settingsClient)
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
authClient = {} as jest.Mocked<AuthClientInterface>
|
||||||
|
authClient.generateRecoveryCodes = jest.fn().mockResolvedValue('recovery-codes')
|
||||||
|
|
||||||
|
settingsClient = {} as jest.Mocked<SettingsClientInterface>
|
||||||
|
settingsClient.getSetting = jest.fn().mockResolvedValue('existing-recovery-codes')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return existing recovery codes if they exist', async () => {
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute()
|
||||||
|
|
||||||
|
expect(result.getValue()).toBe('existing-recovery-codes')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should generate recovery codes if they do not exist', async () => {
|
||||||
|
settingsClient.getSetting = jest.fn().mockResolvedValue(undefined)
|
||||||
|
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute()
|
||||||
|
|
||||||
|
expect(result.getValue()).toBe('recovery-codes')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return error if recovery codes could not be generated', async () => {
|
||||||
|
settingsClient.getSetting = jest.fn().mockResolvedValue(undefined)
|
||||||
|
authClient.generateRecoveryCodes = jest.fn().mockResolvedValue(false)
|
||||||
|
|
||||||
|
const useCase = createUseCase()
|
||||||
|
|
||||||
|
const result = await useCase.execute()
|
||||||
|
|
||||||
|
expect(result.isFailed()).toBe(true)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { AuthClientInterface } from '@standardnotes/services'
|
||||||
|
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||||
|
import { SettingName } from '@standardnotes/settings'
|
||||||
|
|
||||||
|
import { SettingsClientInterface } from '@Lib/Services/Settings/SettingsClientInterface'
|
||||||
|
|
||||||
|
export class GetRecoveryCodes implements UseCaseInterface<string> {
|
||||||
|
constructor(private authClient: AuthClientInterface, private settingsClient: SettingsClientInterface) {}
|
||||||
|
|
||||||
|
async execute(): Promise<Result<string>> {
|
||||||
|
const existingRecoveryCodes = await this.settingsClient.getSetting(SettingName.RecoveryCodes)
|
||||||
|
if (existingRecoveryCodes !== undefined) {
|
||||||
|
return Result.ok(existingRecoveryCodes)
|
||||||
|
}
|
||||||
|
|
||||||
|
const generatedRecoveryCodes = await this.authClient.generateRecoveryCodes()
|
||||||
|
if (generatedRecoveryCodes === false) {
|
||||||
|
return Result.fail('Could not generate recovery codes')
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result.ok(generatedRecoveryCodes)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,4 +2,5 @@ import { UseCaseInterface } from '@standardnotes/domain-core'
|
|||||||
|
|
||||||
export interface UseCaseContainerInterface {
|
export interface UseCaseContainerInterface {
|
||||||
get signInWithRecoveryCodes(): UseCaseInterface<void>
|
get signInWithRecoveryCodes(): UseCaseInterface<void>
|
||||||
|
get getRecoveryCodes(): UseCaseInterface<string>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,7 +45,7 @@
|
|||||||
"@standardnotes/responses": "workspace:*",
|
"@standardnotes/responses": "workspace:*",
|
||||||
"@standardnotes/security": "^1.7.0",
|
"@standardnotes/security": "^1.7.0",
|
||||||
"@standardnotes/services": "workspace:*",
|
"@standardnotes/services": "workspace:*",
|
||||||
"@standardnotes/settings": "^1.18.4",
|
"@standardnotes/settings": "^1.19.0",
|
||||||
"@standardnotes/sncrypto-common": "workspace:*",
|
"@standardnotes/sncrypto-common": "workspace:*",
|
||||||
"@standardnotes/sncrypto-web": "workspace:*",
|
"@standardnotes/sncrypto-web": "workspace:*",
|
||||||
"@standardnotes/utils": "workspace:*",
|
"@standardnotes/utils": "workspace:*",
|
||||||
|
|||||||
10
yarn.lock
10
yarn.lock
@@ -6025,12 +6025,12 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@standardnotes/settings@npm:^1.18.4":
|
"@standardnotes/settings@npm:^1.19.0":
|
||||||
version: 1.18.4
|
version: 1.19.0
|
||||||
resolution: "@standardnotes/settings@npm:1.18.4"
|
resolution: "@standardnotes/settings@npm:1.19.0"
|
||||||
dependencies:
|
dependencies:
|
||||||
reflect-metadata: ^0.1.13
|
reflect-metadata: ^0.1.13
|
||||||
checksum: 73017d19517c5719a3611385b76a5a3273f03c570c42da79656d0df04cb539f6a5eeb7a5d7db30a8ffc7fd0ef4c801924ff27093f84b2c40ad1542b0397745ac
|
checksum: 85816e5d25e2a4cfe014dbe371bc30902b1bb194f5d3c021fe754678ff9bd693255f1d209a66c4375334b1edd40a8f4133480a20a85b88859b3cfbff1a570cc9
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@@ -6111,7 +6111,7 @@ __metadata:
|
|||||||
"@standardnotes/responses": "workspace:*"
|
"@standardnotes/responses": "workspace:*"
|
||||||
"@standardnotes/security": ^1.7.0
|
"@standardnotes/security": ^1.7.0
|
||||||
"@standardnotes/services": "workspace:*"
|
"@standardnotes/services": "workspace:*"
|
||||||
"@standardnotes/settings": ^1.18.4
|
"@standardnotes/settings": ^1.19.0
|
||||||
"@standardnotes/sncrypto-common": "workspace:*"
|
"@standardnotes/sncrypto-common": "workspace:*"
|
||||||
"@standardnotes/sncrypto-web": "workspace:*"
|
"@standardnotes/sncrypto-web": "workspace:*"
|
||||||
"@standardnotes/utils": "workspace:*"
|
"@standardnotes/utils": "workspace:*"
|
||||||
|
|||||||
Reference in New Issue
Block a user