1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-12-17 15:37:57 +01:00

[PM-15154] Domain verification copy update (#12217)

* refactor: update domain verification terminology to claimed domains

* feat: add description for claimed domains in domain verification

* Add informational link to claimed domains description in domain verification component

* Update domain verification references to claimed domains in organization layout and SSO component

* Add validation message for invalid domain name format in domain verification

* Add domain claim messages and descriptions to localization files

* Update domain verification navigation text based on feature flag

* Update domain verification dialog to support account deprovisioning feature flag

* Update domain verification component to support account deprovisioning feature flag

* Refactor domain verification dialog to use synchronous feature flag for account deprovisioning

* Refactor domain verification routing to resolve title based on account deprovisioning feature flag

* Update SSO component to conditionally display domain verification link based on account deprovisioning feature flag

* Update event service to conditionally display domain verification messages based on account deprovisioning feature flag

* Update domain verification warning message

* Refactor domain verification navigation text handling based on account deprovisioning feature flag

* Refactor domain verification dialog to use observable for account deprovisioning feature flag

* Refactor domain verification component to use observable for account deprovisioning feature flag
This commit is contained in:
Rui Tomé 2024-12-11 14:47:49 +00:00 committed by GitHub
parent d4fcb5852a
commit b502e2bc25
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 223 additions and 44 deletions

View File

@ -98,7 +98,7 @@
*ngIf="canAccessExport$ | async" *ngIf="canAccessExport$ | async"
></bit-nav-item> ></bit-nav-item>
<bit-nav-item <bit-nav-item
[text]="'domainVerification' | i18n" [text]="domainVerificationNavigationTextKey | i18n"
route="settings/domain-verification" route="settings/domain-verification"
*ngIf="organization?.canManageDomainVerification" *ngIf="organization?.canManageDomainVerification"
></bit-nav-item> ></bit-nav-item>

View File

@ -23,6 +23,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
import { ProductTierType } from "@bitwarden/common/billing/enums"; import { ProductTierType } from "@bitwarden/common/billing/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; 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 { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { getById } from "@bitwarden/common/platform/misc"; import { getById } from "@bitwarden/common/platform/misc";
import { BannerModule, IconModule } from "@bitwarden/components"; import { BannerModule, IconModule } from "@bitwarden/components";
@ -49,6 +50,7 @@ export class OrganizationLayoutComponent implements OnInit {
protected readonly logo = AdminConsoleLogo; protected readonly logo = AdminConsoleLogo;
protected orgFilter = (org: Organization) => canAccessOrgAdmin(org); protected orgFilter = (org: Organization) => canAccessOrgAdmin(org);
protected domainVerificationNavigationTextKey: string;
protected integrationPageEnabled$: Observable<boolean>; protected integrationPageEnabled$: Observable<boolean>;
@ -67,6 +69,7 @@ export class OrganizationLayoutComponent implements OnInit {
private configService: ConfigService, private configService: ConfigService,
private policyService: PolicyService, private policyService: PolicyService,
private providerService: ProviderService, private providerService: ProviderService,
private i18nService: I18nService,
) {} ) {}
async ngOnInit() { async ngOnInit() {
@ -116,6 +119,12 @@ export class OrganizationLayoutComponent implements OnInit {
org.productTierType === ProductTierType.Enterprise && featureFlagEnabled, org.productTierType === ProductTierType.Enterprise && featureFlagEnabled,
), ),
); );
this.domainVerificationNavigationTextKey = (await this.configService.getFeatureFlag(
FeatureFlag.AccountDeprovisioning,
))
? "claimedDomains"
: "domainVerification";
} }
canShowVaultTab(organization: Organization): boolean { canShowVaultTab(organization: Organization): boolean {

View File

@ -6,7 +6,9 @@ import { PolicyService } from "@bitwarden/common/admin-console/abstractions/poli
import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { PolicyType } from "@bitwarden/common/admin-console/enums";
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
import { DeviceType, EventType } from "@bitwarden/common/enums"; import { DeviceType, EventType } from "@bitwarden/common/enums";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { EventResponse } from "@bitwarden/common/models/response/event.response"; import { EventResponse } from "@bitwarden/common/models/response/event.response";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
@Injectable() @Injectable()
@ -16,6 +18,7 @@ export class EventService {
constructor( constructor(
private i18nService: I18nService, private i18nService: I18nService,
policyService: PolicyService, policyService: PolicyService,
private configService: ConfigService,
) { ) {
policyService.policies$.subscribe((policies) => { policyService.policies$.subscribe((policies) => {
this.policies = policies; this.policies = policies;
@ -451,10 +454,20 @@ export class EventService {
msg = humanReadableMsg = this.i18nService.t("removedDomain", ev.domainName); msg = humanReadableMsg = this.i18nService.t("removedDomain", ev.domainName);
break; break;
case EventType.OrganizationDomain_Verified: case EventType.OrganizationDomain_Verified:
msg = humanReadableMsg = this.i18nService.t("domainVerifiedEvent", ev.domainName); msg = humanReadableMsg = this.i18nService.t(
(await this.configService.getFeatureFlag(FeatureFlag.AccountDeprovisioning))
? "domainClaimedEvent"
: "domainVerifiedEvent",
ev.domainName,
);
break; break;
case EventType.OrganizationDomain_NotVerified: case EventType.OrganizationDomain_NotVerified:
msg = humanReadableMsg = this.i18nService.t("domainNotVerifiedEvent", ev.domainName); msg = humanReadableMsg = this.i18nService.t(
(await this.configService.getFeatureFlag(FeatureFlag.AccountDeprovisioning))
? "domainNotClaimedEvent"
: "domainNotVerifiedEvent",
ev.domainName,
);
break; break;
// Secrets Manager // Secrets Manager
case EventType.Secret_Retrieved: case EventType.Secret_Retrieved:

View File

@ -9801,8 +9801,8 @@
"selfHostingTitleProper": { "selfHostingTitleProper": {
"message": "Self-Hosting" "message": "Self-Hosting"
}, },
"verified-domain-single-org-warning" : { "claim-domain-single-org-warning" : {
"message": "Verifying a domain will turn on the single organization policy." "message": "Claiming a domain will turn on the single organization policy."
}, },
"single-org-revoked-user-warning": { "single-org-revoked-user-warning": {
"message": "Non-compliant members will be revoked. Administrators can restore members once they leave all other organizations." "message": "Non-compliant members will be revoked. Administrators can restore members once they leave all other organizations."
@ -9902,5 +9902,62 @@
}, },
"removeMembers": { "removeMembers": {
"message": "Remove members" "message": "Remove members"
},
"claimedDomains": {
"message": "Claimed domains"
},
"claimDomain": {
"message": "Claim domain"
},
"reclaimDomain": {
"message": "Reclaim domain"
},
"claimDomainNameInputHint": {
"message": "Example: mydomain.com. Subdomains require separate entries to be claimed."
},
"automaticClaimedDomains": {
"message": "Automatic Claimed Domains"
},
"automaticDomainClaimProcess": {
"message": "Bitwarden will attempt to claim the domain 3 times during the first 72 hours. If the domain cant be claimed, check the DNS record in your host and manually claim. The domain will be removed from your organization in 7 days if it is not claimed."
},
"domainNotClaimed": {
"message": "$DOMAIN$ not claimed. Check your DNS records.",
"placeholders": {
"DOMAIN": {
"content": "$1",
"example": "bitwarden.com"
}
}
},
"domainStatusClaimed": {
"message": "Claimed"
},
"domainStatusUnderVerification": {
"message": "Under verification"
},
"claimedDomainsDesc": {
"message": "Claim a domain to own all member accounts whose email address matches the domain. Members will be able to skip the SSO identifier when logging in. Administrators will also be able to delete member accounts."
},
"invalidDomainNameClaimMessage": {
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be claimed."
},
"domainClaimedEvent": {
"message": "$DOMAIN$ claimed",
"placeholders": {
"DOMAIN": {
"content": "$1",
"example": "bitwarden.com"
}
}
},
"domainNotClaimedEvent": {
"message": "$DOMAIN$ not claimed",
"placeholders": {
"DOMAIN": {
"content": "$1",
"example": "bitwarden.com"
}
}
} }
} }

View File

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

View File

@ -3,14 +3,16 @@
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog"; import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
import { Component, Inject, OnDestroy, OnInit } from "@angular/core"; import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
import { FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from "@angular/forms"; import { FormBuilder, FormControl, FormGroup, ValidatorFn, Validators } from "@angular/forms";
import { Subject, takeUntil } from "rxjs"; import { Subject, takeUntil, Observable, firstValueFrom } from "rxjs";
import { OrgDomainApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/organization-domain/org-domain-api.service.abstraction"; 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 { 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 { 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 { OrganizationDomainRequest } from "@bitwarden/common/admin-console/services/organization-domain/requests/organization-domain.request";
import { HttpStatusCode } from "@bitwarden/common/enums"; 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 { ErrorResponse } from "@bitwarden/common/models/response/error.response";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service"; import { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/platform/abstractions/crypto-function.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@ -31,20 +33,8 @@ export interface DomainAddEditDialogData {
export class DomainAddEditDialogComponent implements OnInit, OnDestroy { export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
private componentDestroyed$: Subject<void> = new Subject(); private componentDestroyed$: Subject<void> = new Subject();
domainForm: FormGroup = this.formBuilder.group({ accountDeprovisioningEnabled$: Observable<boolean>;
domainName: [ domainForm: FormGroup;
"",
[
Validators.required,
domainNameValidator(this.i18nService.t("invalidDomainNameMessage")),
uniqueInArrayValidator(
this.data.existingDomainNames,
this.i18nService.t("duplicateDomainError"),
),
],
],
txt: [{ value: null, disabled: true }],
});
get domainNameCtrl(): FormControl { get domainNameCtrl(): FormControl {
return this.domainForm.controls.domainName as FormControl; return this.domainForm.controls.domainName as FormControl;
@ -69,11 +59,34 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
private validationService: ValidationService, private validationService: ValidationService,
private dialogService: DialogService, private dialogService: DialogService,
private toastService: ToastService, private toastService: ToastService,
) {} private configService: ConfigService,
) {
this.accountDeprovisioningEnabled$ = this.configService.getFeatureFlag$(
FeatureFlag.AccountDeprovisioning,
);
}
// Angular Method Implementations // Angular Method Implementations
async ngOnInit(): Promise<void> { async ngOnInit(): Promise<void> {
this.domainForm = this.formBuilder.group({
domainName: [
"",
[
Validators.required,
domainNameValidator(
(await firstValueFrom(this.accountDeprovisioningEnabled$))
? this.i18nService.t("invalidDomainNameClaimMessage")
: this.i18nService.t("invalidDomainNameMessage"),
),
uniqueInArrayValidator(
this.data.existingDomainNames,
this.i18nService.t("duplicateDomainError"),
),
],
],
txt: [{ value: null, disabled: true }],
});
// If we have data.orgDomain, then editing, otherwise creating new domain // If we have data.orgDomain, then editing, otherwise creating new domain
await this.populateForm(); await this.populateForm();
} }
@ -211,13 +224,22 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
this.toastService.showToast({ this.toastService.showToast({
variant: "success", variant: "success",
title: null, title: null,
message: this.i18nService.t("domainVerified"), message: this.i18nService.t(
(await firstValueFrom(this.accountDeprovisioningEnabled$))
? "domainClaimed"
: "domainVerified",
),
}); });
this.dialogRef.close(); this.dialogRef.close();
} else { } else {
this.domainNameCtrl.setErrors({ this.domainNameCtrl.setErrors({
errorPassthrough: { errorPassthrough: {
message: this.i18nService.t("domainNotVerified", this.domainNameCtrl.value), message: this.i18nService.t(
(await firstValueFrom(this.accountDeprovisioningEnabled$))
? "domainNotClaimed"
: "domainNotVerified",
this.domainNameCtrl.value,
),
}, },
}); });
// For the case where user opens dialog and reverifies when domain name formControl disabled. // For the case where user opens dialog and reverifies when domain name formControl disabled.

View File

@ -4,6 +4,20 @@
</button> </button>
</app-header> </app-header>
<p class="tw-text-muted tw-w-1/3" *ngIf="accountDeprovisioningEnabled$ | async">
{{ "claimedDomainsDesc" | i18n }}
<a
bitLink
target="_blank"
rel="noreferrer"
appA11yTitle="{{ 'learnMore' | i18n }}"
href="https://bitwarden.com/help/claimed-accounts/"
slot="end"
>
<i class="bwi bwi-question-circle" aria-hidden="true"></i>
</a>
</p>
<ng-container *ngIf="loading"> <ng-container *ngIf="loading">
<i <i
class="bwi bwi-spinner bwi-spin text-muted" class="bwi bwi-spinner bwi-spin text-muted"
@ -40,10 +54,16 @@
</td> </td>
<td bitCell> <td bitCell>
<span *ngIf="!orgDomain?.verifiedDate" bitBadge variant="warning">{{ <span *ngIf="!orgDomain?.verifiedDate" bitBadge variant="warning">{{
"domainStatusUnverified" | i18n ((accountDeprovisioningEnabled$ | async)
? "domainStatusUnderVerification"
: "domainStatusUnverified"
) | i18n
}}</span> }}</span>
<span *ngIf="orgDomain?.verifiedDate" bitBadge variant="success">{{ <span *ngIf="orgDomain?.verifiedDate" bitBadge variant="success">{{
"domainStatusVerified" | i18n ((accountDeprovisioningEnabled$ | async)
? "domainStatusClaimed"
: "domainStatusVerified"
) | i18n
}}</span> }}</span>
</td> </td>
<td bitCell class="tw-text-muted"> <td bitCell class="tw-text-muted">
@ -70,7 +90,10 @@
type="button" type="button"
> >
<i class="bwi bwi-fw bwi-check" aria-hidden="true"></i> <i class="bwi bwi-fw bwi-check" aria-hidden="true"></i>
{{ "verifyDomain" | i18n }} {{
((accountDeprovisioningEnabled$ | async) ? "claimDomain" : "verifyDomain")
| i18n
}}
</button> </button>
<button bitMenuItem (click)="deleteDomain(orgDomain.id)" type="button"> <button bitMenuItem (click)="deleteDomain(orgDomain.id)" type="button">
<span class="tw-text-danger"> <span class="tw-text-danger">

View File

@ -43,6 +43,7 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
organizationId: string; organizationId: string;
orgDomains$: Observable<OrganizationDomainResponse[]>; orgDomains$: Observable<OrganizationDomainResponse[]>;
accountDeprovisioningEnabled$: Observable<boolean>;
constructor( constructor(
private route: ActivatedRoute, private route: ActivatedRoute,
@ -54,7 +55,11 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
private toastService: ToastService, private toastService: ToastService,
private configService: ConfigService, private configService: ConfigService,
private policyApiService: PolicyApiServiceAbstraction, private policyApiService: PolicyApiServiceAbstraction,
) {} ) {
this.accountDeprovisioningEnabled$ = this.configService.getFeatureFlag$(
FeatureFlag.AccountDeprovisioning,
);
}
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
async ngOnInit() { async ngOnInit() {
@ -105,7 +110,7 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
organizationDomains.every((domain) => domain.verifiedDate === null) organizationDomains.every((domain) => domain.verifiedDate === null)
) { ) {
await this.dialogService.openSimpleDialog({ await this.dialogService.openSimpleDialog({
title: { key: "verified-domain-single-org-warning" }, title: { key: "claim-domain-single-org-warning" },
content: { key: "single-org-revoked-user-warning" }, content: { key: "single-org-revoked-user-warning" },
cancelButtonText: { key: "cancel" }, cancelButtonText: { key: "cancel" },
acceptButtonText: { key: "confirm" }, acceptButtonText: { key: "confirm" },
@ -169,13 +174,22 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
this.toastService.showToast({ this.toastService.showToast({
variant: "success", variant: "success",
title: null, title: null,
message: this.i18nService.t("domainVerified"), message: this.i18nService.t(
(await firstValueFrom(this.accountDeprovisioningEnabled$))
? "domainClaimed"
: "domainVerified",
),
}); });
} else { } else {
this.toastService.showToast({ this.toastService.showToast({
variant: "error", variant: "error",
title: null, title: null,
message: this.i18nService.t("domainNotVerified", domainName), message: this.i18nService.t(
(await firstValueFrom(this.accountDeprovisioningEnabled$))
? "domainNotClaimed"
: "domainNotVerified",
domainName,
),
}); });
// Update this item so the last checked date gets updated. // Update this item so the last checked date gets updated.
await this.updateOrgDomain(orgDomainId); await this.updateOrgDomain(orgDomainId);

View File

@ -1,8 +1,10 @@
import { NgModule } from "@angular/core"; import { inject, NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router"; import { RouterModule, Routes } from "@angular/router";
import { authGuard } from "@bitwarden/angular/auth/guards"; import { authGuard } from "@bitwarden/angular/auth/guards";
import { canAccessSettingsTab } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; 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 { 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 { 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"; import { OrganizationLayoutComponent } from "@bitwarden/web-vault/app/admin-console/organizations/layouts/organization-layout.component";
@ -26,8 +28,13 @@ const routes: Routes = [
path: "domain-verification", path: "domain-verification",
component: DomainVerificationComponent, component: DomainVerificationComponent,
canActivate: [organizationPermissionsGuard((org) => org.canManageDomainVerification)], canActivate: [organizationPermissionsGuard((org) => org.canManageDomainVerification)],
data: { resolve: {
titleId: "domainVerification", titleId: async () => {
const configService = inject(ConfigService);
return (await configService.getFeatureFlag(FeatureFlag.AccountDeprovisioning))
? "claimedDomains"
: "domainVerification";
},
}, },
}, },
{ {

View File

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

View File

@ -9,7 +9,7 @@ import {
Validators, Validators,
} from "@angular/forms"; } from "@angular/forms";
import { ActivatedRoute } from "@angular/router"; import { ActivatedRoute } from "@angular/router";
import { concatMap, Subject, takeUntil } from "rxjs"; import { concatMap, Observable, Subject, takeUntil } from "rxjs";
import { ControlsOf } from "@bitwarden/angular/types/controls-of"; import { ControlsOf } from "@bitwarden/angular/types/controls-of";
import { ApiService } from "@bitwarden/common/abstractions/api.service"; import { ApiService } from "@bitwarden/common/abstractions/api.service";
@ -28,6 +28,7 @@ import { SsoConfigApi } from "@bitwarden/common/auth/models/api/sso-config.api";
import { OrganizationSsoRequest } from "@bitwarden/common/auth/models/request/organization-sso.request"; import { OrganizationSsoRequest } from "@bitwarden/common/auth/models/request/organization-sso.request";
import { OrganizationSsoResponse } from "@bitwarden/common/auth/models/response/organization-sso.response"; import { OrganizationSsoResponse } from "@bitwarden/common/auth/models/response/organization-sso.response";
import { SsoConfigView } from "@bitwarden/common/auth/models/view/sso-config.view"; import { SsoConfigView } from "@bitwarden/common/auth/models/view/sso-config.view";
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
@ -185,6 +186,8 @@ export class SsoComponent implements OnInit, OnDestroy {
return this.ssoConfigForm?.controls?.configType as FormControl; return this.ssoConfigForm?.controls?.configType as FormControl;
} }
accountDeprovisioningEnabled$: Observable<boolean>;
constructor( constructor(
private formBuilder: FormBuilder, private formBuilder: FormBuilder,
private route: ActivatedRoute, private route: ActivatedRoute,
@ -195,7 +198,11 @@ export class SsoComponent implements OnInit, OnDestroy {
private organizationApiService: OrganizationApiServiceAbstraction, private organizationApiService: OrganizationApiServiceAbstraction,
private configService: ConfigService, private configService: ConfigService,
private toastService: ToastService, private toastService: ToastService,
) {} ) {
this.accountDeprovisioningEnabled$ = this.configService.getFeatureFlag$(
FeatureFlag.AccountDeprovisioning,
);
}
async ngOnInit() { async ngOnInit() {
this.enabledCtrl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((enabled) => { this.enabledCtrl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe((enabled) => {