mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-21 16:18:28 +01:00
[SM-249] Adding detailed description of which secrets are unable to be deleted (#4849)
* Adding detailed description of which secrets are unable to be deleted * removing accidental change * removing Can write from Service Account options * suggested PR changes * thomas's suggested changes * fixing merge conflicts after merging in master
This commit is contained in:
parent
9d4d340930
commit
1d01bfbb61
@ -254,10 +254,10 @@ export class OverviewComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
openDeleteSecret(secretIds: string[]) {
|
||||
openDeleteSecret(event: SecretListView[]) {
|
||||
this.dialogService.open<unknown, SecretDeleteOperation>(SecretDeleteDialogComponent, {
|
||||
data: {
|
||||
secretIds: secretIds,
|
||||
secrets: event,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -62,10 +62,10 @@ export class ProjectSecretsComponent {
|
||||
});
|
||||
}
|
||||
|
||||
openDeleteSecret(secretIds: string[]) {
|
||||
openDeleteSecret(event: SecretListView[]) {
|
||||
this.dialogService.open<unknown, SecretDeleteOperation>(SecretDeleteDialogComponent, {
|
||||
data: {
|
||||
secretIds: secretIds,
|
||||
secrets: event,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
<bit-simple-dialog>
|
||||
<span bitDialogTitle>{{ title | i18n }}</span>
|
||||
<span bitDialogContent>
|
||||
<div *ngIf="data.secretIds.length === 1">
|
||||
<div *ngIf="showSoftDeleteSecretWarning">
|
||||
{{ "softDeleteSecretWarning" | i18n }}
|
||||
</div>
|
||||
{{ "deleteItemConfirmation" | i18n }}
|
||||
|
@ -3,11 +3,18 @@ import { Component, Inject } from "@angular/core";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
import { SecretListView } from "../../models/view/secret-list.view";
|
||||
import {
|
||||
BulkOperationStatus,
|
||||
BulkStatusDetails,
|
||||
BulkStatusDialogComponent,
|
||||
} from "../../shared/dialogs/bulk-status-dialog.component";
|
||||
import { SecretService } from "../secret.service";
|
||||
|
||||
export interface SecretDeleteOperation {
|
||||
secretIds: string[];
|
||||
secrets: SecretListView[];
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -20,22 +27,45 @@ export class SecretDeleteDialogComponent {
|
||||
private secretService: SecretService,
|
||||
private i18nService: I18nService,
|
||||
private platformUtilsService: PlatformUtilsService,
|
||||
@Inject(DIALOG_DATA) public data: SecretDeleteOperation
|
||||
@Inject(DIALOG_DATA) private data: SecretDeleteOperation,
|
||||
private dialogService: DialogService
|
||||
) {}
|
||||
|
||||
showSoftDeleteSecretWarning = this.data.secrets.length === 1;
|
||||
|
||||
get title() {
|
||||
return this.data.secretIds.length === 1 ? "deleteSecret" : "deleteSecrets";
|
||||
return this.data.secrets.length === 1 ? "deleteSecret" : "deleteSecrets";
|
||||
}
|
||||
|
||||
get submitButtonText() {
|
||||
return this.data.secretIds.length === 1 ? "deleteSecret" : "deleteSecrets";
|
||||
return this.data.secrets.length === 1 ? "deleteSecret" : "deleteSecrets";
|
||||
}
|
||||
|
||||
delete = async () => {
|
||||
await this.secretService.delete(this.data.secretIds);
|
||||
const bulkResponses = await this.secretService.delete(this.data.secrets);
|
||||
|
||||
if (bulkResponses.find((response) => response.errorMessage)) {
|
||||
this.openBulkStatusDialog(bulkResponses.filter((response) => response.errorMessage));
|
||||
this.dialogRef.close();
|
||||
return;
|
||||
}
|
||||
|
||||
const message =
|
||||
this.data.secretIds.length === 1 ? "softDeleteSuccessToast" : "softDeletesSuccessToast";
|
||||
this.dialogRef.close(this.data.secretIds);
|
||||
this.data.secrets.length === 1 ? "softDeleteSuccessToast" : "softDeletesSuccessToast";
|
||||
this.platformUtilsService.showToast("success", null, this.i18nService.t(message));
|
||||
|
||||
this.dialogRef.close();
|
||||
};
|
||||
|
||||
openBulkStatusDialog(bulkStatusResults: BulkOperationStatus[]) {
|
||||
this.dialogService.open<unknown, BulkStatusDetails>(BulkStatusDialogComponent, {
|
||||
data: {
|
||||
title: "deleteSecrets",
|
||||
subTitle: "secrets",
|
||||
columnTitle: "secretName",
|
||||
message: "bulkDeleteSecretsErrorMessage",
|
||||
details: bulkStatusResults,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,8 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
import { ProjectListView } from "../../models/view/project-list.view";
|
||||
import { SecretListView } from "../../models/view/secret-list.view";
|
||||
import { SecretProjectView } from "../../models/view/secret-project.view";
|
||||
import { SecretView } from "../../models/view/secret.view";
|
||||
import { ProjectService } from "../../projects/project.service";
|
||||
import { SecretService } from "../secret.service";
|
||||
@ -112,11 +114,13 @@ export class SecretDialogComponent implements OnInit {
|
||||
}
|
||||
|
||||
protected openDeleteSecretDialog() {
|
||||
const secretListView: SecretListView[] = this.getSecretListView();
|
||||
|
||||
const dialogRef = this.dialogService.open<unknown, SecretDeleteOperation>(
|
||||
SecretDeleteDialogComponent,
|
||||
{
|
||||
data: {
|
||||
secretIds: [this.data.secretId],
|
||||
secrets: secretListView,
|
||||
},
|
||||
}
|
||||
);
|
||||
@ -146,4 +150,17 @@ export class SecretDialogComponent implements OnInit {
|
||||
secretView.projects = [this.projects.find((p) => p.id == this.formGroup.value.project)];
|
||||
return secretView;
|
||||
}
|
||||
|
||||
private getSecretListView() {
|
||||
const secretListViews: SecretListView[] = [];
|
||||
const emptyProjects: SecretProjectView[] = [];
|
||||
const selectedProject = [this.projects.find((p) => p.id == this.formGroup.value.project)];
|
||||
|
||||
const secretListView = new SecretListView();
|
||||
secretListView.organizationId = this.data.organizationId;
|
||||
secretListView.name = this.formGroup.value.name;
|
||||
secretListView.projects = selectedProject ? selectedProject : emptyProjects;
|
||||
secretListViews.push(secretListView);
|
||||
return secretListViews;
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import { SymmetricCryptoKey } from "@bitwarden/common/models/domain/symmetric-cr
|
||||
import { SecretListView } from "../models/view/secret-list.view";
|
||||
import { SecretProjectView } from "../models/view/secret-project.view";
|
||||
import { SecretView } from "../models/view/secret.view";
|
||||
import { BulkOperationStatus } from "../shared/dialogs/bulk-status-dialog.component";
|
||||
|
||||
import { SecretRequest } from "./requests/secret.request";
|
||||
import { SecretListItemResponse } from "./responses/secret-list-item.response";
|
||||
@ -82,23 +83,18 @@ export class SecretService {
|
||||
this._secret.next(await this.createSecretView(new SecretResponse(r)));
|
||||
}
|
||||
|
||||
async delete(secretIds: string[]) {
|
||||
async delete(secrets: SecretListView[]): Promise<BulkOperationStatus[]> {
|
||||
const secretIds = secrets.map((secret) => secret.id);
|
||||
const r = await this.apiService.send("POST", "/secrets/delete", secretIds, true, true);
|
||||
|
||||
const responseErrors: string[] = [];
|
||||
r.data.forEach((element: { error: string }) => {
|
||||
if (element.error) {
|
||||
responseErrors.push(element.error);
|
||||
}
|
||||
});
|
||||
|
||||
// TODO waiting to hear back on how to display multiple errors.
|
||||
// for now send as a list of strings to be displayed in toast.
|
||||
if (responseErrors?.length >= 1) {
|
||||
throw new Error(responseErrors.join(","));
|
||||
}
|
||||
|
||||
this._secret.next(null);
|
||||
return r.data.map((element: { id: string; error: string }) => {
|
||||
const bulkOperationStatus = new BulkOperationStatus();
|
||||
bulkOperationStatus.id = element.id;
|
||||
bulkOperationStatus.name = secrets.find((secret) => secret.id == element.id).name;
|
||||
bulkOperationStatus.errorMessage = element.error;
|
||||
return bulkOperationStatus;
|
||||
});
|
||||
}
|
||||
|
||||
async getTrashedSecrets(organizationId: string): Promise<SecretListView[]> {
|
||||
|
@ -66,10 +66,10 @@ export class SecretsComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
openDeleteSecret(secretIds: string[]) {
|
||||
openDeleteSecret(event: SecretListView[]) {
|
||||
this.dialogService.open<unknown, SecretDeleteOperation>(SecretDeleteDialogComponent, {
|
||||
data: {
|
||||
secretIds: secretIds,
|
||||
secrets: event,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
@ -124,7 +124,7 @@
|
||||
<i class="bwi bwi-fw bwi-refresh" aria-hidden="true"></i>
|
||||
{{ "restoreSecret" | i18n }}
|
||||
</button>
|
||||
<button type="button" bitMenuItem (click)="deleteSecretsEvent.emit([secret.id])">
|
||||
<button type="button" bitMenuItem (click)="deleteSecretsEvent.emit([secret])">
|
||||
<i class="bwi bwi-fw bwi-trash tw-text-danger" aria-hidden="true"></i>
|
||||
<span class="tw-text-danger">{{
|
||||
(trash ? "permanentlyDelete" : "deleteSecret") | i18n
|
||||
|
@ -35,7 +35,7 @@ export class SecretsListComponent implements OnDestroy {
|
||||
@Output() copySecretNameEvent = new EventEmitter<string>();
|
||||
@Output() copySecretValueEvent = new EventEmitter<string>();
|
||||
@Output() onSecretCheckedEvent = new EventEmitter<string[]>();
|
||||
@Output() deleteSecretsEvent = new EventEmitter<string[]>();
|
||||
@Output() deleteSecretsEvent = new EventEmitter<SecretListView[]>();
|
||||
@Output() newSecretEvent = new EventEmitter();
|
||||
@Output() restoreSecretsEvent = new EventEmitter();
|
||||
|
||||
@ -68,7 +68,9 @@ export class SecretsListComponent implements OnDestroy {
|
||||
|
||||
bulkDeleteSecrets() {
|
||||
if (this.selection.selected.length >= 1) {
|
||||
this.deleteSecretsEvent.emit(this.selection.selected);
|
||||
this.deleteSecretsEvent.emit(
|
||||
this.secrets.filter((secret) => this.selection.isSelected(secret.id))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,9 @@ export class TrashComponent implements OnInit {
|
||||
return await this.secretService.getTrashedSecrets(this.organizationId);
|
||||
}
|
||||
|
||||
openDeleteSecret(secretIds: string[]) {
|
||||
openDeleteSecret(secrets: SecretListView[]) {
|
||||
const secretIds = secrets.map((secret) => secret.id);
|
||||
|
||||
this.dialogService.open<unknown, SecretHardDeleteOperation>(SecretHardDeleteDialogComponent, {
|
||||
data: {
|
||||
secretIds: secretIds,
|
||||
|
Loading…
Reference in New Issue
Block a user