mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 13:23:34 +00:00
[PM-14613] Remove account deprovisioning feature flag (#14353)
This commit is contained in:
@@ -11,7 +11,7 @@ import {
|
|||||||
} from "@angular/core";
|
} from "@angular/core";
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { Router } from "@angular/router";
|
import { Router } from "@angular/router";
|
||||||
import { filter, firstValueFrom, map, Subject, takeUntil, timeout, withLatestFrom } from "rxjs";
|
import { filter, firstValueFrom, map, Subject, switchMap, takeUntil, timeout } from "rxjs";
|
||||||
|
|
||||||
import { CollectionService } from "@bitwarden/admin-console/common";
|
import { CollectionService } from "@bitwarden/admin-console/common";
|
||||||
import { DeviceTrustToastService } from "@bitwarden/angular/auth/services/device-trust-toast.service.abstraction";
|
import { DeviceTrustToastService } from "@bitwarden/angular/auth/services/device-trust-toast.service.abstraction";
|
||||||
@@ -29,7 +29,6 @@ import { UserVerificationService } from "@bitwarden/common/auth/abstractions/use
|
|||||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service";
|
import { ProcessReloadServiceAbstraction } from "@bitwarden/common/key-management/abstractions/process-reload.service";
|
||||||
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
import { KeyConnectorService } from "@bitwarden/common/key-management/key-connector/abstractions/key-connector.service";
|
||||||
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
import { MasterPasswordServiceAbstraction } from "@bitwarden/common/key-management/master-password/abstractions/master-password.service.abstraction";
|
||||||
@@ -820,27 +819,26 @@ export class AppComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async deleteAccount() {
|
private async deleteAccount() {
|
||||||
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
const userIsManaged = await firstValueFrom(
|
||||||
await firstValueFrom(
|
this.accountService.activeAccount$.pipe(
|
||||||
this.configService.getFeatureFlag$(FeatureFlag.AccountDeprovisioning).pipe(
|
getUserId,
|
||||||
withLatestFrom(this.organizationService.organizations$(userId)),
|
switchMap((userId) => this.organizationService.organizations$(userId)),
|
||||||
map(async ([accountDeprovisioningEnabled, organization]) => {
|
map((orgs) => orgs.some((o) => o.userIsManagedByOrganization === true)),
|
||||||
if (
|
|
||||||
accountDeprovisioningEnabled &&
|
|
||||||
organization.some((o) => o.userIsManagedByOrganization === true)
|
|
||||||
) {
|
|
||||||
await this.dialogService.openSimpleDialog({
|
|
||||||
title: { key: "cannotDeleteAccount" },
|
|
||||||
content: { key: "cannotDeleteAccountDesc" },
|
|
||||||
cancelButtonText: null,
|
|
||||||
acceptButtonText: { key: "close" },
|
|
||||||
type: "danger",
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
DeleteAccountComponent.open(this.dialogService);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (userIsManaged) {
|
||||||
|
await this.dialogService.openSimpleDialog({
|
||||||
|
title: { key: "cannotDeleteAccount" },
|
||||||
|
content: { key: "cannotDeleteAccountDesc" },
|
||||||
|
cancelButtonText: null,
|
||||||
|
acceptButtonText: { key: "close" },
|
||||||
|
type: "danger",
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteAccountComponent.open(this.dialogService);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,11 +50,15 @@ export abstract class BaseMembersComponent<UserView extends UserViewTypes> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get showBulkConfirmUsers(): boolean {
|
get showBulkConfirmUsers(): boolean {
|
||||||
return this.dataSource.acceptedUserCount > 0;
|
return this.dataSource
|
||||||
|
.getCheckedUsers()
|
||||||
|
.every((member) => member.status == this.userStatusType.Accepted);
|
||||||
}
|
}
|
||||||
|
|
||||||
get showBulkReinviteUsers(): boolean {
|
get showBulkReinviteUsers(): boolean {
|
||||||
return this.dataSource.invitedUserCount > 0;
|
return this.dataSource
|
||||||
|
.getCheckedUsers()
|
||||||
|
.every((member) => member.status == this.userStatusType.Invited);
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract userType: typeof OrganizationUserType | typeof ProviderUserType;
|
abstract userType: typeof OrganizationUserType | typeof ProviderUserType;
|
||||||
|
|||||||
@@ -115,7 +115,7 @@
|
|||||||
*ngIf="canAccessExport$ | async"
|
*ngIf="canAccessExport$ | async"
|
||||||
></bit-nav-item>
|
></bit-nav-item>
|
||||||
<bit-nav-item
|
<bit-nav-item
|
||||||
[text]="domainVerificationNavigationTextKey | i18n"
|
[text]="'claimedDomains' | i18n"
|
||||||
route="settings/domain-verification"
|
route="settings/domain-verification"
|
||||||
*ngIf="organization?.canManageDomainVerification"
|
*ngIf="organization?.canManageDomainVerification"
|
||||||
></bit-nav-item>
|
></bit-nav-item>
|
||||||
|
|||||||
@@ -55,7 +55,6 @@ 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>;
|
||||||
|
|
||||||
@@ -146,12 +145,6 @@ export class OrganizationLayoutComponent implements OnInit {
|
|||||||
|
|
||||||
this.integrationPageEnabled$ = this.organization$.pipe(map((org) => org.canAccessIntegrations));
|
this.integrationPageEnabled$ = this.organization$.pipe(map((org) => org.canAccessIntegrations));
|
||||||
|
|
||||||
this.domainVerificationNavigationTextKey = (await this.configService.getFeatureFlag(
|
|
||||||
FeatureFlag.AccountDeprovisioning,
|
|
||||||
))
|
|
||||||
? "claimedDomains"
|
|
||||||
: "domainVerification";
|
|
||||||
|
|
||||||
this.canShowPoliciesTab$ = this.organization$.pipe(
|
this.canShowPoliciesTab$ = this.organization$.pipe(
|
||||||
switchMap((organization) =>
|
switchMap((organization) =>
|
||||||
this.organizationBillingService
|
this.organizationBillingService
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Component, Inject } from "@angular/core";
|
import { Component, Inject } from "@angular/core";
|
||||||
import { firstValueFrom } from "rxjs";
|
|
||||||
|
|
||||||
import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
|
import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
|
||||||
import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums";
|
import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums";
|
||||||
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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { DIALOG_DATA, DialogConfig, DialogService } from "@bitwarden/components";
|
import { DIALOG_DATA, DialogConfig, DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
@@ -35,7 +32,6 @@ export class BulkDeleteDialogComponent {
|
|||||||
@Inject(DIALOG_DATA) protected dialogParams: BulkDeleteDialogParams,
|
@Inject(DIALOG_DATA) protected dialogParams: BulkDeleteDialogParams,
|
||||||
protected i18nService: I18nService,
|
protected i18nService: I18nService,
|
||||||
private organizationUserApiService: OrganizationUserApiService,
|
private organizationUserApiService: OrganizationUserApiService,
|
||||||
private configService: ConfigService,
|
|
||||||
private deleteManagedMemberWarningService: DeleteManagedMemberWarningService,
|
private deleteManagedMemberWarningService: DeleteManagedMemberWarningService,
|
||||||
) {
|
) {
|
||||||
this.organizationId = dialogParams.organizationId;
|
this.organizationId = dialogParams.organizationId;
|
||||||
@@ -43,11 +39,7 @@ export class BulkDeleteDialogComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async submit() {
|
async submit() {
|
||||||
if (
|
await this.deleteManagedMemberWarningService.acknowledgeWarning(this.organizationId);
|
||||||
await firstValueFrom(this.configService.getFeatureFlag$(FeatureFlag.AccountDeprovisioning))
|
|
||||||
) {
|
|
||||||
await this.deleteManagedMemberWarningService.acknowledgeWarning(this.organizationId);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.loading = true;
|
this.loading = true;
|
||||||
|
|||||||
@@ -1,12 +1,6 @@
|
|||||||
<bit-dialog
|
<bit-dialog dialogSize="large">
|
||||||
dialogSize="large"
|
|
||||||
*ngIf="{ enabled: accountDeprovisioningEnabled$ | async } as accountDeprovisioning"
|
|
||||||
>
|
|
||||||
<ng-container bitDialogTitle>
|
<ng-container bitDialogTitle>
|
||||||
<span *ngIf="accountDeprovisioning.enabled; else nonMemberTitle">{{ bulkMemberTitle }}</span>
|
<span>{{ bulkTitle }}</span>
|
||||||
<ng-template #nonMemberTitle>
|
|
||||||
{{ bulkTitle }}
|
|
||||||
</ng-template>
|
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
||||||
<div bitDialogContent>
|
<div bitDialogContent>
|
||||||
@@ -20,7 +14,7 @@
|
|||||||
|
|
||||||
<bit-callout
|
<bit-callout
|
||||||
type="danger"
|
type="danger"
|
||||||
*ngIf="nonCompliantMembers && accountDeprovisioning.enabled"
|
*ngIf="nonCompliantMembers"
|
||||||
title="{{ 'nonCompliantMembersTitle' | i18n }}"
|
title="{{ 'nonCompliantMembersTitle' | i18n }}"
|
||||||
>
|
>
|
||||||
{{ "nonCompliantMembersError" | i18n }}
|
{{ "nonCompliantMembersError" | i18n }}
|
||||||
@@ -50,7 +44,7 @@
|
|||||||
<bit-table>
|
<bit-table>
|
||||||
<ng-container header>
|
<ng-container header>
|
||||||
<tr>
|
<tr>
|
||||||
<th bitCell>{{ (accountDeprovisioning.enabled ? "member" : "user") | i18n }}</th>
|
<th bitCell>{{ "member" | i18n }}</th>
|
||||||
<th bitCell class="tw-w-1/2" *ngIf="this.showNoMasterPasswordWarning">
|
<th bitCell class="tw-w-1/2" *ngIf="this.showNoMasterPasswordWarning">
|
||||||
{{ "details" | i18n }}
|
{{ "details" | i18n }}
|
||||||
</th>
|
</th>
|
||||||
@@ -82,7 +76,7 @@
|
|||||||
<ng-container header>
|
<ng-container header>
|
||||||
<tr>
|
<tr>
|
||||||
<th bitCell class="tw-w-1/2">
|
<th bitCell class="tw-w-1/2">
|
||||||
{{ (accountDeprovisioning.enabled ? "member" : "user") | i18n }}
|
{{ "member" | i18n }}
|
||||||
</th>
|
</th>
|
||||||
<th bitCell class="tw-w-1/2">{{ "status" | i18n }}</th>
|
<th bitCell class="tw-w-1/2">{{ "status" | i18n }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -113,7 +107,7 @@
|
|||||||
[bitAction]="submit"
|
[bitAction]="submit"
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
>
|
>
|
||||||
{{ accountDeprovisioning.enabled ? bulkMemberTitle : bulkTitle }}
|
{{ bulkTitle }}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" bitButton buttonType="secondary" bitDialogClose>
|
<button type="button" bitButton buttonType="secondary" bitDialogClose>
|
||||||
{{ "close" | i18n }}
|
{{ "close" | i18n }}
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Component, Inject } from "@angular/core";
|
import { Component, Inject } from "@angular/core";
|
||||||
import { Observable } from "rxjs";
|
|
||||||
|
|
||||||
import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
|
import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
|
||||||
import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums";
|
import { OrganizationUserStatusType } from "@bitwarden/common/admin-console/enums";
|
||||||
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 { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { DIALOG_DATA, DialogService } from "@bitwarden/components";
|
import { DIALOG_DATA, DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
@@ -34,12 +31,10 @@ export class BulkRestoreRevokeComponent {
|
|||||||
error: string;
|
error: string;
|
||||||
showNoMasterPasswordWarning = false;
|
showNoMasterPasswordWarning = false;
|
||||||
nonCompliantMembers: boolean = false;
|
nonCompliantMembers: boolean = false;
|
||||||
accountDeprovisioningEnabled$: Observable<boolean>;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected i18nService: I18nService,
|
protected i18nService: I18nService,
|
||||||
private organizationUserApiService: OrganizationUserApiService,
|
private organizationUserApiService: OrganizationUserApiService,
|
||||||
private configService: ConfigService,
|
|
||||||
@Inject(DIALOG_DATA) protected data: BulkRestoreDialogParams,
|
@Inject(DIALOG_DATA) protected data: BulkRestoreDialogParams,
|
||||||
) {
|
) {
|
||||||
this.isRevoking = data.isRevoking;
|
this.isRevoking = data.isRevoking;
|
||||||
@@ -48,17 +43,9 @@ export class BulkRestoreRevokeComponent {
|
|||||||
this.showNoMasterPasswordWarning = this.users.some(
|
this.showNoMasterPasswordWarning = this.users.some(
|
||||||
(u) => u.status > OrganizationUserStatusType.Invited && u.hasMasterPassword === false,
|
(u) => u.status > OrganizationUserStatusType.Invited && u.hasMasterPassword === false,
|
||||||
);
|
);
|
||||||
this.accountDeprovisioningEnabled$ = this.configService.getFeatureFlag$(
|
|
||||||
FeatureFlag.AccountDeprovisioning,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get bulkTitle() {
|
get bulkTitle() {
|
||||||
const titleKey = this.isRevoking ? "revokeUsers" : "restoreUsers";
|
|
||||||
return this.i18nService.t(titleKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
get bulkMemberTitle() {
|
|
||||||
const titleKey = this.isRevoking ? "revokeMembers" : "restoreMembers";
|
const titleKey = this.isRevoking ? "revokeMembers" : "restoreMembers";
|
||||||
return this.i18nService.t(titleKey);
|
return this.i18nService.t(titleKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -275,11 +275,7 @@
|
|||||||
{{ "revoke" | i18n }}
|
{{ "revoke" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
*ngIf="
|
*ngIf="this.editMode && !(editParams$ | async)?.managedByOrganization"
|
||||||
this.editMode &&
|
|
||||||
(!(accountDeprovisioningEnabled$ | async) ||
|
|
||||||
!(editParams$ | async)?.managedByOrganization)
|
|
||||||
"
|
|
||||||
type="button"
|
type="button"
|
||||||
buttonType="danger"
|
buttonType="danger"
|
||||||
bitButton
|
bitButton
|
||||||
@@ -290,11 +286,7 @@
|
|||||||
{{ "remove" | i18n }}
|
{{ "remove" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
*ngIf="
|
*ngIf="this.editMode && (editParams$ | async)?.managedByOrganization"
|
||||||
this.editMode &&
|
|
||||||
(accountDeprovisioningEnabled$ | async) &&
|
|
||||||
(editParams$ | async)?.managedByOrganization
|
|
||||||
"
|
|
||||||
type="button"
|
type="button"
|
||||||
buttonType="danger"
|
buttonType="danger"
|
||||||
bitButton
|
bitButton
|
||||||
|
|||||||
@@ -152,10 +152,6 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
manageResetPassword: false,
|
manageResetPassword: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
protected accountDeprovisioningEnabled$: Observable<boolean> = this.configService.getFeatureFlag$(
|
|
||||||
FeatureFlag.AccountDeprovisioning,
|
|
||||||
);
|
|
||||||
|
|
||||||
protected isExternalIdVisible$ = this.configService
|
protected isExternalIdVisible$ = this.configService
|
||||||
.getFeatureFlag$(FeatureFlag.SsoExternalIdVisibility)
|
.getFeatureFlag$(FeatureFlag.SsoExternalIdVisibility)
|
||||||
.pipe(
|
.pipe(
|
||||||
@@ -667,11 +663,9 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
const showWarningDialog = combineLatest([
|
const showWarningDialog = combineLatest([
|
||||||
this.organization$,
|
this.organization$,
|
||||||
this.deleteManagedMemberWarningService.warningAcknowledged(this.params.organizationId),
|
this.deleteManagedMemberWarningService.warningAcknowledged(this.params.organizationId),
|
||||||
this.accountDeprovisioningEnabled$,
|
|
||||||
]).pipe(
|
]).pipe(
|
||||||
map(
|
map(
|
||||||
([organization, acknowledged, featureFlagEnabled]) =>
|
([organization, acknowledged]) =>
|
||||||
featureFlagEnabled &&
|
|
||||||
organization.canManageUsers &&
|
organization.canManageUsers &&
|
||||||
organization.productTierType === ProductTierType.Enterprise &&
|
organization.productTierType === ProductTierType.Enterprise &&
|
||||||
!acknowledged,
|
!acknowledged,
|
||||||
@@ -714,9 +708,8 @@ export class MemberDialogComponent implements OnDestroy {
|
|||||||
message: this.i18nService.t("organizationUserDeleted", this.params.name),
|
message: this.i18nService.t("organizationUserDeleted", this.params.name),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (await firstValueFrom(this.accountDeprovisioningEnabled$)) {
|
await this.deleteManagedMemberWarningService.acknowledgeWarning(this.params.organizationId);
|
||||||
await this.deleteManagedMemberWarningService.acknowledgeWarning(this.params.organizationId);
|
|
||||||
}
|
|
||||||
this.close(MemberDialogResult.Deleted);
|
this.close(MemberDialogResult.Deleted);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -345,7 +345,7 @@
|
|||||||
{{ "revokeAccess" | i18n }}
|
{{ "revokeAccess" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
*ngIf="!accountDeprovisioningEnabled || !u.managedByOrganization"
|
*ngIf="!u.managedByOrganization"
|
||||||
type="button"
|
type="button"
|
||||||
bitMenuItem
|
bitMenuItem
|
||||||
(click)="remove(u)"
|
(click)="remove(u)"
|
||||||
@@ -355,7 +355,7 @@
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
*ngIf="accountDeprovisioningEnabled && u.managedByOrganization"
|
*ngIf="u.managedByOrganization"
|
||||||
type="button"
|
type="button"
|
||||||
bitMenuItem
|
bitMenuItem
|
||||||
(click)="deleteUser(u)"
|
(click)="deleteUser(u)"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Component, OnInit, ViewChild, ViewContainerRef } from "@angular/core";
|
import { Component, ViewChild, ViewContainerRef } from "@angular/core";
|
||||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import {
|
import {
|
||||||
@@ -46,9 +46,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv
|
|||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
|
||||||
import { isNotSelfUpgradable, ProductTierType } from "@bitwarden/common/billing/enums";
|
import { isNotSelfUpgradable, ProductTierType } from "@bitwarden/common/billing/enums";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.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 { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
@@ -91,7 +89,7 @@ class MembersTableDataSource extends PeopleTableDataSource<OrganizationUserView>
|
|||||||
@Component({
|
@Component({
|
||||||
templateUrl: "members.component.html",
|
templateUrl: "members.component.html",
|
||||||
})
|
})
|
||||||
export class MembersComponent extends BaseMembersComponent<OrganizationUserView> implements OnInit {
|
export class MembersComponent extends BaseMembersComponent<OrganizationUserView> {
|
||||||
@ViewChild("resetPasswordTemplate", { read: ViewContainerRef, static: true })
|
@ViewChild("resetPasswordTemplate", { read: ViewContainerRef, static: true })
|
||||||
resetPasswordModalRef: ViewContainerRef;
|
resetPasswordModalRef: ViewContainerRef;
|
||||||
|
|
||||||
@@ -104,7 +102,6 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
status: OrganizationUserStatusType = null;
|
status: OrganizationUserStatusType = null;
|
||||||
orgResetPasswordPolicyEnabled = false;
|
orgResetPasswordPolicyEnabled = false;
|
||||||
orgIsOnSecretsManagerStandalone = false;
|
orgIsOnSecretsManagerStandalone = false;
|
||||||
accountDeprovisioningEnabled = false;
|
|
||||||
|
|
||||||
protected canUseSecretsManager$: Observable<boolean>;
|
protected canUseSecretsManager$: Observable<boolean>;
|
||||||
|
|
||||||
@@ -139,7 +136,6 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
private groupService: GroupApiService,
|
private groupService: GroupApiService,
|
||||||
private collectionService: CollectionService,
|
private collectionService: CollectionService,
|
||||||
private billingApiService: BillingApiServiceAbstraction,
|
private billingApiService: BillingApiServiceAbstraction,
|
||||||
private configService: ConfigService,
|
|
||||||
protected deleteManagedMemberWarningService: DeleteManagedMemberWarningService,
|
protected deleteManagedMemberWarningService: DeleteManagedMemberWarningService,
|
||||||
) {
|
) {
|
||||||
super(
|
super(
|
||||||
@@ -237,12 +233,6 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
.subscribe();
|
.subscribe();
|
||||||
}
|
}
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
this.accountDeprovisioningEnabled = await this.configService.getFeatureFlag(
|
|
||||||
FeatureFlag.AccountDeprovisioning,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async getUsers(): Promise<OrganizationUserView[]> {
|
async getUsers(): Promise<OrganizationUserView[]> {
|
||||||
let groupsPromise: Promise<Map<string, string>>;
|
let groupsPromise: Promise<Map<string, string>>;
|
||||||
let collectionsPromise: Promise<Map<string, string>>;
|
let collectionsPromise: Promise<Map<string, string>>;
|
||||||
@@ -591,20 +581,18 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
}
|
}
|
||||||
|
|
||||||
async bulkDelete() {
|
async bulkDelete() {
|
||||||
if (this.accountDeprovisioningEnabled) {
|
const warningAcknowledged = await firstValueFrom(
|
||||||
const warningAcknowledged = await firstValueFrom(
|
this.deleteManagedMemberWarningService.warningAcknowledged(this.organization.id),
|
||||||
this.deleteManagedMemberWarningService.warningAcknowledged(this.organization.id),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!warningAcknowledged &&
|
!warningAcknowledged &&
|
||||||
this.organization.canManageUsers &&
|
this.organization.canManageUsers &&
|
||||||
this.organization.productTierType === ProductTierType.Enterprise
|
this.organization.productTierType === ProductTierType.Enterprise
|
||||||
) {
|
) {
|
||||||
const acknowledged = await this.deleteManagedMemberWarningService.showWarning();
|
const acknowledged = await this.deleteManagedMemberWarningService.showWarning();
|
||||||
if (!acknowledged) {
|
if (!acknowledged) {
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -794,20 +782,18 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
}
|
}
|
||||||
|
|
||||||
async deleteUser(user: OrganizationUserView) {
|
async deleteUser(user: OrganizationUserView) {
|
||||||
if (this.accountDeprovisioningEnabled) {
|
const warningAcknowledged = await firstValueFrom(
|
||||||
const warningAcknowledged = await firstValueFrom(
|
this.deleteManagedMemberWarningService.warningAcknowledged(this.organization.id),
|
||||||
this.deleteManagedMemberWarningService.warningAcknowledged(this.organization.id),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
!warningAcknowledged &&
|
!warningAcknowledged &&
|
||||||
this.organization.canManageUsers &&
|
this.organization.canManageUsers &&
|
||||||
this.organization.productTierType === ProductTierType.Enterprise
|
this.organization.productTierType === ProductTierType.Enterprise
|
||||||
) {
|
) {
|
||||||
const acknowledged = await this.deleteManagedMemberWarningService.showWarning();
|
const acknowledged = await this.deleteManagedMemberWarningService.showWarning();
|
||||||
if (!acknowledged) {
|
if (!acknowledged) {
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -829,9 +815,7 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.accountDeprovisioningEnabled) {
|
await this.deleteManagedMemberWarningService.acknowledgeWarning(this.organization.id);
|
||||||
await this.deleteManagedMemberWarningService.acknowledgeWarning(this.organization.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.actionPromise = this.organizationUserApiService.deleteOrganizationUser(
|
this.actionPromise = this.organizationUserApiService.deleteOrganizationUser(
|
||||||
this.organization.id,
|
this.organization.id,
|
||||||
@@ -864,56 +848,23 @@ export class MembersComponent extends BaseMembersComponent<OrganizationUserView>
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
get showBulkConfirmUsers(): boolean {
|
|
||||||
if (!this.accountDeprovisioningEnabled) {
|
|
||||||
return super.showBulkConfirmUsers;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.dataSource
|
|
||||||
.getCheckedUsers()
|
|
||||||
.every((member) => member.status == this.userStatusType.Accepted);
|
|
||||||
}
|
|
||||||
|
|
||||||
get showBulkReinviteUsers(): boolean {
|
|
||||||
if (!this.accountDeprovisioningEnabled) {
|
|
||||||
return super.showBulkReinviteUsers;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.dataSource
|
|
||||||
.getCheckedUsers()
|
|
||||||
.every((member) => member.status == this.userStatusType.Invited);
|
|
||||||
}
|
|
||||||
|
|
||||||
get showBulkRestoreUsers(): boolean {
|
get showBulkRestoreUsers(): boolean {
|
||||||
return (
|
return this.dataSource
|
||||||
!this.accountDeprovisioningEnabled ||
|
.getCheckedUsers()
|
||||||
this.dataSource
|
.every((member) => member.status == this.userStatusType.Revoked);
|
||||||
.getCheckedUsers()
|
|
||||||
.every((member) => member.status == this.userStatusType.Revoked)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get showBulkRevokeUsers(): boolean {
|
get showBulkRevokeUsers(): boolean {
|
||||||
return (
|
return this.dataSource
|
||||||
!this.accountDeprovisioningEnabled ||
|
.getCheckedUsers()
|
||||||
this.dataSource
|
.every((member) => member.status != this.userStatusType.Revoked);
|
||||||
.getCheckedUsers()
|
|
||||||
.every((member) => member.status != this.userStatusType.Revoked)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get showBulkRemoveUsers(): boolean {
|
get showBulkRemoveUsers(): boolean {
|
||||||
return (
|
return this.dataSource.getCheckedUsers().every((member) => !member.managedByOrganization);
|
||||||
!this.accountDeprovisioningEnabled ||
|
|
||||||
this.dataSource.getCheckedUsers().every((member) => !member.managedByOrganization)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get showBulkDeleteUsers(): boolean {
|
get showBulkDeleteUsers(): boolean {
|
||||||
if (!this.accountDeprovisioningEnabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const validStatuses = [
|
const validStatuses = [
|
||||||
this.userStatusType.Accepted,
|
this.userStatusType.Accepted,
|
||||||
this.userStatusType.Confirmed,
|
this.userStatusType.Confirmed,
|
||||||
|
|||||||
@@ -1,11 +1,6 @@
|
|||||||
<bit-callout *ngIf="accountDeprovisioningEnabled$ | async; else disabledBlock" type="warning">
|
<bit-callout type="warning">
|
||||||
{{ "singleOrgPolicyMemberWarning" | i18n }}
|
{{ "singleOrgPolicyMemberWarning" | i18n }}
|
||||||
</bit-callout>
|
</bit-callout>
|
||||||
<ng-template #disabledBlock>
|
|
||||||
<bit-callout type="warning">
|
|
||||||
{{ "singleOrgPolicyWarning" | i18n }}
|
|
||||||
</bit-callout>
|
|
||||||
</ng-template>
|
|
||||||
|
|
||||||
<bit-form-control>
|
<bit-form-control>
|
||||||
<input type="checkbox" bitCheckbox [formControl]="enabled" id="enabled" />
|
<input type="checkbox" bitCheckbox [formControl]="enabled" id="enabled" />
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { firstValueFrom, Observable } from "rxjs";
|
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
|
||||||
|
|
||||||
import { BasePolicy, BasePolicyComponent } from "./base-policy.component";
|
import { BasePolicy, BasePolicyComponent } from "./base-policy.component";
|
||||||
|
|
||||||
export class SingleOrgPolicy extends BasePolicy {
|
export class SingleOrgPolicy extends BasePolicy {
|
||||||
name = "singleOrg";
|
name = "singleOrg";
|
||||||
description = "singleOrgDesc";
|
description = "singleOrgPolicyDesc";
|
||||||
type = PolicyType.SingleOrg;
|
type = PolicyType.SingleOrg;
|
||||||
component = SingleOrgPolicyComponent;
|
component = SingleOrgPolicyComponent;
|
||||||
}
|
}
|
||||||
@@ -19,22 +16,9 @@ export class SingleOrgPolicy extends BasePolicy {
|
|||||||
templateUrl: "single-org.component.html",
|
templateUrl: "single-org.component.html",
|
||||||
})
|
})
|
||||||
export class SingleOrgPolicyComponent extends BasePolicyComponent implements OnInit {
|
export class SingleOrgPolicyComponent extends BasePolicyComponent implements OnInit {
|
||||||
constructor(private configService: ConfigService) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected accountDeprovisioningEnabled$: Observable<boolean> = this.configService.getFeatureFlag$(
|
|
||||||
FeatureFlag.AccountDeprovisioning,
|
|
||||||
);
|
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
|
|
||||||
const isAccountDeprovisioningEnabled = await firstValueFrom(this.accountDeprovisioningEnabled$);
|
|
||||||
this.policy.description = isAccountDeprovisioningEnabled
|
|
||||||
? "singleOrgPolicyDesc"
|
|
||||||
: "singleOrgDesc";
|
|
||||||
|
|
||||||
if (!this.policyResponse.canToggleState) {
|
if (!this.policyResponse.canToggleState) {
|
||||||
this.enabled.disable();
|
this.enabled.disable();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,10 @@
|
|||||||
import { Component, OnInit, OnDestroy } from "@angular/core";
|
import { Component, OnInit, OnDestroy } from "@angular/core";
|
||||||
import {
|
import { firstValueFrom, from, lastValueFrom, map, Observable, Subject, takeUntil } from "rxjs";
|
||||||
combineLatest,
|
|
||||||
firstValueFrom,
|
|
||||||
from,
|
|
||||||
lastValueFrom,
|
|
||||||
map,
|
|
||||||
Observable,
|
|
||||||
Subject,
|
|
||||||
takeUntil,
|
|
||||||
} from "rxjs";
|
|
||||||
|
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
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 { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { DialogService } from "@bitwarden/components";
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
@@ -47,10 +37,6 @@ export class AccountComponent implements OnInit, OnDestroy {
|
|||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
||||||
|
|
||||||
const isAccountDeprovisioningEnabled$ = this.configService.getFeatureFlag$(
|
|
||||||
FeatureFlag.AccountDeprovisioning,
|
|
||||||
);
|
|
||||||
|
|
||||||
const userIsManagedByOrganization$ = this.organizationService
|
const userIsManagedByOrganization$ = this.organizationService
|
||||||
.organizations$(userId)
|
.organizations$(userId)
|
||||||
.pipe(
|
.pipe(
|
||||||
@@ -61,25 +47,14 @@ export class AccountComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
this.showChangeEmail$ = hasMasterPassword$;
|
this.showChangeEmail$ = hasMasterPassword$;
|
||||||
|
|
||||||
this.showPurgeVault$ = combineLatest([
|
this.showPurgeVault$ = userIsManagedByOrganization$.pipe(
|
||||||
isAccountDeprovisioningEnabled$,
|
map((userIsManagedByOrganization) => !userIsManagedByOrganization),
|
||||||
userIsManagedByOrganization$,
|
|
||||||
]).pipe(
|
|
||||||
map(
|
|
||||||
([isAccountDeprovisioningEnabled, userIsManagedByOrganization]) =>
|
|
||||||
!isAccountDeprovisioningEnabled || !userIsManagedByOrganization,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.showDeleteAccount$ = combineLatest([
|
this.showDeleteAccount$ = userIsManagedByOrganization$.pipe(
|
||||||
isAccountDeprovisioningEnabled$,
|
map((userIsManagedByOrganization) => !userIsManagedByOrganization),
|
||||||
userIsManagedByOrganization$,
|
|
||||||
]).pipe(
|
|
||||||
map(
|
|
||||||
([isAccountDeprovisioningEnabled, userIsManagedByOrganization]) =>
|
|
||||||
!isAccountDeprovisioningEnabled || !userIsManagedByOrganization,
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.accountService.accountVerifyNewDeviceLogin$
|
this.accountService.accountVerifyNewDeviceLogin$
|
||||||
.pipe(takeUntil(this.destroy$))
|
.pipe(takeUntil(this.destroy$))
|
||||||
.subscribe((verifyDevices) => {
|
.subscribe((verifyDevices) => {
|
||||||
|
|||||||
@@ -1,12 +1,9 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { CommonModule } from "@angular/common";
|
import { CommonModule } from "@angular/common";
|
||||||
import { Component, OnInit } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { Observable } from "rxjs";
|
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
|
||||||
import { TypographyModule } from "@bitwarden/components";
|
import { TypographyModule } from "@bitwarden/components";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,13 +15,4 @@ import { TypographyModule } from "@bitwarden/components";
|
|||||||
standalone: true,
|
standalone: true,
|
||||||
imports: [TypographyModule, JslibModule, CommonModule],
|
imports: [TypographyModule, JslibModule, CommonModule],
|
||||||
})
|
})
|
||||||
export class DangerZoneComponent implements OnInit {
|
export class DangerZoneComponent {}
|
||||||
constructor(private configService: ConfigService) {}
|
|
||||||
accountDeprovisioningEnabled$: Observable<boolean>;
|
|
||||||
|
|
||||||
ngOnInit(): void {
|
|
||||||
this.accountDeprovisioningEnabled$ = this.configService.getFeatureFlag$(
|
|
||||||
FeatureFlag.AccountDeprovisioning,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
import { Component, OnDestroy, OnInit } from "@angular/core";
|
||||||
import { FormControl, FormGroup } from "@angular/forms";
|
import { FormControl, FormGroup } from "@angular/forms";
|
||||||
import { firstValueFrom, map, Observable, of, Subject, switchMap, takeUntil } from "rxjs";
|
import { firstValueFrom, map, Observable, Subject, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
|
||||||
@@ -10,9 +10,7 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
|
|||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { UpdateProfileRequest } from "@bitwarden/common/auth/models/request/update-profile.request";
|
import { UpdateProfileRequest } from "@bitwarden/common/auth/models/request/update-profile.request";
|
||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
|
||||||
import { ProfileResponse } from "@bitwarden/common/models/response/profile.response";
|
import { ProfileResponse } from "@bitwarden/common/models/response/profile.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";
|
||||||
import { DialogService, ToastService } from "@bitwarden/components";
|
import { DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
@@ -40,7 +38,6 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private toastService: ToastService,
|
private toastService: ToastService,
|
||||||
private configService: ConfigService,
|
|
||||||
private organizationService: OrganizationService,
|
private organizationService: OrganizationService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@@ -53,21 +50,12 @@ export class ProfileComponent implements OnInit, OnDestroy {
|
|||||||
|
|
||||||
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
||||||
|
|
||||||
this.managingOrganization$ = this.configService
|
this.managingOrganization$ = this.organizationService
|
||||||
.getFeatureFlag$(FeatureFlag.AccountDeprovisioning)
|
.organizations$(userId)
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap((isAccountDeprovisioningEnabled) =>
|
map((organizations) => organizations.find((o) => o.userIsManagedByOrganization === true)),
|
||||||
isAccountDeprovisioningEnabled
|
|
||||||
? this.organizationService
|
|
||||||
.organizations$(userId)
|
|
||||||
.pipe(
|
|
||||||
map((organizations) =>
|
|
||||||
organizations.find((o) => o.userIsManagedByOrganization === true),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: of(null),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
this.formGroup.get("name").setValue(this.profile.name);
|
this.formGroup.get("name").setValue(this.profile.name);
|
||||||
this.formGroup.get("email").setValue(this.profile.email);
|
this.formGroup.get("email").setValue(this.profile.email);
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
|||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
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()
|
||||||
@@ -21,8 +19,7 @@ export class EventService {
|
|||||||
constructor(
|
constructor(
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
policyService: PolicyService,
|
policyService: PolicyService,
|
||||||
private configService: ConfigService,
|
accountService: AccountService,
|
||||||
private accountService: AccountService,
|
|
||||||
) {
|
) {
|
||||||
accountService.activeAccount$
|
accountService.activeAccount$
|
||||||
.pipe(
|
.pipe(
|
||||||
@@ -463,20 +460,10 @@ 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(
|
msg = humanReadableMsg = this.i18nService.t("domainClaimedEvent", ev.domainName);
|
||||||
(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(
|
msg = humanReadableMsg = this.i18nService.t("domainNotClaimedEvent", ev.domainName);
|
||||||
(await this.configService.getFeatureFlag(FeatureFlag.AccountDeprovisioning))
|
|
||||||
? "domainNotClaimedEvent"
|
|
||||||
: "domainNotVerifiedEvent",
|
|
||||||
ev.domainName,
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
// Secrets Manager
|
// Secrets Manager
|
||||||
case EventType.Secret_Retrieved:
|
case EventType.Secret_Retrieved:
|
||||||
|
|||||||
@@ -1,14 +1,5 @@
|
|||||||
import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
|
import { Component, Inject, OnDestroy, OnInit } from "@angular/core";
|
||||||
import {
|
import { combineLatest, map, Observable, Subject, switchMap, takeUntil } from "rxjs";
|
||||||
combineLatest,
|
|
||||||
firstValueFrom,
|
|
||||||
map,
|
|
||||||
Observable,
|
|
||||||
of,
|
|
||||||
Subject,
|
|
||||||
switchMap,
|
|
||||||
takeUntil,
|
|
||||||
} from "rxjs";
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
OrganizationUserApiService,
|
OrganizationUserApiService,
|
||||||
@@ -25,7 +16,6 @@ import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
|||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
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 { 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 { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
@@ -83,22 +73,11 @@ export class OrganizationOptionsComponent implements OnInit, OnDestroy {
|
|||||||
map((policies) => policies.filter((p) => p.type == PolicyType.ResetPassword)),
|
map((policies) => policies.filter((p) => p.type == PolicyType.ResetPassword)),
|
||||||
);
|
);
|
||||||
|
|
||||||
const userId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
const managingOrg$ = this.accountService.activeAccount$.pipe(
|
||||||
const managingOrg$ = this.configService
|
getUserId,
|
||||||
.getFeatureFlag$(FeatureFlag.AccountDeprovisioning)
|
switchMap((userId) => this.organizationService.organizations$(userId)),
|
||||||
.pipe(
|
map((organizations) => organizations.find((o) => o.userIsManagedByOrganization === true)),
|
||||||
switchMap((isAccountDeprovisioningEnabled) =>
|
);
|
||||||
isAccountDeprovisioningEnabled
|
|
||||||
? this.organizationService
|
|
||||||
.organizations$(userId)
|
|
||||||
.pipe(
|
|
||||||
map((organizations) =>
|
|
||||||
organizations.find((o) => o.userIsManagedByOrganization === true),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: of(null),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
combineLatest([
|
combineLatest([
|
||||||
this.organization$,
|
this.organization$,
|
||||||
|
|||||||
@@ -5055,9 +5055,6 @@
|
|||||||
"singleOrgBlockCreateMessage": {
|
"singleOrgBlockCreateMessage": {
|
||||||
"message": "Your current organization has a policy that does not allow you to join more than one organization. Please contact your organization admins or sign up from a different Bitwarden account."
|
"message": "Your current organization has a policy that does not allow you to join more than one organization. Please contact your organization admins or sign up from a different Bitwarden account."
|
||||||
},
|
},
|
||||||
"singleOrgPolicyWarning": {
|
|
||||||
"message": "Organization members who are not owners or admins and are already a member of another organization will be removed from your organization."
|
|
||||||
},
|
|
||||||
"singleOrgPolicyMemberWarning": {
|
"singleOrgPolicyMemberWarning": {
|
||||||
"message": "Non-compliant members will be placed in revoked status until they leave all other organizations. Administrators are exempt and can restore members once compliance is met."
|
"message": "Non-compliant members will be placed in revoked status until they leave all other organizations. Administrators are exempt and can restore members once compliance is met."
|
||||||
},
|
},
|
||||||
@@ -5887,15 +5884,6 @@
|
|||||||
"fingerprintPhrase": {
|
"fingerprintPhrase": {
|
||||||
"message": "Fingerprint phrase:"
|
"message": "Fingerprint phrase:"
|
||||||
},
|
},
|
||||||
"removeUsers": {
|
|
||||||
"message": "Remove users"
|
|
||||||
},
|
|
||||||
"revokeUsers": {
|
|
||||||
"message": "Revoke users"
|
|
||||||
},
|
|
||||||
"restoreUsers": {
|
|
||||||
"message": "Restore users"
|
|
||||||
},
|
|
||||||
"error": {
|
"error": {
|
||||||
"message": "Error"
|
"message": "Error"
|
||||||
},
|
},
|
||||||
@@ -7837,12 +7825,6 @@
|
|||||||
"noDomainsSubText": {
|
"noDomainsSubText": {
|
||||||
"message": "Connecting a domain allows members to skip the SSO identifier field during Login with SSO."
|
"message": "Connecting a domain allows members to skip the SSO identifier field during Login with SSO."
|
||||||
},
|
},
|
||||||
"verifyDomain": {
|
|
||||||
"message": "Verify domain"
|
|
||||||
},
|
|
||||||
"reverifyDomain": {
|
|
||||||
"message": "Reverify domain"
|
|
||||||
},
|
|
||||||
"copyDnsTxtRecord": {
|
"copyDnsTxtRecord": {
|
||||||
"message": "Copy DNS TXT record"
|
"message": "Copy DNS TXT record"
|
||||||
},
|
},
|
||||||
@@ -7852,18 +7834,6 @@
|
|||||||
"dnsTxtRecordInputHint": {
|
"dnsTxtRecordInputHint": {
|
||||||
"message": "Copy and paste the TXT record into your DNS Provider."
|
"message": "Copy and paste the TXT record into your DNS Provider."
|
||||||
},
|
},
|
||||||
"domainNameInputHint": {
|
|
||||||
"message": "Example: mydomain.com. Subdomains require separate entries to be verified."
|
|
||||||
},
|
|
||||||
"automaticDomainVerification": {
|
|
||||||
"message": "Automatic Domain Verification"
|
|
||||||
},
|
|
||||||
"automaticDomainVerificationProcess": {
|
|
||||||
"message": "Bitwarden will attempt to verify the domain 3 times during the first 72 hours. If the domain can’t be verified, check the DNS record in your host and manually verify. The domain will be removed from your organization in 7 days if it is not verified"
|
|
||||||
},
|
|
||||||
"invalidDomainNameMessage": {
|
|
||||||
"message": "Input is not a valid format. Format: mydomain.com. Subdomains require separate entries to be verified."
|
|
||||||
},
|
|
||||||
"removeDomain": {
|
"removeDomain": {
|
||||||
"message": "Remove domain"
|
"message": "Remove domain"
|
||||||
},
|
},
|
||||||
@@ -7876,9 +7846,6 @@
|
|||||||
"domainSaved": {
|
"domainSaved": {
|
||||||
"message": "Domain saved"
|
"message": "Domain saved"
|
||||||
},
|
},
|
||||||
"domainVerified": {
|
|
||||||
"message": "Domain verified"
|
|
||||||
},
|
|
||||||
"duplicateDomainError": {
|
"duplicateDomainError": {
|
||||||
"message": "You can't claim the same domain twice."
|
"message": "You can't claim the same domain twice."
|
||||||
},
|
},
|
||||||
@@ -7891,21 +7858,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domainNotVerified": {
|
|
||||||
"message": "$DOMAIN$ not verified. Check your DNS record.",
|
|
||||||
"placeholders": {
|
|
||||||
"DOMAIN": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "bitwarden.com"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"domainStatusVerified": {
|
|
||||||
"message": "Verified"
|
|
||||||
},
|
|
||||||
"domainStatusUnverified": {
|
|
||||||
"message": "Unverified"
|
|
||||||
},
|
|
||||||
"domainNameTh": {
|
"domainNameTh": {
|
||||||
"message": "Name"
|
"message": "Name"
|
||||||
},
|
},
|
||||||
@@ -7939,24 +7891,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domainVerifiedEvent": {
|
|
||||||
"message": "$DOMAIN$ verified",
|
|
||||||
"placeholders": {
|
|
||||||
"DOMAIN": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "bitwarden.com"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"domainNotVerifiedEvent": {
|
|
||||||
"message": "$DOMAIN$ not verified",
|
|
||||||
"placeholders": {
|
|
||||||
"DOMAIN": {
|
|
||||||
"content": "$1",
|
|
||||||
"example": "bitwarden.com"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"verificationRequiredForActionSetPinToContinue": {
|
"verificationRequiredForActionSetPinToContinue": {
|
||||||
"message": "Verification required for this action. Set a PIN to continue."
|
"message": "Verification required for this action. Set a PIN to continue."
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<span bitDialogTitle>
|
<span bitDialogTitle>
|
||||||
<span *ngIf="!data.orgDomain">{{ "newDomain" | i18n }}</span>
|
<span *ngIf="!data.orgDomain">{{ "newDomain" | i18n }}</span>
|
||||||
<span *ngIf="data.orgDomain">
|
<span *ngIf="data.orgDomain">
|
||||||
{{ ((accountDeprovisioningEnabled$ | async) ? "claimDomain" : "verifyDomain") | i18n }}
|
{{ "claimDomain" | i18n }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span *ngIf="data.orgDomain" class="tw-text-xs tw-text-muted">
|
<span *ngIf="data.orgDomain" class="tw-text-xs tw-text-muted">
|
||||||
@@ -15,30 +15,17 @@
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
<span *ngIf="data?.orgDomain && !data.orgDomain?.verifiedDate" bitBadge variant="warning">
|
<span *ngIf="data?.orgDomain && !data.orgDomain?.verifiedDate" bitBadge variant="warning">
|
||||||
{{
|
{{ "domainStatusUnderVerification" | 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">
|
||||||
{{
|
{{ "domainStatusClaimed" | 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>{{
|
<bit-hint>{{ "claimDomainNameInputHint" | i18n }}</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">
|
||||||
@@ -57,29 +44,18 @@
|
|||||||
<bit-callout
|
<bit-callout
|
||||||
*ngIf="data?.orgDomain && !data?.orgDomain?.verifiedDate"
|
*ngIf="data?.orgDomain && !data?.orgDomain?.verifiedDate"
|
||||||
type="info"
|
type="info"
|
||||||
title="{{
|
title="{{ 'automaticClaimedDomains' | i18n | uppercase }}"
|
||||||
(accountDeprovisioningEnabled$ | async)
|
|
||||||
? ('automaticClaimedDomains' | i18n | uppercase)
|
|
||||||
: ('automaticDomainVerification' | i18n)
|
|
||||||
}}"
|
|
||||||
>
|
>
|
||||||
{{
|
{{ "automaticDomainClaimProcess" | 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">{{
|
||||||
((accountDeprovisioningEnabled$ | async) ? "claimDomain" : "verifyDomain") | i18n
|
"claimDomain" | i18n
|
||||||
}}</span>
|
|
||||||
<span *ngIf="data?.orgDomain?.verifiedDate">{{
|
|
||||||
((accountDeprovisioningEnabled$ | async) ? "reclaimDomain" : "reverifyDomain") | i18n
|
|
||||||
}}</span>
|
}}</span>
|
||||||
|
<span *ngIf="data?.orgDomain?.verifiedDate">{{ "reclaimDomain" | 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 }}
|
||||||
|
|||||||
@@ -2,19 +2,15 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
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, 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 { 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 { CryptoFunctionService as CryptoFunctionServiceAbstraction } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service";
|
|
||||||
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 { 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 { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
import { DialogRef, DIALOG_DATA, DialogService, ToastService } from "@bitwarden/components";
|
import { DialogRef, DIALOG_DATA, DialogService, ToastService } from "@bitwarden/components";
|
||||||
|
|
||||||
@@ -32,7 +28,6 @@ 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();
|
||||||
|
|
||||||
accountDeprovisioningEnabled$: Observable<boolean>;
|
|
||||||
domainForm: FormGroup;
|
domainForm: FormGroup;
|
||||||
|
|
||||||
get domainNameCtrl(): FormControl {
|
get domainNameCtrl(): FormControl {
|
||||||
@@ -50,20 +45,13 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
|
|||||||
public dialogRef: DialogRef,
|
public dialogRef: DialogRef,
|
||||||
@Inject(DIALOG_DATA) public data: DomainAddEditDialogData,
|
@Inject(DIALOG_DATA) public data: DomainAddEditDialogData,
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private cryptoFunctionService: CryptoFunctionServiceAbstraction,
|
|
||||||
private platformUtilsService: PlatformUtilsService,
|
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private orgDomainApiService: OrgDomainApiServiceAbstraction,
|
private orgDomainApiService: OrgDomainApiServiceAbstraction,
|
||||||
private orgDomainService: OrgDomainServiceAbstraction,
|
private orgDomainService: OrgDomainServiceAbstraction,
|
||||||
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
|
||||||
|
|
||||||
@@ -73,11 +61,7 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
|
|||||||
"",
|
"",
|
||||||
[
|
[
|
||||||
Validators.required,
|
Validators.required,
|
||||||
domainNameValidator(
|
domainNameValidator(this.i18nService.t("invalidDomainNameClaimMessage")),
|
||||||
(await firstValueFrom(this.accountDeprovisioningEnabled$))
|
|
||||||
? this.i18nService.t("invalidDomainNameClaimMessage")
|
|
||||||
: this.i18nService.t("invalidDomainNameMessage"),
|
|
||||||
),
|
|
||||||
uniqueInArrayValidator(
|
uniqueInArrayValidator(
|
||||||
this.data.existingDomainNames,
|
this.data.existingDomainNames,
|
||||||
this.i18nService.t("duplicateDomainError"),
|
this.i18nService.t("duplicateDomainError"),
|
||||||
@@ -223,22 +207,13 @@ export class DomainAddEditDialogComponent implements OnInit, OnDestroy {
|
|||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
title: null,
|
title: null,
|
||||||
message: this.i18nService.t(
|
message: this.i18nService.t("domainClaimed"),
|
||||||
(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(
|
message: this.i18nService.t("domainNotClaimed", this.domainNameCtrl.value),
|
||||||
(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.
|
||||||
|
|||||||
@@ -4,11 +4,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</app-header>
|
</app-header>
|
||||||
|
|
||||||
<p
|
<p bitTypography="body1" class="tw-text-main tw-w-2/5">
|
||||||
bitTypography="body1"
|
|
||||||
class="tw-text-main tw-w-2/5"
|
|
||||||
*ngIf="accountDeprovisioningEnabled$ | async"
|
|
||||||
>
|
|
||||||
{{ "claimedDomainsDesc" | i18n }}
|
{{ "claimedDomainsDesc" | i18n }}
|
||||||
<a
|
<a
|
||||||
bitLink
|
bitLink
|
||||||
@@ -58,16 +54,10 @@
|
|||||||
</td>
|
</td>
|
||||||
<td bitCell>
|
<td bitCell>
|
||||||
<span *ngIf="!orgDomain?.verifiedDate" bitBadge variant="warning">{{
|
<span *ngIf="!orgDomain?.verifiedDate" bitBadge variant="warning">{{
|
||||||
((accountDeprovisioningEnabled$ | async)
|
"domainStatusUnderVerification" | i18n
|
||||||
? "domainStatusUnderVerification"
|
|
||||||
: "domainStatusUnverified"
|
|
||||||
) | i18n
|
|
||||||
}}</span>
|
}}</span>
|
||||||
<span *ngIf="orgDomain?.verifiedDate" bitBadge variant="success">{{
|
<span *ngIf="orgDomain?.verifiedDate" bitBadge variant="success">{{
|
||||||
((accountDeprovisioningEnabled$ | async)
|
"domainStatusClaimed" | i18n
|
||||||
? "domainStatusClaimed"
|
|
||||||
: "domainStatusVerified"
|
|
||||||
) | i18n
|
|
||||||
}}</span>
|
}}</span>
|
||||||
</td>
|
</td>
|
||||||
<td bitCell class="tw-text-muted">
|
<td bitCell class="tw-text-muted">
|
||||||
@@ -94,10 +84,7 @@
|
|||||||
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>
|
||||||
{{
|
{{ "claimDomain" | 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">
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import {
|
|||||||
switchMap,
|
switchMap,
|
||||||
take,
|
take,
|
||||||
takeUntil,
|
takeUntil,
|
||||||
withLatestFrom,
|
|
||||||
} from "rxjs";
|
} 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";
|
||||||
@@ -22,7 +21,6 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
|||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
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 { 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";
|
||||||
@@ -46,7 +44,6 @@ 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,
|
||||||
@@ -59,11 +56,7 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
|
|||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
private policyService: PolicyService,
|
private policyService: PolicyService,
|
||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
) {
|
) {}
|
||||||
this.accountDeprovisioningEnabled$ = this.configService.getFeatureFlag$(
|
|
||||||
FeatureFlag.AccountDeprovisioning,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.orgDomains$ = this.orgDomainService.orgDomains$;
|
this.orgDomains$ = this.orgDomainService.orgDomains$;
|
||||||
@@ -85,20 +78,18 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
|
|||||||
async load() {
|
async load() {
|
||||||
await this.orgDomainApiService.getAllByOrgId(this.organizationId);
|
await this.orgDomainApiService.getAllByOrgId(this.organizationId);
|
||||||
|
|
||||||
if (await this.configService.getFeatureFlag(FeatureFlag.AccountDeprovisioning)) {
|
const singleOrgPolicy = await firstValueFrom(
|
||||||
const singleOrgPolicy = await firstValueFrom(
|
this.accountService.activeAccount$.pipe(
|
||||||
this.accountService.activeAccount$.pipe(
|
getUserId,
|
||||||
getUserId,
|
switchMap((userId) => this.policyService.policies$(userId)),
|
||||||
switchMap((userId) => this.policyService.policies$(userId)),
|
map((policies) =>
|
||||||
map((policies) =>
|
policies.find(
|
||||||
policies.find(
|
(p) => p.type === PolicyType.SingleOrg && p.organizationId === this.organizationId,
|
||||||
(p) => p.type === PolicyType.SingleOrg && p.organizationId === this.organizationId,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
this.singleOrgPolicyEnabled = singleOrgPolicy?.enabled ?? false;
|
);
|
||||||
}
|
this.singleOrgPolicyEnabled = singleOrgPolicy?.enabled ?? false;
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
}
|
}
|
||||||
@@ -110,29 +101,30 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
|
|||||||
existingDomainNames: this.getExistingDomainNames(),
|
existingDomainNames: this.getExistingDomainNames(),
|
||||||
};
|
};
|
||||||
|
|
||||||
await firstValueFrom(
|
const showSingleOrgWarning = await firstValueFrom(
|
||||||
this.configService.getFeatureFlag$(FeatureFlag.AccountDeprovisioning).pipe(
|
this.orgDomains$.pipe(
|
||||||
withLatestFrom(this.orgDomains$),
|
map(
|
||||||
map(async ([accountDeprovisioningEnabled, organizationDomains]) => {
|
(organizationDomains) =>
|
||||||
if (
|
|
||||||
accountDeprovisioningEnabled &&
|
|
||||||
!this.singleOrgPolicyEnabled &&
|
!this.singleOrgPolicyEnabled &&
|
||||||
organizationDomains.every((domain) => domain.verifiedDate === null)
|
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);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
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) {
|
private async openAddDomainDialog(domainAddEditDialogData: DomainAddEditDialogData) {
|
||||||
@@ -184,22 +176,13 @@ export class DomainVerificationComponent implements OnInit, OnDestroy {
|
|||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
title: null,
|
title: null,
|
||||||
message: this.i18nService.t(
|
message: this.i18nService.t("domainClaimed"),
|
||||||
(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(
|
message: this.i18nService.t("domainNotClaimed", domainName),
|
||||||
(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);
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import { inject, NgModule } from "@angular/core";
|
import { 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";
|
||||||
@@ -30,12 +28,7 @@ const routes: Routes = [
|
|||||||
component: DomainVerificationComponent,
|
component: DomainVerificationComponent,
|
||||||
canActivate: [organizationPermissionsGuard((org) => org.canManageDomainVerification)],
|
canActivate: [organizationPermissionsGuard((org) => org.canManageDomainVerification)],
|
||||||
resolve: {
|
resolve: {
|
||||||
titleId: async () => {
|
titleId: "claimedDomains",
|
||||||
const configService = inject(ConfigService);
|
|
||||||
return (await configService.getFeatureFlag(FeatureFlag.AccountDeprovisioning))
|
|
||||||
? "claimedDomains"
|
|
||||||
: "domainVerification";
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -31,10 +31,7 @@
|
|||||||
<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">{{
|
<a bitLink routerLink="../domain-verification">{{ "claimedDomains" | i18n }}</a>
|
||||||
((accountDeprovisioningEnabled$ | async) ? "claimedDomains" : "domainVerification")
|
|
||||||
| i18n
|
|
||||||
}}</a>
|
|
||||||
</bit-hint>
|
</bit-hint>
|
||||||
</bit-form-field>
|
</bit-form-field>
|
||||||
|
|
||||||
|
|||||||
@@ -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, firstValueFrom, Observable, Subject, takeUntil } from "rxjs";
|
import { concatMap, firstValueFrom, 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";
|
||||||
@@ -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 { 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 { getUserId } from "@bitwarden/common/auth/services/account.service";
|
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 { 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 { Utils } from "@bitwarden/common/platform/misc/utils";
|
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;
|
return this.ssoConfigForm?.controls?.configType as FormControl;
|
||||||
}
|
}
|
||||||
|
|
||||||
accountDeprovisioningEnabled$: Observable<boolean>;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private formBuilder: FormBuilder,
|
private formBuilder: FormBuilder,
|
||||||
private route: ActivatedRoute,
|
private route: ActivatedRoute,
|
||||||
@@ -202,13 +198,8 @@ export class SsoComponent implements OnInit, OnDestroy {
|
|||||||
private organizationService: OrganizationService,
|
private organizationService: OrganizationService,
|
||||||
private accountService: AccountService,
|
private accountService: AccountService,
|
||||||
private organizationApiService: OrganizationApiServiceAbstraction,
|
private organizationApiService: OrganizationApiServiceAbstraction,
|
||||||
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) => {
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { ServerConfig } from "../platform/abstractions/config/server-config";
|
|||||||
*/
|
*/
|
||||||
export enum FeatureFlag {
|
export enum FeatureFlag {
|
||||||
/* Admin Console Team */
|
/* Admin Console Team */
|
||||||
AccountDeprovisioning = "pm-10308-account-deprovisioning",
|
|
||||||
VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint",
|
VerifiedSsoDomainEndpoint = "pm-12337-refactor-sso-details-endpoint",
|
||||||
LimitItemDeletion = "pm-15493-restrict-item-deletion-to-can-manage-permission",
|
LimitItemDeletion = "pm-15493-restrict-item-deletion-to-can-manage-permission",
|
||||||
SsoExternalIdVisibility = "pm-18630-sso-external-id-visibility",
|
SsoExternalIdVisibility = "pm-18630-sso-external-id-visibility",
|
||||||
@@ -80,7 +79,6 @@ const FALSE = false as boolean;
|
|||||||
*/
|
*/
|
||||||
export const DefaultFeatureFlagValue = {
|
export const DefaultFeatureFlagValue = {
|
||||||
/* Admin Console Team */
|
/* Admin Console Team */
|
||||||
[FeatureFlag.AccountDeprovisioning]: FALSE,
|
|
||||||
[FeatureFlag.VerifiedSsoDomainEndpoint]: FALSE,
|
[FeatureFlag.VerifiedSsoDomainEndpoint]: FALSE,
|
||||||
[FeatureFlag.LimitItemDeletion]: FALSE,
|
[FeatureFlag.LimitItemDeletion]: FALSE,
|
||||||
[FeatureFlag.SsoExternalIdVisibility]: FALSE,
|
[FeatureFlag.SsoExternalIdVisibility]: FALSE,
|
||||||
|
|||||||
Reference in New Issue
Block a user