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',
|
DeleteAccount: 'Authentication is required to delete your account',
|
||||||
ListedAuthorization: 'Authentication is required to approve this note for Listed',
|
ListedAuthorization: 'Authentication is required to approve this note for Listed',
|
||||||
UnlockVault: (vaultName: string) => `Unlock ${vaultName}`,
|
UnlockVault: (vaultName: string) => `Unlock ${vaultName}`,
|
||||||
|
DeleteVault: (vaultName: string) => `Delete ${vaultName}`,
|
||||||
EnterVaultPassword: 'Enter the password for this vault',
|
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 { GetVaults } from './UseCase/GetVaults'
|
||||||
import { VaultLockServiceInterface } from '../VaultLock/VaultLockServiceInterface'
|
import { VaultLockServiceInterface } from '../VaultLock/VaultLockServiceInterface'
|
||||||
import { Result } from '@standardnotes/domain-core'
|
import { Result } from '@standardnotes/domain-core'
|
||||||
|
import { AuthorizeVaultDeletion } from './UseCase/AuthorizeVaultDeletion'
|
||||||
|
|
||||||
export class VaultService
|
export class VaultService
|
||||||
extends AbstractService<VaultServiceEvent, VaultServiceEventPayload[VaultServiceEvent]>
|
extends AbstractService<VaultServiceEvent, VaultServiceEventPayload[VaultServiceEvent]>
|
||||||
@@ -55,6 +56,7 @@ export class VaultService
|
|||||||
private _sendVaultDataChangeMessage: SendVaultDataChangedMessage,
|
private _sendVaultDataChangeMessage: SendVaultDataChangedMessage,
|
||||||
private _isVaultOwner: IsVaultOwner,
|
private _isVaultOwner: IsVaultOwner,
|
||||||
private _validateVaultPassword: ValidateVaultPassword,
|
private _validateVaultPassword: ValidateVaultPassword,
|
||||||
|
private _authorizeVaultDeletion: AuthorizeVaultDeletion,
|
||||||
eventBus: InternalEventBusInterface,
|
eventBus: InternalEventBusInterface,
|
||||||
) {
|
) {
|
||||||
super(eventBus)
|
super(eventBus)
|
||||||
@@ -183,6 +185,10 @@ export class VaultService
|
|||||||
return this.items.findSureItem(item.uuid)
|
return this.items.findSureItem(item.uuid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
authorizeVaultDeletion(vault: VaultListingInterface): Promise<Result<boolean>> {
|
||||||
|
return this._authorizeVaultDeletion.execute(vault)
|
||||||
|
}
|
||||||
|
|
||||||
async deleteVault(vault: VaultListingInterface): Promise<boolean> {
|
async deleteVault(vault: VaultListingInterface): Promise<boolean> {
|
||||||
if (vault.isSharedVaultListing()) {
|
if (vault.isSharedVaultListing()) {
|
||||||
throw new Error('Shared vault must be deleted through SharedVaultService')
|
throw new Error('Shared vault must be deleted through SharedVaultService')
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ export interface VaultServiceInterface
|
|||||||
|
|
||||||
getVaults(): VaultListingInterface[]
|
getVaults(): VaultListingInterface[]
|
||||||
getVault(dto: { keySystemIdentifier: KeySystemIdentifier }): VaultListingInterface | undefined
|
getVault(dto: { keySystemIdentifier: KeySystemIdentifier }): VaultListingInterface | undefined
|
||||||
|
authorizeVaultDeletion(vault: VaultListingInterface): Promise<Result<boolean>>
|
||||||
deleteVault(vault: VaultListingInterface): Promise<boolean>
|
deleteVault(vault: VaultListingInterface): Promise<boolean>
|
||||||
|
|
||||||
moveItemToVault(
|
moveItemToVault(
|
||||||
|
|||||||
@@ -164,6 +164,7 @@ import { Logger, isNotUndefined, isDeinitable } from '@standardnotes/utils'
|
|||||||
import { EncryptionOperators } from '@standardnotes/encryption'
|
import { EncryptionOperators } from '@standardnotes/encryption'
|
||||||
import { AsymmetricMessagePayload, AsymmetricMessageSharedVaultInvite } from '@standardnotes/models'
|
import { AsymmetricMessagePayload, AsymmetricMessageSharedVaultInvite } from '@standardnotes/models'
|
||||||
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
|
||||||
|
import { AuthorizeVaultDeletion } from '@standardnotes/services/src/Domain/Vault/UseCase/AuthorizeVaultDeletion'
|
||||||
|
|
||||||
export class Dependencies {
|
export class Dependencies {
|
||||||
private factory = new Map<symbol, () => unknown>()
|
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, () => {
|
this.factory.set(TYPES.GenerateUuid, () => {
|
||||||
return new GenerateUuid(this.get<PureCryptoInterface>(TYPES.Crypto))
|
return new GenerateUuid(this.get<PureCryptoInterface>(TYPES.Crypto))
|
||||||
})
|
})
|
||||||
@@ -879,6 +889,7 @@ export class Dependencies {
|
|||||||
this.get<SendVaultDataChangedMessage>(TYPES.SendVaultDataChangedMessage),
|
this.get<SendVaultDataChangedMessage>(TYPES.SendVaultDataChangedMessage),
|
||||||
this.get<IsVaultOwner>(TYPES.IsVaultOwner),
|
this.get<IsVaultOwner>(TYPES.IsVaultOwner),
|
||||||
this.get<ValidateVaultPassword>(TYPES.ValidateVaultPassword),
|
this.get<ValidateVaultPassword>(TYPES.ValidateVaultPassword),
|
||||||
|
this.get<AuthorizeVaultDeletion>(TYPES.AuthorizeVaultDeletion),
|
||||||
this.get<InternalEventBus>(TYPES.InternalEventBus),
|
this.get<InternalEventBus>(TYPES.InternalEventBus),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -163,6 +163,7 @@ export const TYPES = {
|
|||||||
GenerateUuid: Symbol.for('GenerateUuid'),
|
GenerateUuid: Symbol.for('GenerateUuid'),
|
||||||
GetVaultItems: Symbol.for('GetVaultItems'),
|
GetVaultItems: Symbol.for('GetVaultItems'),
|
||||||
ValidateVaultPassword: Symbol.for('ValidateVaultPassword'),
|
ValidateVaultPassword: Symbol.for('ValidateVaultPassword'),
|
||||||
|
AuthorizeVaultDeletion: Symbol.for('AuthorizeVaultDeletion'),
|
||||||
|
|
||||||
// Mappers
|
// Mappers
|
||||||
SessionStorageMapper: Symbol.for('SessionStorageMapper'),
|
SessionStorageMapper: Symbol.for('SessionStorageMapper'),
|
||||||
|
|||||||
@@ -29,10 +29,17 @@ const VaultItem = ({ vault }: Props) => {
|
|||||||
undefined,
|
undefined,
|
||||||
ButtonType.Danger,
|
ButtonType.Danger,
|
||||||
)
|
)
|
||||||
|
|
||||||
if (!confirm) {
|
if (!confirm) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const authorized = await application.vaults.authorizeVaultDeletion(vault)
|
||||||
|
|
||||||
|
if (!authorized.getValue()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (vault.isSharedVaultListing()) {
|
if (vault.isSharedVaultListing()) {
|
||||||
const result = await application.sharedVaults.deleteSharedVault(vault)
|
const result = await application.sharedVaults.deleteSharedVault(vault)
|
||||||
if (isClientDisplayableError(result)) {
|
if (isClientDisplayableError(result)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user