mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-21 16:18:28 +01:00
[PM-10323] Add delete option to managed members (#11655)
* Add managedByOrganization property to OrganizationUserUserDetailsResponse and OrganizationUserView * Add managedByOrganization property to OrganizationUserDetailsResponse and OrganizationUserAdminView * Add deleteOrganizationUser method to OrganizationUserApiService * Add copy strings for organization user delete dialog * Add copy string for organization user deleted toast * Add delete organization user dialog component * Add the option to delete managed organization users from the members list * Refactor delete user confirmation dialog in MembersComponent to use DialogService * Delete DeleteOrganizationUserDialogComponent * Refactor delete button in member dialog component to change the icon and tooltip text to 'Remove' * Add delete button to members dialog if the user is managed by the organization
This commit is contained in:
parent
2d0460eb15
commit
d669d2003f
@ -264,6 +264,16 @@
|
||||
<button
|
||||
*ngIf="editMode"
|
||||
type="button"
|
||||
bitIconButton="bwi-close"
|
||||
buttonType="danger"
|
||||
bitFormButton
|
||||
[appA11yTitle]="'remove' | i18n"
|
||||
[bitAction]="remove"
|
||||
[disabled]="loading"
|
||||
></button>
|
||||
<button
|
||||
*ngIf="editMode && params.managedByOrganization === true"
|
||||
type="button"
|
||||
bitIconButton="bwi-trash"
|
||||
buttonType="danger"
|
||||
bitFormButton
|
||||
|
@ -65,6 +65,7 @@ export interface MemberDialogParams {
|
||||
isOnSecretsManagerStandalone: boolean;
|
||||
initialTab?: MemberDialogTab;
|
||||
numConfirmedMembers: number;
|
||||
managedByOrganization?: boolean;
|
||||
}
|
||||
|
||||
export enum MemberDialogResult {
|
||||
@ -464,7 +465,7 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
this.close(MemberDialogResult.Saved);
|
||||
};
|
||||
|
||||
delete = async () => {
|
||||
remove = async () => {
|
||||
if (!this.editMode) {
|
||||
return;
|
||||
}
|
||||
@ -561,6 +562,39 @@ export class MemberDialogComponent implements OnDestroy {
|
||||
this.close(MemberDialogResult.Restored);
|
||||
};
|
||||
|
||||
delete = async () => {
|
||||
if (!this.editMode) {
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: {
|
||||
key: "deleteOrganizationUser",
|
||||
placeholders: [this.params.name],
|
||||
},
|
||||
content: { key: "deleteOrganizationUserWarning" },
|
||||
type: "warning",
|
||||
acceptButtonText: { key: "delete" },
|
||||
cancelButtonText: { key: "cancel" },
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
await this.organizationUserApiService.deleteOrganizationUser(
|
||||
this.params.organizationId,
|
||||
this.params.organizationUserId,
|
||||
);
|
||||
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("organizationUserDeleted", this.params.name),
|
||||
});
|
||||
this.close(MemberDialogResult.Deleted);
|
||||
};
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy$.next();
|
||||
this.destroy$.complete();
|
||||
|
@ -320,6 +320,17 @@
|
||||
<i aria-hidden="true" class="bwi bwi-close"></i> {{ "remove" | i18n }}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
*ngIf="u.managedByOrganization === true"
|
||||
type="button"
|
||||
bitMenuItem
|
||||
(click)="deleteUser(u)"
|
||||
>
|
||||
<span class="tw-text-danger">
|
||||
<i class="bwi bwi-fw bwi-trash" aria-hidden="true"></i>
|
||||
{{ "delete" | i18n }}
|
||||
</span>
|
||||
</button>
|
||||
</bit-menu>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -518,6 +518,7 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
||||
isOnSecretsManagerStandalone: this.orgIsOnSecretsManagerStandalone,
|
||||
initialTab: initialTab,
|
||||
numConfirmedMembers: this.dataSource.confirmedUserCount,
|
||||
managedByOrganization: user?.managedByOrganization,
|
||||
},
|
||||
});
|
||||
|
||||
@ -725,6 +726,40 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
||||
return true;
|
||||
}
|
||||
|
||||
async deleteUser(user: OrganizationUserView) {
|
||||
const confirmed = await this.dialogService.openSimpleDialog({
|
||||
title: {
|
||||
key: "deleteOrganizationUser",
|
||||
placeholders: [this.userNamePipe.transform(user)],
|
||||
},
|
||||
content: { key: "deleteOrganizationUserWarning" },
|
||||
type: "warning",
|
||||
acceptButtonText: { key: "delete" },
|
||||
cancelButtonText: { key: "cancel" },
|
||||
});
|
||||
|
||||
if (!confirmed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.actionPromise = this.organizationUserApiService.deleteOrganizationUser(
|
||||
this.organization.id,
|
||||
user.id,
|
||||
);
|
||||
try {
|
||||
await this.actionPromise;
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
message: this.i18nService.t("organizationUserDeleted", this.userNamePipe.transform(user)),
|
||||
});
|
||||
this.dataSource.removeUser(user);
|
||||
} catch (e) {
|
||||
this.validationService.showError(e);
|
||||
}
|
||||
this.actionPromise = null;
|
||||
}
|
||||
|
||||
private async noMasterPasswordConfirmationDialog(user: OrganizationUserView) {
|
||||
return this.dialogService.openSimpleDialog({
|
||||
title: {
|
||||
|
@ -9554,5 +9554,31 @@
|
||||
},
|
||||
"single-org-revoked-user-warning": {
|
||||
"message": "Non-compliant members will be revoked. Administrators can restore members once they leave all other organizations."
|
||||
},
|
||||
"deleteOrganizationUser": {
|
||||
"message": "Delete $NAME$",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"content": "$1",
|
||||
"example": "John Doe"
|
||||
},
|
||||
"description": "Title for the delete organization user dialog"
|
||||
}
|
||||
},
|
||||
"deleteOrganizationUserWarning": {
|
||||
"message": "When a member is deleted, their Bitwarden account and individual vault data will be permanently deleted. Collection data will remain in the organization. To reinstate them they must create an account and be onboarded again.",
|
||||
"description": "Warning for the delete organization user dialog"
|
||||
},
|
||||
"organizationUserDeleted": {
|
||||
"message": "Deleted $NAME$",
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"content": "$1",
|
||||
"example": "John Doe"
|
||||
}
|
||||
}
|
||||
},
|
||||
"organizationUserDeletedDesc": {
|
||||
"message": "The user was removed from the organization and all associated user data has been deleted."
|
||||
}
|
||||
}
|
||||
|
@ -275,4 +275,11 @@ export abstract class OrganizationUserApiService {
|
||||
organizationId: string,
|
||||
ids: string[],
|
||||
): Promise<ListResponse<OrganizationUserBulkResponse>>;
|
||||
|
||||
/**
|
||||
* Remove an organization user's access to the organization and delete their account data
|
||||
* @param organizationId - Identifier for the organization the user belongs to
|
||||
* @param id - Organization user identifier
|
||||
*/
|
||||
abstract deleteOrganizationUser(organizationId: string, id: string): Promise<void>;
|
||||
}
|
||||
|
@ -359,4 +359,14 @@ export class DefaultOrganizationUserApiService implements OrganizationUserApiSer
|
||||
);
|
||||
return new ListResponse(r, OrganizationUserBulkResponse);
|
||||
}
|
||||
|
||||
deleteOrganizationUser(organizationId: string, id: string): Promise<void> {
|
||||
return this.apiService.send(
|
||||
"DELETE",
|
||||
"/organizations/" + organizationId + "/users/" + id + "/delete-account",
|
||||
null,
|
||||
true,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user