chore: should validate password before deleting vault (#2400)
This commit is contained in:
@@ -168,6 +168,7 @@ export const ChallengeStrings = {
|
||||
DeleteAccount: 'Authentication is required to delete your account',
|
||||
ListedAuthorization: 'Authentication is required to approve this note for Listed',
|
||||
UnlockVault: (vaultName: string) => `Unlock ${vaultName}`,
|
||||
DeleteVault: (vaultName: string) => `Delete ${vaultName}`,
|
||||
EnterVaultPassword: 'Enter the password for this vault',
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { VaultListingInterface } from '@standardnotes/models'
|
||||
import { ProtectionsClientInterface } from '../../Protection/ProtectionClientInterface'
|
||||
import { VaultLockServiceInterface } from '../../VaultLock/VaultLockServiceInterface'
|
||||
import {
|
||||
ChallengeReason,
|
||||
Challenge,
|
||||
ChallengePrompt,
|
||||
ChallengeServiceInterface,
|
||||
ChallengeValidation,
|
||||
} from '../../Challenge'
|
||||
import { ChallengeStrings } from '../../Strings/Messages'
|
||||
import { ValidateVaultPassword } from '../../VaultLock/UseCase/ValidateVaultPassword'
|
||||
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
|
||||
|
||||
export class AuthorizeVaultDeletion implements UseCaseInterface<boolean> {
|
||||
constructor(
|
||||
private vaultLocks: VaultLockServiceInterface,
|
||||
private protection: ProtectionsClientInterface,
|
||||
private challenges: ChallengeServiceInterface,
|
||||
private _validateVaultPassword: ValidateVaultPassword,
|
||||
) {}
|
||||
|
||||
async execute(vault: VaultListingInterface): Promise<Result<boolean>> {
|
||||
if (!this.vaultLocks.isVaultLockable(vault)) {
|
||||
const authorized = await this.protection.authorizeAction(ChallengeReason.Custom, {
|
||||
fallBackToAccountPassword: true,
|
||||
requireAccountPassword: false,
|
||||
forcePrompt: true,
|
||||
})
|
||||
return Result.ok(authorized)
|
||||
}
|
||||
|
||||
const challenge = new Challenge(
|
||||
[new ChallengePrompt(ChallengeValidation.None, undefined, 'Password')],
|
||||
ChallengeReason.Custom,
|
||||
true,
|
||||
ChallengeStrings.DeleteVault(vault.name),
|
||||
ChallengeStrings.EnterVaultPassword,
|
||||
)
|
||||
|
||||
return new Promise((resolve) => {
|
||||
this.challenges.addChallengeObserver(challenge, {
|
||||
onCancel() {
|
||||
resolve(Result.ok(false))
|
||||
},
|
||||
onNonvalidatedSubmit: async (challengeResponse) => {
|
||||
const value = challengeResponse.getDefaultValue()
|
||||
if (!value) {
|
||||
this.challenges.completeChallenge(challenge)
|
||||
resolve(Result.ok(false))
|
||||
return
|
||||
}
|
||||
|
||||
const password = value.value as string
|
||||
|
||||
const validPassword = this._validateVaultPassword.execute(vault, password).getValue()
|
||||
if (!validPassword) {
|
||||
this.challenges.setValidationStatusForChallenge(challenge, value, false)
|
||||
resolve(Result.ok(false))
|
||||
return
|
||||
}
|
||||
|
||||
this.challenges.completeChallenge(challenge)
|
||||
resolve(Result.ok(true))
|
||||
},
|
||||
})
|
||||
|
||||
void this.challenges.promptForChallengeResponse(challenge)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -33,6 +33,7 @@ import { AlertService } from '../Alert/AlertService'
|
||||
import { GetVaults } from './UseCase/GetVaults'
|
||||
import { VaultLockServiceInterface } from '../VaultLock/VaultLockServiceInterface'
|
||||
import { Result } from '@standardnotes/domain-core'
|
||||
import { AuthorizeVaultDeletion } from './UseCase/AuthorizeVaultDeletion'
|
||||
|
||||
export class VaultService
|
||||
extends AbstractService<VaultServiceEvent, VaultServiceEventPayload[VaultServiceEvent]>
|
||||
@@ -55,6 +56,7 @@ export class VaultService
|
||||
private _sendVaultDataChangeMessage: SendVaultDataChangedMessage,
|
||||
private _isVaultOwner: IsVaultOwner,
|
||||
private _validateVaultPassword: ValidateVaultPassword,
|
||||
private _authorizeVaultDeletion: AuthorizeVaultDeletion,
|
||||
eventBus: InternalEventBusInterface,
|
||||
) {
|
||||
super(eventBus)
|
||||
@@ -183,6 +185,10 @@ export class VaultService
|
||||
return this.items.findSureItem(item.uuid)
|
||||
}
|
||||
|
||||
authorizeVaultDeletion(vault: VaultListingInterface): Promise<Result<boolean>> {
|
||||
return this._authorizeVaultDeletion.execute(vault)
|
||||
}
|
||||
|
||||
async deleteVault(vault: VaultListingInterface): Promise<boolean> {
|
||||
if (vault.isSharedVaultListing()) {
|
||||
throw new Error('Shared vault must be deleted through SharedVaultService')
|
||||
|
||||
@@ -29,6 +29,7 @@ export interface VaultServiceInterface
|
||||
|
||||
getVaults(): VaultListingInterface[]
|
||||
getVault(dto: { keySystemIdentifier: KeySystemIdentifier }): VaultListingInterface | undefined
|
||||
authorizeVaultDeletion(vault: VaultListingInterface): Promise<Result<boolean>>
|
||||
deleteVault(vault: VaultListingInterface): Promise<boolean>
|
||||
|
||||
moveItemToVault(
|
||||
|
||||
@@ -164,6 +164,7 @@ import { Logger, isNotUndefined, isDeinitable } from '@standardnotes/utils'
|
||||
import { EncryptionOperators } from '@standardnotes/encryption'
|
||||
import { AsymmetricMessagePayload, AsymmetricMessageSharedVaultInvite } from '@standardnotes/models'
|
||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||
import { AuthorizeVaultDeletion } from '@standardnotes/services/src/Domain/Vault/UseCase/AuthorizeVaultDeletion'
|
||||
|
||||
export class Dependencies {
|
||||
private factory = new Map<symbol, () => unknown>()
|
||||
@@ -225,6 +226,15 @@ export class Dependencies {
|
||||
)
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.AuthorizeVaultDeletion, () => {
|
||||
return new AuthorizeVaultDeletion(
|
||||
this.get<VaultLockService>(TYPES.VaultLockService),
|
||||
this.get<ProtectionService>(TYPES.ProtectionService),
|
||||
this.get<ChallengeService>(TYPES.ChallengeService),
|
||||
this.get<ValidateVaultPassword>(TYPES.ValidateVaultPassword),
|
||||
)
|
||||
})
|
||||
|
||||
this.factory.set(TYPES.GenerateUuid, () => {
|
||||
return new GenerateUuid(this.get<PureCryptoInterface>(TYPES.Crypto))
|
||||
})
|
||||
@@ -879,6 +889,7 @@ export class Dependencies {
|
||||
this.get<SendVaultDataChangedMessage>(TYPES.SendVaultDataChangedMessage),
|
||||
this.get<IsVaultOwner>(TYPES.IsVaultOwner),
|
||||
this.get<ValidateVaultPassword>(TYPES.ValidateVaultPassword),
|
||||
this.get<AuthorizeVaultDeletion>(TYPES.AuthorizeVaultDeletion),
|
||||
this.get<InternalEventBus>(TYPES.InternalEventBus),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -163,6 +163,7 @@ export const TYPES = {
|
||||
GenerateUuid: Symbol.for('GenerateUuid'),
|
||||
GetVaultItems: Symbol.for('GetVaultItems'),
|
||||
ValidateVaultPassword: Symbol.for('ValidateVaultPassword'),
|
||||
AuthorizeVaultDeletion: Symbol.for('AuthorizeVaultDeletion'),
|
||||
|
||||
// Mappers
|
||||
SessionStorageMapper: Symbol.for('SessionStorageMapper'),
|
||||
|
||||
@@ -29,10 +29,17 @@ const VaultItem = ({ vault }: Props) => {
|
||||
undefined,
|
||||
ButtonType.Danger,
|
||||
)
|
||||
|
||||
if (!confirm) {
|
||||
return
|
||||
}
|
||||
|
||||
const authorized = await application.vaults.authorizeVaultDeletion(vault)
|
||||
|
||||
if (!authorized.getValue()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (vault.isSharedVaultListing()) {
|
||||
const result = await application.sharedVaults.deleteSharedVault(vault)
|
||||
if (isClientDisplayableError(result)) {
|
||||
|
||||
Reference in New Issue
Block a user