1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-17 16:53:34 +00:00

[PM-14613] Remove account deprovisioning feature flag (#14353)

This commit is contained in:
Thomas Rittson
2025-05-07 11:23:18 +10:00
committed by GitHub
parent 744c1b1b49
commit df40954b61
27 changed files with 153 additions and 519 deletions

View File

@@ -7,7 +7,7 @@
<span bitDialogTitle>
<span *ngIf="!data.orgDomain">{{ "newDomain" | i18n }}</span>
<span *ngIf="data.orgDomain">
{{ ((accountDeprovisioningEnabled$ | async) ? "claimDomain" : "verifyDomain") | i18n }}
{{ "claimDomain" | i18n }}
</span>
<span *ngIf="data.orgDomain" class="tw-text-xs tw-text-muted">
@@ -15,30 +15,17 @@
</span>
<span *ngIf="data?.orgDomain && !data.orgDomain?.verifiedDate" bitBadge variant="warning">
{{
((accountDeprovisioningEnabled$ | async)
? "domainStatusUnderVerification"
: "domainStatusUnverified"
) | i18n
}}
{{ "domainStatusUnderVerification" | i18n }}
</span>
<span *ngIf="data?.orgDomain && data?.orgDomain?.verifiedDate" bitBadge variant="success">
{{
((accountDeprovisioningEnabled$ | async) ? "domainStatusClaimed" : "domainStatusVerified")
| i18n
}}
{{ "domainStatusClaimed" | i18n }}
</span>
</span>
<div bitDialogContent>
<bit-form-field>
<bit-label>{{ "domainName" | i18n }}</bit-label>
<input bitInput appAutofocus formControlName="domainName" [showErrorsWhenDisabled]="true" />
<bit-hint>{{
((accountDeprovisioningEnabled$ | async)
? "claimDomainNameInputHint"
: "domainNameInputHint"
) | i18n
}}</bit-hint>
<bit-hint>{{ "claimDomainNameInputHint" | i18n }}</bit-hint>
</bit-form-field>
<bit-form-field *ngIf="data?.orgDomain">
@@ -57,29 +44,18 @@
<bit-callout
*ngIf="data?.orgDomain && !data?.orgDomain?.verifiedDate"
type="info"
title="{{
(accountDeprovisioningEnabled$ | async)
? ('automaticClaimedDomains' | i18n | uppercase)
: ('automaticDomainVerification' | i18n)
}}"
title="{{ 'automaticClaimedDomains' | i18n | uppercase }}"
>
{{
((accountDeprovisioningEnabled$ | async)
? "automaticDomainClaimProcess"
: "automaticDomainVerificationProcess"
) | i18n
}}
{{ "automaticDomainClaimProcess" | i18n }}
</bit-callout>
</div>
<ng-container bitDialogFooter>
<button type="submit" bitButton bitFormButton buttonType="primary">
<span *ngIf="!data?.orgDomain">{{ "next" | i18n }}</span>
<span *ngIf="data?.orgDomain && !data?.orgDomain?.verifiedDate">{{
((accountDeprovisioningEnabled$ | async) ? "claimDomain" : "verifyDomain") | i18n
}}</span>
<span *ngIf="data?.orgDomain?.verifiedDate">{{
((accountDeprovisioningEnabled$ | async) ? "reclaimDomain" : "reverifyDomain") | i18n
"claimDomain" | i18n
}}</span>
<span *ngIf="data?.orgDomain?.verifiedDate">{{ "reclaimDomain" | i18n }}</span>
</button>
<button bitButton buttonType="secondary" (click)="dialogRef.close()" type="button">
{{ "cancel" | i18n }}

View File

