mirror of
https://github.com/bitwarden/browser
synced 2025-12-14 23:33:31 +00:00
* Extract files only used in cli out of libs/node Move commands from libs/node to cli Move program from libs/node to cli Move services from libs/node to cli Move specs from libs/node to cli Naming changes based on ADR 12 Rename commands Rename models/request Rename models/response Remove entries from whitelist-capital-letters.txt * Merge lowDbStorageService into base class Move logic from extended lowdbStorage.service.ts into base-lowdb-storage.service.ts Delete lowdb-storage.service.ts Rename base-lowdb-storage.service.ts to lowdb-storage.service.ts * Merge login.command with base class program.ts - changed import temporarily to make it easier to review Remove passing in clientId, set "cli" when constructing ssoRedirectUri call Remove setting callbacks, use private methods instead Remove i18nService from constructor params Add syncService, keyConnectorService and logoutCallback to constructor Merge successCallback with handleSuccessResponse Remove validatedParams callback and added private method Move options(program.OptionValues) and set in run() Delete login.command.ts * Rename base-login.command.ts to login.command.ts * Merge base.program.ts with program.ts
134 lines
4.3 KiB
TypeScript
134 lines
4.3 KiB
TypeScript
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
|
import { CipherService } from "@bitwarden/common/abstractions/cipher.service";
|
|
import { FolderApiServiceAbstraction } from "@bitwarden/common/abstractions/folder/folder-api.service.abstraction";
|
|
import { FolderService } from "@bitwarden/common/abstractions/folder/folder.service.abstraction";
|
|
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
|
import { Utils } from "@bitwarden/common/misc/utils";
|
|
|
|
import { Response } from "../models/response";
|
|
import { CliUtils } from "../utils";
|
|
|
|
export class DeleteCommand {
|
|
constructor(
|
|
private cipherService: CipherService,
|
|
private folderService: FolderService,
|
|
private stateService: StateService,
|
|
private apiService: ApiService,
|
|
private folderApiService: FolderApiServiceAbstraction
|
|
) {}
|
|
|
|
async run(object: string, id: string, cmdOptions: Record<string, any>): Promise<Response> {
|
|
if (id != null) {
|
|
id = id.toLowerCase();
|
|
}
|
|
|
|
const normalizedOptions = new Options(cmdOptions);
|
|
switch (object.toLowerCase()) {
|
|
case "item":
|
|
return await this.deleteCipher(id, normalizedOptions);
|
|
case "attachment":
|
|
return await this.deleteAttachment(id, normalizedOptions);
|
|
case "folder":
|
|
return await this.deleteFolder(id);
|
|
case "org-collection":
|
|
return await this.deleteOrganizationCollection(id, normalizedOptions);
|
|
default:
|
|
return Response.badRequest("Unknown object.");
|
|
}
|
|
}
|
|
|
|
private async deleteCipher(id: string, options: Options) {
|
|
const cipher = await this.cipherService.get(id);
|
|
if (cipher == null) {
|
|
return Response.notFound();
|
|
}
|
|
|
|
try {
|
|
if (options.permanent) {
|
|
await this.cipherService.deleteWithServer(id);
|
|
} else {
|
|
await this.cipherService.softDeleteWithServer(id);
|
|
}
|
|
return Response.success();
|
|
} catch (e) {
|
|
return Response.error(e);
|
|
}
|
|
}
|
|
|
|
private async deleteAttachment(id: string, options: Options) {
|
|
if (options.itemId == null || options.itemId === "") {
|
|
return Response.badRequest("`itemid` option is required.");
|
|
}
|
|
|
|
const itemId = options.itemId.toLowerCase();
|
|
const cipher = await this.cipherService.get(itemId);
|
|
if (cipher == null) {
|
|
return Response.notFound();
|
|
}
|
|
|
|
if (cipher.attachments == null || cipher.attachments.length === 0) {
|
|
return Response.error("No attachments available for this item.");
|
|
}
|
|
|
|
const attachments = cipher.attachments.filter((a) => a.id.toLowerCase() === id);
|
|
if (attachments.length === 0) {
|
|
return Response.error("Attachment `" + id + "` was not found.");
|
|
}
|
|
|
|
if (cipher.organizationId == null && !(await this.stateService.getCanAccessPremium())) {
|
|
return Response.error("Premium status is required to use this feature.");
|
|
}
|
|
|
|
try {
|
|
await this.cipherService.deleteAttachmentWithServer(cipher.id, attachments[0].id);
|
|
return Response.success();
|
|
} catch (e) {
|
|
return Response.error(e);
|
|
}
|
|
}
|
|
|
|
private async deleteFolder(id: string) {
|
|
const folder = await this.folderService.getFromState(id);
|
|
if (folder == null) {
|
|
return Response.notFound();
|
|
}
|
|
|
|
try {
|
|
await this.folderApiService.delete(id);
|
|
return Response.success();
|
|
} catch (e) {
|
|
return Response.error(e);
|
|
}
|
|
}
|
|
|
|
private async deleteOrganizationCollection(id: string, options: Options) {
|
|
if (options.organizationId == null || options.organizationId === "") {
|
|
return Response.badRequest("`organizationid` options is required.");
|
|
}
|
|
if (!Utils.isGuid(id)) {
|
|
return Response.badRequest("`" + id + "` is not a GUID.");
|
|
}
|
|
if (!Utils.isGuid(options.organizationId)) {
|
|
return Response.badRequest("`" + options.organizationId + "` is not a GUID.");
|
|
}
|
|
try {
|
|
await this.apiService.deleteCollection(options.organizationId, id);
|
|
return Response.success();
|
|
} catch (e) {
|
|
return Response.error(e);
|
|
}
|
|
}
|
|
}
|
|
|
|
class Options {
|
|
itemId: string;
|
|
organizationId: string;
|
|
permanent: boolean;
|
|
|
|
constructor(passedOptions: Record<string, any>) {
|
|
this.organizationId = passedOptions?.organizationid || passedOptions?.organizationId;
|
|
this.itemId = passedOptions?.itemid || passedOptions?.itemId;
|
|
this.permanent = CliUtils.convertBooleanOption(passedOptions?.permanent);
|
|
}
|
|
}
|