1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-03 18:23:57 +00:00
Files
browser/apps/cli/src/commands/restore.command.ts
Nick Krantz 727689d827 [PM-24534] Archive via CLI (#16502)
* refactor `canInteract` into a component level usage.

- The default service is going to be used in the CLI which won't make use of the UI-related aspects

* all nested entities to be imported from the vault

* initial add of archive command to the cli

* add archive to oss serve

* check for deleted cipher when attempting to archive

* add searchability/list functionality for archived ciphers

* restore an archived cipher

* unarchive a cipher when a user is editing it and has lost their premium status

* add missing feature flags

* re-export only needed services from the vault

* add needed await

* add prompt when applicable for editing an archived cipher

* move cipher archive service into `common/vault`

* fix testing code
2025-09-30 10:45:04 -04:00

88 lines
3.0 KiB
TypeScript

import { firstValueFrom } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CipherId } from "@bitwarden/common/types/guid";
import { CipherArchiveService } from "@bitwarden/common/vault/abstractions/cipher-archive.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
import { CipherAuthorizationService } from "@bitwarden/common/vault/services/cipher-authorization.service";
import { UserId } from "@bitwarden/user-core";
import { Response } from "../models/response";
export class RestoreCommand {
constructor(
private cipherService: CipherService,
private accountService: AccountService,
private cipherAuthorizationService: CipherAuthorizationService,
private cipherArchiveService: CipherArchiveService,
private configService: ConfigService,
) {}
async run(object: string, id: string): Promise<Response> {
if (id != null) {
id = id.toLowerCase();
}
switch (object.toLowerCase()) {
case "item":
return await this.restoreCipher(id);
default:
return Response.badRequest("Unknown object.");
}
}
private async restoreCipher(id: string) {
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const cipher = await this.cipherService.get(id, activeUserId);
const isArchivedVaultEnabled = await firstValueFrom(
this.configService.getFeatureFlag$(FeatureFlag.PM19148_InnovationArchive),
);
if (cipher == null) {
return Response.notFound();
}
if (cipher.archivedDate && isArchivedVaultEnabled) {
return this.restoreArchivedCipher(cipher, activeUserId);
} else {
return this.restoreDeletedCipher(cipher, activeUserId);
}
}
/** Restores a cipher from the trash. */
private async restoreDeletedCipher(cipher: Cipher, userId: UserId) {
if (cipher.deletedDate == null) {
return Response.badRequest("Cipher is not in trash.");
}
const canRestore = await firstValueFrom(
this.cipherAuthorizationService.canRestoreCipher$(cipher),
);
if (!canRestore) {
return Response.error("You do not have permission to restore this item");
}
try {
await this.cipherService.restoreWithServer(cipher.id, userId);
return Response.success();
} catch (e) {
return Response.error(e);
}
}
/** Restore a cipher from the archive vault */
private async restoreArchivedCipher(cipher: Cipher, userId: UserId) {
try {
await this.cipherArchiveService.unarchiveWithServer(cipher.id as CipherId, userId);
return Response.success();
} catch (e) {
return Response.error(e);
}
}
}