bitwarden-estensione-browser/apps/web/src/app/vault/individual-vault/bulk-action-dialogs/bulk-delete-dialog/bulk-delete-dialog.componen...

188 lines
6.5 KiB
TypeScript

import { DIALOG_DATA, DialogConfig, DialogRef } from "@angular/cdk/dialog";
import { Component, Inject } from "@angular/core";
import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service";
import { CipherBulkDeleteRequest } from "@bitwarden/common/vault/models/request/cipher-bulk-delete.request";
import { CollectionView } from "@bitwarden/common/vault/models/view/collection.view";
import { DialogService } from "@bitwarden/components";
export interface BulkDeleteDialogParams {
cipherIds?: string[];
permanent?: boolean;
organization?: Organization;
organizations?: Organization[];
collections?: CollectionView[];
}
export enum BulkDeleteDialogResult {
Deleted = "deleted",
Canceled = "canceled",
}
/**
* Strongly typed helper to open a BulkDeleteDialog
* @param dialogService Instance of the dialog service that will be used to open the dialog
* @param config Configuration for the dialog
*/
export const openBulkDeleteDialog = (
dialogService: DialogService,
config: DialogConfig<BulkDeleteDialogParams>,
) => {
return dialogService.open<BulkDeleteDialogResult, BulkDeleteDialogParams>(
BulkDeleteDialogComponent,
config,
);
};
@Component({
templateUrl: "bulk-delete-dialog.component.html",
})
export class BulkDeleteDialogComponent {
cipherIds: string[];
permanent = false;
organization: Organization;
organizations: Organization[];
collections: CollectionView[];
private flexibleCollectionsV1Enabled$ = this.configService.getFeatureFlag$(
FeatureFlag.FlexibleCollectionsV1,
);
private restrictProviderAccess$ = this.configService.getFeatureFlag$(
FeatureFlag.RestrictProviderAccess,
);
constructor(
@Inject(DIALOG_DATA) params: BulkDeleteDialogParams,
private dialogRef: DialogRef<BulkDeleteDialogResult>,
private cipherService: CipherService,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private apiService: ApiService,
private collectionService: CollectionService,
private configService: ConfigService,
) {
this.cipherIds = params.cipherIds ?? [];
this.permanent = params.permanent;
this.organization = params.organization;
this.organizations = params.organizations;
this.collections = params.collections;
}
protected async cancel() {
this.close(BulkDeleteDialogResult.Canceled);
}
protected submit = async () => {
const deletePromises: Promise<void>[] = [];
if (this.cipherIds.length) {
const flexibleCollectionsV1Enabled = await firstValueFrom(this.flexibleCollectionsV1Enabled$);
const restrictProviderAccess = await firstValueFrom(this.restrictProviderAccess$);
if (
!this.organization ||
!this.organization.canEditAllCiphers(flexibleCollectionsV1Enabled, restrictProviderAccess)
) {
deletePromises.push(this.deleteCiphers());
} else {
deletePromises.push(this.deleteCiphersAdmin());
}
}
if (this.collections.length) {
deletePromises.push(this.deleteCollections());
}
await Promise.all(deletePromises);
if (this.cipherIds.length) {
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t(this.permanent ? "permanentlyDeletedItems" : "deletedItems"),
);
}
if (this.collections.length) {
await this.collectionService.delete(this.collections.map((c) => c.id));
this.platformUtilsService.showToast(
"success",
null,
this.i18nService.t("deletedCollections"),
);
}
this.close(BulkDeleteDialogResult.Deleted);
};
private async deleteCiphers(): Promise<any> {
const flexibleCollectionsV1Enabled = await firstValueFrom(this.flexibleCollectionsV1Enabled$);
const restrictProviderAccess = await firstValueFrom(this.restrictProviderAccess$);
const asAdmin = this.organization?.canEditAllCiphers(
flexibleCollectionsV1Enabled,
restrictProviderAccess,
);
if (this.permanent) {
await this.cipherService.deleteManyWithServer(this.cipherIds, asAdmin);
} else {
await this.cipherService.softDeleteManyWithServer(this.cipherIds, asAdmin);
}
}
private async deleteCiphersAdmin(): Promise<any> {
const deleteRequest = new CipherBulkDeleteRequest(this.cipherIds, this.organization.id);
if (this.permanent) {
return await this.apiService.deleteManyCiphersAdmin(deleteRequest);
} else {
return await this.apiService.putDeleteManyCiphersAdmin(deleteRequest);
}
}
private async deleteCollections(): Promise<any> {
// From org vault
if (this.organization) {
if (this.collections.some((c) => !c.canDelete(this.organization))) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("missingPermissions"),
);
return;
}
return await this.apiService.deleteManyCollections(
this.organization.id,
this.collections.map((c) => c.id),
);
// From individual vault, so there can be multiple organizations
} else if (this.organizations && this.collections) {
const deletePromises: Promise<any>[] = [];
for (const organization of this.organizations) {
const orgCollections = this.collections.filter((o) => o.organizationId === organization.id);
if (orgCollections.some((c) => !c.canDelete(organization))) {
this.platformUtilsService.showToast(
"error",
this.i18nService.t("errorOccurred"),
this.i18nService.t("missingPermissions"),
);
return;
}
const orgCollectionIds = orgCollections.map((c) => c.id);
deletePromises.push(
this.apiService.deleteManyCollections(organization.id, orgCollectionIds),
);
}
return await Promise.all(deletePromises);
}
}
private close(result: BulkDeleteDialogResult) {
this.dialogRef.close(result);
}
}