1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-21 11:35:34 +01: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:
Kyle Spearrin 2024-05-22 13:00:02 -04:00 committed by GitHub
parent 594a6f1daf
commit 5a29fee7e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 134 additions and 0 deletions

View File

@ -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>

View File

@ -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(["/"]);
};
}

View File

@ -11,6 +11,7 @@ import {
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 { FamiliesForEnterpriseSetupComponent } from "./admin-console/organizations/sponsorships/families-for-enterprise-setup.component";
import { VerifyRecoverDeleteProviderComponent } from "./admin-console/providers/verify-recover-delete-provider.component";
@ -157,6 +158,12 @@ const routes: Routes = [
canActivate: [UnauthGuard],
data: { titleId: "deleteAccount" },
},
{
path: "verify-recover-delete-org",
component: VerifyRecoverDeleteOrgComponent,
canActivate: [UnauthGuard],
data: { titleId: "deleteOrganization" },
},
{
path: "verify-recover-delete-provider",
component: VerifyRecoverDeleteProviderComponent,

View File

@ -9,6 +9,7 @@ import { LayoutComponent, NavigationModule } from "@bitwarden/components";
import { OrganizationLayoutComponent } from "../admin-console/organizations/layouts/organization-layout.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 { VerifyRecoverDeleteOrgComponent } from "../admin-console/organizations/manage/verify-recover-delete-org.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 { InactiveTwoFactorReportComponent as OrgInactiveTwoFactorReportComponent } from "../admin-console/organizations/tools/inactive-two-factor-report.component";
@ -115,6 +116,7 @@ import { SharedModule } from "./shared.module";
OrganizationLayoutComponent,
UserLayoutComponent,
PaymentMethodWarningsModule,
VerifyRecoverDeleteOrgComponent,
],
declarations: [
AcceptFamilySponsorshipComponent,

View File

@ -1341,6 +1341,9 @@
"accountDeletedDesc": {
"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": {
"message": "My account"
},
@ -3388,6 +3391,9 @@
"deleteRecoverConfirmDesc": {
"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": {
"message": "My organization"
},

View File

@ -23,6 +23,7 @@ import { OrganizationCreateRequest } from "../../models/request/organization-cre
import { OrganizationKeysRequest } from "../../models/request/organization-keys.request";
import { OrganizationUpdateRequest } from "../../models/request/organization-update.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 { OrganizationAutoEnrollStatusResponse } from "../../models/response/organization-auto-enroll-status.response";
import { OrganizationKeysResponse } from "../../models/response/organization-keys.response";
@ -54,6 +55,10 @@ export class OrganizationApiServiceAbstraction {
reinstate: (id: string) => Promise<void>;
leave: (id: string) => Promise<void>;
delete: (id: string, request: SecretVerificationRequest) => Promise<void>;
deleteUsingToken: (
organizationId: string,
request: OrganizationVerifyDeleteRecoverRequest,
) => Promise<any>;
updateLicense: (id: string, data: FormData) => Promise<void>;
importDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise<void>;
getOrCreateApiKey: (id: string, request: OrganizationApiKeyRequest) => Promise<ApiKeyResponse>;

View File

@ -0,0 +1,7 @@
export class OrganizationVerifyDeleteRecoverRequest {
token: string;
constructor(token: string) {
this.token = token;
}
}

View File

@ -26,6 +26,7 @@ import { OrganizationCreateRequest } from "../../models/request/organization-cre
import { OrganizationKeysRequest } from "../../models/request/organization-keys.request";
import { OrganizationUpdateRequest } from "../../models/request/organization-update.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 { OrganizationAutoEnrollStatusResponse } from "../../models/response/organization-auto-enroll-status.response";
import { OrganizationKeysResponse } from "../../models/response/organization-keys.response";
@ -198,6 +199,19 @@ export class OrganizationApiService implements OrganizationApiServiceAbstraction
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> {
await this.apiService.send(
"POST",