@@ -2,19 +2,15 @@
// @ts-strict-ignore
import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
import { FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from "@angular/forms";
import { Subject, takeUntil, Observable, firstValueFrom } from "rxjs";
import { Subject, takeUntil } from "rxjs";
import { OrgDomainApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization-domain/org-domain-api.service.abstraction";
import { OrgDomainServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization-domain/org-domain.service.abstraction";
import { OrganizationDomainResponse } from "@bitwarden/common/admin-console/abstractions/organization-domain/responses/organization-domain.response";
import { OrganizationDomainRequest } from "@bitwarden/common/admin-console/services/organization-domain/requests/organization-domain.request";
import { HttpStatusCode } from "@bitwarden/common/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
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 { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
import { DialogRef, DIALOG_DATA, DialogService, ToastService } from "@bitwarden/components";
@@ -32,7 +28,6 @@ export interface DomainAddEditDialogData {
export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
private componentDestroyed$: Subject<void> = new Subject();
accountDeprovisioningEnabled$: Observable<boolean>;
domainForm: FormGroup;
get domainNameCtrl(): FormControl {
@@ -50,20 +45,13 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
public dialogRef: DialogRef,
@Inject(DIALOG_DATA) public data: DomainAddEditDialogData,
private formBuilder: FormBuilder,
private cryptoFunctionService: CryptoFunctionServiceAbstraction,
private platformUtilsService: PlatformUtilsService,
private i18nService: I18nService,
private orgDomainApiService: OrgDomainApiServiceAbstraction,
private orgDomainService: OrgDomainServiceAbstraction,
private validationService: ValidationService,
private dialogService: DialogService,
private toastService: ToastService,
private configService: ConfigService,
) {
this.accountDeprovisioningEnabled$ = this.configService.getFeatureFlag$(
FeatureFlag.AccountDeprovisioning,
);
}
) {}
// Angular Method Implementations
@@ -73,11 +61,7 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
"",
[
Validators.required,
domainNameValidator(
(await firstValueFrom(this.accountDeprovisioningEnabled$))
? this.i18nService.t("invalidDomainNameClaimMessage")
: this.i18nService.t("invalidDomainNameMessage"),
),
domainNameValidator(this.i18nService.t("invalidDomainNameClaimMessage")),
uniqueInArrayValidator(
this.data.existingDomainNames,
this.i18nService.t("duplicateDomainError"),
@@ -223,22 +207,13 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(
(await firstValueFrom(this.accountDeprovisioningEnabled$))
? "domainClaimed"
: "domainVerified",
),
message: this.i18nService.t("domainClaimed"),
});
this.dialogRef.close();
} else {
this.domainNameCtrl.setErrors({
errorPassthrough: {
message: this.i18nService.t(
(await firstValueFrom(this.accountDeprovisioningEnabled$))
? "domainNotClaimed"
: "domainNotVerified",
this.domainNameCtrl.value,
),
message: this.i18nService.t("domainNotClaimed", this.domainNameCtrl.value),
},
});
// For the case where user opens dialog and reverifies when domain name formControl disabled.

View File

@@ -4,11 +4,7 @@
</button>
</app-header>
<p
bitTypography="body1"
class="tw-text-main tw-w-2/5"
*ngIf="accountDeprovisioningEnabled$ | async"
>
<p bitTypography="body1" class="tw-text-main tw-w-2/5">
{{ "claimedDomainsDesc" | i18n }}
<a
bitLink
@@ -58,16 +54,10 @@
</td>
<td bitCell>
<span *ngIf="!orgDomain?.verifiedDate" bitBadge variant="warning">{{
((accountDeprovisioningEnabled$ | async)
? "domainStatusUnderVerification"
: "domainStatusUnverified"
) | i18n
"domainStatusUnderVerification" | i18n
}}</span>
<span *ngIf="orgDomain?.verifiedDate" bitBadge variant="success">{{
((accountDeprovisioningEnabled$ | async)
? "domainStatusClaimed"
: "domainStatusVerified"
) | i18n
"domainStatusClaimed" | i18n
}}</span>
</td>
<td bitCell class="tw-text-muted">
@@ -94,10 +84,7 @@
type="button"
>
<i class="bwi bwi-fw bwi-check" aria-hidden="true"></i>
{{
((accountDeprovisioningEnabled$ | async) ? "claimDomain" : "verifyDomain")
| i18n
}}
{{ "claimDomain" | i18n }}
</button>
<button bitMenuItem (click)="deleteDomain(orgDomain.id)" type="button">
<span class="tw-text-danger">

View File

@@ -11,7 +11,6 @@ import {
switchMap,
take,
takeUntil,
withLatestFrom,
} from "rxjs";
import { OrgDomainApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization-domain/org-domain-api.service.abstraction";
@@ -22,7 +21,6 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { HttpStatusCode } from "@bitwarden/common/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@@ -46,7 +44,6 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
organizationId: string;
orgDomains$: Observable<OrganizationDomainResponse[]>;
accountDeprovisioningEnabled$: Observable<boolean>;
constructor(
private route: ActivatedRoute,
@@ -59,11 +56,7 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
private configService: ConfigService,
private policyService: PolicyService,
private accountService: AccountService,
) {
this.accountDeprovisioningEnabled$ = this.configService.getFeatureFlag$(
FeatureFlag.AccountDeprovisioning,
);
}
) {}
async ngOnInit() {
this.orgDomains$ = this.orgDomainService.orgDomains$;
@@ -85,20 +78,18 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
async load() {
await this.orgDomainApiService.getAllByOrgId(this.organizationId);
if (await this.configService.getFeatureFlag(FeatureFlag.AccountDeprovisioning)) {
const singleOrgPolicy = await firstValueFrom(
this.accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) => this.policyService.policies$(userId)),
map((policies) =>
policies.find(
(p) => p.type === PolicyType.SingleOrg && p.organizationId === this.organizationId,
),
const singleOrgPolicy = await firstValueFrom(
this.accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) => this.policyService.policies$(userId)),
map((policies) =>
policies.find(
(p) => p.type === PolicyType.SingleOrg && p.organizationId === this.organizationId,
),
),
);
this.singleOrgPolicyEnabled = singleOrgPolicy?.enabled ?? false;
}
),
);
this.singleOrgPolicyEnabled = singleOrgPolicy?.enabled ?? false;
this.loading = false;
}
@@ -110,29 +101,30 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
existingDomainNames: this.getExistingDomainNames(),
};
await firstValueFrom(
this.configService.getFeatureFlag$(FeatureFlag.AccountDeprovisioning).pipe(
withLatestFrom(this.orgDomains$),
map(async ([accountDeprovisioningEnabled, organizationDomains]) => {
if (
accountDeprovisioningEnabled &&
const showSingleOrgWarning = await firstValueFrom(
this.orgDomains$.pipe(
map(
(organizationDomains) =>
!this.singleOrgPolicyEnabled &&
organizationDomains.every((domain) => domain.verifiedDate === null)
) {
await this.dialogService.openSimpleDialog({
title: { key: "claim-domain-single-org-warning" },
content: { key: "single-org-revoked-user-warning" },
cancelButtonText: { key: "cancel" },
acceptButtonText: { key: "confirm" },
acceptAction: () => this.openAddDomainDialog(domainAddEditDialogData),
type: "info",
});
} else {
await this.openAddDomainDialog(domainAddEditDialogData);
}
}),
organizationDomains.every((domain) => domain.verifiedDate === null),
),
),
);
if (showSingleOrgWarning) {
await this.dialogService.openSimpleDialog({
title: { key: "claim-domain-single-org-warning" },
content: { key: "single-org-revoked-user-warning" },
cancelButtonText: { key: "cancel" },
acceptButtonText: { key: "confirm" },
acceptAction: () => this.openAddDomainDialog(domainAddEditDialogData),
type: "info",
});
return;
}
await this.openAddDomainDialog(domainAddEditDialogData);
}
private async openAddDomainDialog(domainAddEditDialogData: DomainAddEditDialogData) {
@@ -184,22 +176,13 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
this.toastService.showToast({
variant: "success",
title: null,
message: this.i18nService.t(
(await firstValueFrom(this.accountDeprovisioningEnabled$))
? "domainClaimed"
: "domainVerified",
),
message: this.i18nService.t("domainClaimed"),
});
} else {
this.toastService.showToast({
variant: "error",
title: null,
message: this.i18nService.t(
(await firstValueFrom(this.accountDeprovisioningEnabled$))
? "domainNotClaimed"
: "domainNotVerified",
domainName,
),
message: this.i18nService.t("domainNotClaimed", domainName),
});
// Update this item so the last checked date gets updated.
await this.updateOrgDomain(orgDomainId);

View File

@@ -1,10 +1,8 @@
import { inject, NgModule } from "@angular/core";
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import { authGuard } from "@bitwarden/angular/auth/guards";
import { canAccessSettingsTab } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { isEnterpriseOrgGuard } from "@bitwarden/web-vault/app/admin-console/organizations/guards/is-enterprise-org.guard";
import { organizationPermissionsGuard } from "@bitwarden/web-vault/app/admin-console/organizations/guards/org-permissions.guard";
import { OrganizationLayoutComponent } from "@bitwarden/web-vault/app/admin-console/organizations/layouts/organization-layout.component";
@@ -30,12 +28,7 @@ const routes: Routes = [
component: DomainVerificationComponent,
canActivate: [organizationPermissionsGuard((org) => org.canManageDomainVerification)],
resolve: {
titleId: async () => {
const configService = inject(ConfigService);
return (await configService.getFeatureFlag(FeatureFlag.AccountDeprovisioning))
? "claimedDomains"
: "domainVerification";
},
titleId: "claimedDomains",
},
},
{

View File

@@ -31,10 +31,7 @@
<input bitInput type="text" formControlName="ssoIdentifier" />
<bit-hint>
{{ "ssoIdentifierHintPartOne" | i18n }}
<a bitLink routerLink="../domain-verification">{{
((accountDeprovisioningEnabled$ | async) ? "claimedDomains" : "domainVerification")
| i18n
}}</a>
<a bitLink routerLink="../domain-verification">{{ "claimedDomains" | i18n }}</a>
</bit-hint>
</bit-form-field>

View File

@@ -9,7 +9,7 @@ import {
Validators,
} from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
import { concatMap, firstValueFrom, Observable, Subject, takeUntil } from "rxjs";
import { concatMap, firstValueFrom, Subject, takeUntil } from "rxjs";
import { ControlsOf } from "@bitwarden/angular/types/controls-of";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
@@ -33,8 +33,6 @@ import { OrganizationSsoRequest } from "@bitwarden/common/auth/models/request/or
import { OrganizationSsoResponse } from "@bitwarden/common/auth/models/response/organization-sso.response";
import { SsoConfigView } from "@bitwarden/common/auth/models/view/sso-config.view";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
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 { Utils } from "@bitwarden/common/platform/misc/utils";
@@ -191,8 +189,6 @@ export class SsoComponent implements OnInit, OnDestroy {
return this.ssoConfigForm?.controls?.configType as FormControl;
}
accountDeprovisioningEnabled$: Observable<boolean>;
constructor(
private formBuilder: FormBuilder,
private route: ActivatedRoute,
@@ -202,13 +198,8 @@ export class SsoComponent implements OnInit, OnDestroy {
private organizationService: OrganizationService,
private accountService: AccountService,
private organizationApiService: OrganizationApiServiceAbstraction,
private configService: ConfigService,
private toastService: ToastService,
) {
this.accountDeprovisioningEnabled$ = this.configService.getFeatureFlag$(
FeatureFlag.AccountDeprovisioning,
);
}
) {}
async ngOnInit() {
this.enabledCtrl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((enabled) => {