mirror of
https://github.com/bitwarden/browser
synced 2025-12-11 22:03:36 +00:00
[PM-7004] Verify org delete from emailed link (#8445)
* add verify org delete page * PR feedback from thomas * use abstraction * Apply suggestions from code review Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com> * delete org copy * Move verify-recover-delete-org component to admin-console/organizations/manage folder and update routing * [PM-7004] Add async/await to ngOnInit in verify-recover-delete-org.component.ts * [PM-7004] Update deleteRecoverOrgConfirmDesc message in messages.json * [PM-7004] Add warning message for deleting organization's active user accounts * [PM-7004] Update to standalone component * [PM-7004] Update delete organization warning message * [PM-7004] Refactor delete organization form * [PM-7004] Delete unused selector in verify-recover-delete-org.component.ts * [PM-7004] Rename recoverDeleteToken method in verify-recover-delete-org.component.ts to deleteUsingToken * [PM-7004] Update formGroup initialization in verify-recover-delete-org.component.ts * [PM-7004] Delete formGroup initialization in verify-recover-delete-org.component.ts * [PM-7004] Remove try/catch from submit method in verify-recover-delete-org.component.ts * [PM-7004] Update submit button type in verify-recover-delete-org.component.html * [PM-7004] Remove manual loading state in verify-recover-delete-org.component * [PM-7004] Remove unnecessary span in verify-recover-delete-org.component.html * [PM-7004] Update button styles in verify-recover-delete-org.component.html * [PM-7004] Add back in the manual loading state in verify-recover-delete-org.component * [PM-7004] Update button type and class in verify-recover-delete-org.component.html * [PM-7004] Replace bootstrap classes with equivalent tailwind classes * [PM-7004] Replace bootstrap classes with Tailwind in verify-recover-delete-org.component.html --------- Co-authored-by: Rui Tomé <108268980+r-tome@users.noreply.github.com> Co-authored-by: Rui Tome <rtome@bitwarden.com>
This commit is contained in:
@@ -0,0 +1,39 @@
|
|||||||
|
<div class="tw-flex tw-flex-row tw-justify-center tw-mt-5">
|
||||||
|
<div class="tw-w-1/4">
|
||||||
|
<p class="tw-text-xl tw-text-center tw-mb-4">{{ "deleteOrganization" | i18n }}</p>
|
||||||
|
<div class="tw-rounded-md tw-border tw-border-solid tw-border-secondary-300 tw-bg-background">
|
||||||
|
<div class="tw-p-5">
|
||||||
|
<app-callout type="warning">{{
|
||||||
|
"deletingOrganizationIsPermanentWarning" | i18n: name
|
||||||
|
}}</app-callout>
|
||||||
|
<p class="tw-text-center">
|
||||||
|
<strong>{{ name }}</strong>
|
||||||
|
</p>
|
||||||
|
<p>{{ "deleteRecoverOrgConfirmDesc" | i18n }}</p>
|
||||||
|
<p>{{ "deletingOrganizationActiveUserAccountsWarning" | i18n }}</p>
|
||||||
|
<hr />
|
||||||
|
<div class="tw-flex">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
bitButton
|
||||||
|
buttonType="danger"
|
||||||
|
[bitAction]="submit"
|
||||||
|
[disabled]="loading"
|
||||||
|
[block]="true"
|
||||||
|
>
|
||||||
|
{{ "deleteOrganization" | i18n }}
|
||||||
|
</button>
|
||||||
|
<a
|
||||||
|
routerLink="/login"
|
||||||
|
bitButton
|
||||||
|
buttonType="secondary"
|
||||||
|
[block]="true"
|
||||||
|
class="tw-ml-2 tw-mt-0"
|
||||||
|
>
|
||||||
|
{{ "cancel" | i18n }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { OrganizationApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
|
||||||
|
import { OrganizationVerifyDeleteRecoverRequest } from "@bitwarden/common/admin-console/models/request/organization-verify-delete-recover.request";
|
||||||
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
|
||||||
|
|
||||||
|
import { SharedModule } from "../../../shared/shared.module";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
templateUrl: "verify-recover-delete-org.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [SharedModule],
|
||||||
|
})
|
||||||
|
export class VerifyRecoverDeleteOrgComponent implements OnInit {
|
||||||
|
loading = true;
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
private orgId: string;
|
||||||
|
private token: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private router: Router,
|
||||||
|
private apiService: OrganizationApiServiceAbstraction,
|
||||||
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
const qParams = await firstValueFrom(this.route.queryParams);
|
||||||
|
if (qParams.orgId != null && qParams.token != null && qParams.name != null) {
|
||||||
|
this.orgId = qParams.orgId;
|
||||||
|
this.token = qParams.token;
|
||||||
|
this.name = qParams.name;
|
||||||
|
this.loading = false;
|
||||||
|
} else {
|
||||||
|
await this.router.navigate(["/"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
submit = async () => {
|
||||||
|
const request = new OrganizationVerifyDeleteRecoverRequest(this.token);
|
||||||
|
await this.apiService.deleteUsingToken(this.orgId, request);
|
||||||
|
this.platformUtilsService.showToast(
|
||||||
|
"success",
|
||||||
|
this.i18nService.t("organizationDeleted"),
|
||||||
|
this.i18nService.t("organizationDeletedDesc"),
|
||||||
|
);
|
||||||
|
await this.router.navigate(["/"]);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import {
|
|||||||
|
|
||||||
import { flagEnabled, Flags } from "../utils/flags";
|
import { flagEnabled, Flags } from "../utils/flags";
|
||||||
|
|
||||||
|
import { VerifyRecoverDeleteOrgComponent } from "./admin-console/organizations/manage/verify-recover-delete-org.component";
|
||||||
import { AcceptFamilySponsorshipComponent } from "./admin-console/organizations/sponsorships/accept-family-sponsorship.component";
|
import { AcceptFamilySponsorshipComponent } from "./admin-console/organizations/sponsorships/accept-family-sponsorship.component";
|
||||||
import { FamiliesForEnterpriseSetupComponent } from "./admin-console/organizations/sponsorships/families-for-enterprise-setup.component";
|
import { FamiliesForEnterpriseSetupComponent } from "./admin-console/organizations/sponsorships/families-for-enterprise-setup.component";
|
||||||
import { VerifyRecoverDeleteProviderComponent } from "./admin-console/providers/verify-recover-delete-provider.component";
|
import { VerifyRecoverDeleteProviderComponent } from "./admin-console/providers/verify-recover-delete-provider.component";
|
||||||
@@ -157,6 +158,12 @@ const routes: Routes = [
|
|||||||
canActivate: [UnauthGuard],
|
canActivate: [UnauthGuard],
|
||||||
data: { titleId: "deleteAccount" },
|
data: { titleId: "deleteAccount" },
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "verify-recover-delete-org",
|
||||||
|
component: VerifyRecoverDeleteOrgComponent,
|
||||||
|
canActivate: [UnauthGuard],
|
||||||
|
data: { titleId: "deleteOrganization" },
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "verify-recover-delete-provider",
|
path: "verify-recover-delete-provider",
|
||||||
component: VerifyRecoverDeleteProviderComponent,
|
component: VerifyRecoverDeleteProviderComponent,
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { LayoutComponent, NavigationModule } from "@bitwarden/components";
|
|||||||
import { OrganizationLayoutComponent } from "../admin-console/organizations/layouts/organization-layout.component";
|
import { OrganizationLayoutComponent } from "../admin-console/organizations/layouts/organization-layout.component";
|
||||||
import { EventsComponent as OrgEventsComponent } from "../admin-console/organizations/manage/events.component";
|
import { EventsComponent as OrgEventsComponent } from "../admin-console/organizations/manage/events.component";
|
||||||
import { UserConfirmComponent as OrgUserConfirmComponent } from "../admin-console/organizations/manage/user-confirm.component";
|
import { UserConfirmComponent as OrgUserConfirmComponent } from "../admin-console/organizations/manage/user-confirm.component";
|
||||||
|
import { VerifyRecoverDeleteOrgComponent } from "../admin-console/organizations/manage/verify-recover-delete-org.component";
|
||||||
import { AcceptFamilySponsorshipComponent } from "../admin-console/organizations/sponsorships/accept-family-sponsorship.component";
|
import { AcceptFamilySponsorshipComponent } from "../admin-console/organizations/sponsorships/accept-family-sponsorship.component";
|
||||||
import { ExposedPasswordsReportComponent as OrgExposedPasswordsReportComponent } from "../admin-console/organizations/tools/exposed-passwords-report.component";
|
import { ExposedPasswordsReportComponent as OrgExposedPasswordsReportComponent } from "../admin-console/organizations/tools/exposed-passwords-report.component";
|
||||||
import { InactiveTwoFactorReportComponent as OrgInactiveTwoFactorReportComponent } from "../admin-console/organizations/tools/inactive-two-factor-report.component";
|
import { InactiveTwoFactorReportComponent as OrgInactiveTwoFactorReportComponent } from "../admin-console/organizations/tools/inactive-two-factor-report.component";
|
||||||
@@ -115,6 +116,7 @@ import { SharedModule } from "./shared.module";
|
|||||||
OrganizationLayoutComponent,
|
OrganizationLayoutComponent,
|
||||||
UserLayoutComponent,
|
UserLayoutComponent,
|
||||||
PaymentMethodWarningsModule,
|
PaymentMethodWarningsModule,
|
||||||
|
VerifyRecoverDeleteOrgComponent,
|
||||||
],
|
],
|
||||||
declarations: [
|
declarations: [
|
||||||
AcceptFamilySponsorshipComponent,
|
AcceptFamilySponsorshipComponent,
|
||||||
|
|||||||
@@ -1341,6 +1341,9 @@
|
|||||||
"accountDeletedDesc": {
|
"accountDeletedDesc": {
|
||||||
"message": "Your account has been closed and all associated data has been deleted."
|
"message": "Your account has been closed and all associated data has been deleted."
|
||||||
},
|
},
|
||||||
|
"deleteOrganizationWarning": {
|
||||||
|
"message": "Deleting your organization is permanent. It cannot be undone."
|
||||||
|
},
|
||||||
"myAccount": {
|
"myAccount": {
|
||||||
"message": "My account"
|
"message": "My account"
|
||||||
},
|
},
|
||||||
@@ -3388,6 +3391,9 @@
|
|||||||
"deleteRecoverConfirmDesc": {
|
"deleteRecoverConfirmDesc": {
|
||||||
"message": "You have requested to delete your Bitwarden account. Use the button below to confirm."
|
"message": "You have requested to delete your Bitwarden account. Use the button below to confirm."
|
||||||
},
|
},
|
||||||
|
"deleteRecoverOrgConfirmDesc": {
|
||||||
|
"message": "You have requested to delete your Bitwarden organization."
|
||||||
|
},
|
||||||
"myOrganization": {
|
"myOrganization": {
|
||||||
"message": "My organization"
|
"message": "My organization"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import { OrganizationCreateRequest } from "../../models/request/organization-cre
|
|||||||
import { OrganizationKeysRequest } from "../../models/request/organization-keys.request";
|
import { OrganizationKeysRequest } from "../../models/request/organization-keys.request";
|
||||||
import { OrganizationUpdateRequest } from "../../models/request/organization-update.request";
|
import { OrganizationUpdateRequest } from "../../models/request/organization-update.request";
|
||||||
import { OrganizationUpgradeRequest } from "../../models/request/organization-upgrade.request";
|
import { OrganizationUpgradeRequest } from "../../models/request/organization-upgrade.request";
|
||||||
|
import { OrganizationVerifyDeleteRecoverRequest } from "../../models/request/organization-verify-delete-recover.request";
|
||||||
import { OrganizationApiKeyInformationResponse } from "../../models/response/organization-api-key-information.response";
|
import { OrganizationApiKeyInformationResponse } from "../../models/response/organization-api-key-information.response";
|
||||||
import { OrganizationAutoEnrollStatusResponse } from "../../models/response/organization-auto-enroll-status.response";
|
import { OrganizationAutoEnrollStatusResponse } from "../../models/response/organization-auto-enroll-status.response";
|
||||||
import { OrganizationKeysResponse } from "../../models/response/organization-keys.response";
|
import { OrganizationKeysResponse } from "../../models/response/organization-keys.response";
|
||||||
@@ -54,6 +55,10 @@ export class OrganizationApiServiceAbstraction {
|
|||||||
reinstate: (id: string) => Promise<void>;
|
reinstate: (id: string) => Promise<void>;
|
||||||
leave: (id: string) => Promise<void>;
|
leave: (id: string) => Promise<void>;
|
||||||
delete: (id: string, request: SecretVerificationRequest) => Promise<void>;
|
delete: (id: string, request: SecretVerificationRequest) => Promise<void>;
|
||||||
|
deleteUsingToken: (
|
||||||
|
organizationId: string,
|
||||||
|
request: OrganizationVerifyDeleteRecoverRequest,
|
||||||
|
) => Promise<any>;
|
||||||
updateLicense: (id: string, data: FormData) => Promise<void>;
|
updateLicense: (id: string, data: FormData) => Promise<void>;
|
||||||
importDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise<void>;
|
importDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise<void>;
|
||||||
getOrCreateApiKey: (id: string, request: OrganizationApiKeyRequest) => Promise<ApiKeyResponse>;
|
getOrCreateApiKey: (id: string, request: OrganizationApiKeyRequest) => Promise<ApiKeyResponse>;
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
export class OrganizationVerifyDeleteRecoverRequest {
|
||||||
|
token: string;
|
||||||
|
|
||||||
|
constructor(token: string) {
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,6 +26,7 @@ import { OrganizationCreateRequest } from "../../models/request/organization-cre
|
|||||||
import { OrganizationKeysRequest } from "../../models/request/organization-keys.request";
|
import { OrganizationKeysRequest } from "../../models/request/organization-keys.request";
|
||||||
import { OrganizationUpdateRequest } from "../../models/request/organization-update.request";
|
import { OrganizationUpdateRequest } from "../../models/request/organization-update.request";
|
||||||
import { OrganizationUpgradeRequest } from "../../models/request/organization-upgrade.request";
|
import { OrganizationUpgradeRequest } from "../../models/request/organization-upgrade.request";
|
||||||
|
import { OrganizationVerifyDeleteRecoverRequest } from "../../models/request/organization-verify-delete-recover.request";
|
||||||
import { OrganizationApiKeyInformationResponse } from "../../models/response/organization-api-key-information.response";
|
import { OrganizationApiKeyInformationResponse } from "../../models/response/organization-api-key-information.response";
|
||||||
import { OrganizationAutoEnrollStatusResponse } from "../../models/response/organization-auto-enroll-status.response";
|
import { OrganizationAutoEnrollStatusResponse } from "../../models/response/organization-auto-enroll-status.response";
|
||||||
import { OrganizationKeysResponse } from "../../models/response/organization-keys.response";
|
import { OrganizationKeysResponse } from "../../models/response/organization-keys.response";
|
||||||
@@ -198,6 +199,19 @@ export class OrganizationApiService implements OrganizationApiServiceAbstraction
|
|||||||
await this.syncService.fullSync(true);
|
await this.syncService.fullSync(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deleteUsingToken(
|
||||||
|
organizationId: string,
|
||||||
|
request: OrganizationVerifyDeleteRecoverRequest,
|
||||||
|
): Promise<any> {
|
||||||
|
return this.apiService.send(
|
||||||
|
"POST",
|
||||||
|
"/organizations/" + organizationId + "/delete-recover-token",
|
||||||
|
request,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
async updateLicense(id: string, data: FormData): Promise<void> {
|
async updateLicense(id: string, data: FormData): Promise<void> {
|
||||||
await this.apiService.send(
|
await this.apiService.send(
|
||||||
"POST",
|
"POST",
|
||||||
|
|||||||
Reference in New Issue
Block a user