mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 13:23:34 +00:00
[PM-22107] Update Remove Individual Vault policy dialog (#15323)
* WIP * switch to signal * fix ts strict errors * clean up * refactor policy list service * implement vnext component * refactor to include feature flag check in display() * CR feedback * refactor submit to cancel before request is built * clean up * Fix typo --------- Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com>
This commit is contained in:
@@ -1,12 +1,12 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { Directive, Input, OnInit } from "@angular/core";
|
import { Directive, Input, OnInit } from "@angular/core";
|
||||||
import { UntypedFormControl, UntypedFormGroup } from "@angular/forms";
|
import { UntypedFormControl, UntypedFormGroup } from "@angular/forms";
|
||||||
|
import { Observable, of } from "rxjs";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
import { PolicyRequest } from "@bitwarden/common/admin-console/models/request/policy.request";
|
import { PolicyRequest } from "@bitwarden/common/admin-console/models/request/policy.request";
|
||||||
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
|
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
|
|
||||||
export abstract class BasePolicy {
|
export abstract class BasePolicy {
|
||||||
abstract name: string;
|
abstract name: string;
|
||||||
@@ -14,38 +14,56 @@ export abstract class BasePolicy {
|
|||||||
abstract type: PolicyType;
|
abstract type: PolicyType;
|
||||||
abstract component: any;
|
abstract component: any;
|
||||||
|
|
||||||
display(organization: Organization) {
|
/**
|
||||||
return true;
|
* If true, the description will be reused in the policy edit modal. Set this to false if you
|
||||||
|
* have more complex requirements that you will implement in your template instead.
|
||||||
|
**/
|
||||||
|
showDescription: boolean = true;
|
||||||
|
|
||||||
|
display(organization: Organization, configService: ConfigService): Observable<boolean> {
|
||||||
|
return of(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Directive()
|
@Directive()
|
||||||
export abstract class BasePolicyComponent implements OnInit {
|
export abstract class BasePolicyComponent implements OnInit {
|
||||||
@Input() policyResponse: PolicyResponse;
|
@Input() policyResponse: PolicyResponse | undefined;
|
||||||
@Input() policy: BasePolicy;
|
@Input() policy: BasePolicy | undefined;
|
||||||
|
|
||||||
enabled = new UntypedFormControl(false);
|
enabled = new UntypedFormControl(false);
|
||||||
data: UntypedFormGroup = null;
|
data: UntypedFormGroup | undefined;
|
||||||
|
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.enabled.setValue(this.policyResponse.enabled);
|
this.enabled.setValue(this.policyResponse?.enabled);
|
||||||
|
|
||||||
if (this.policyResponse.data != null) {
|
if (this.policyResponse?.data != null) {
|
||||||
this.loadData();
|
this.loadData();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buildRequest() {
|
buildRequest() {
|
||||||
const request = new PolicyRequest();
|
if (!this.policy) {
|
||||||
request.enabled = this.enabled.value;
|
throw new Error("Policy was not found");
|
||||||
request.type = this.policy.type;
|
}
|
||||||
request.data = this.buildRequestData();
|
|
||||||
|
const request: PolicyRequest = {
|
||||||
|
type: this.policy.type,
|
||||||
|
enabled: this.enabled.value,
|
||||||
|
data: this.buildRequestData(),
|
||||||
|
};
|
||||||
|
|
||||||
return Promise.resolve(request);
|
return Promise.resolve(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enable optional validation before sumitting a respose for policy submission
|
||||||
|
* */
|
||||||
|
confirm(): Promise<boolean> | boolean {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected loadData() {
|
protected loadData() {
|
||||||
this.data.patchValue(this.policyResponse.data ?? {});
|
this.data?.patchValue(this.policyResponse?.data ?? {});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected buildRequestData() {
|
protected buildRequestData() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ export { BasePolicy, BasePolicyComponent } from "./base-policy.component";
|
|||||||
export { DisableSendPolicy } from "./disable-send.component";
|
export { DisableSendPolicy } from "./disable-send.component";
|
||||||
export { MasterPasswordPolicy } from "./master-password.component";
|
export { MasterPasswordPolicy } from "./master-password.component";
|
||||||
export { PasswordGeneratorPolicy } from "./password-generator.component";
|
export { PasswordGeneratorPolicy } from "./password-generator.component";
|
||||||
|
export { vNextOrganizationDataOwnershipPolicy } from "./vnext-organization-data-ownership.component";
|
||||||
export { OrganizationDataOwnershipPolicy } from "./organization-data-ownership.component";
|
export { OrganizationDataOwnershipPolicy } from "./organization-data-ownership.component";
|
||||||
export { RequireSsoPolicy } from "./require-sso.component";
|
export { RequireSsoPolicy } from "./require-sso.component";
|
||||||
export { ResetPasswordPolicy } from "./reset-password.component";
|
export { ResetPasswordPolicy } from "./reset-password.component";
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
import { map, Observable } from "rxjs";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
|
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";
|
||||||
|
|
||||||
@@ -9,6 +13,12 @@ export class OrganizationDataOwnershipPolicy extends BasePolicy {
|
|||||||
description = "personalOwnershipPolicyDesc";
|
description = "personalOwnershipPolicyDesc";
|
||||||
type = PolicyType.OrganizationDataOwnership;
|
type = PolicyType.OrganizationDataOwnership;
|
||||||
component = OrganizationDataOwnershipPolicyComponent;
|
component = OrganizationDataOwnershipPolicyComponent;
|
||||||
|
|
||||||
|
display(organization: Organization, configService: ConfigService): Observable<boolean> {
|
||||||
|
return configService
|
||||||
|
.getFeatureFlag$(FeatureFlag.CreateDefaultLocation)
|
||||||
|
.pipe(map((enabled) => !enabled));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@@ -1,38 +1,45 @@
|
|||||||
<app-header>
|
<app-header>
|
||||||
@let organization = organization$ | async;
|
@let organization = organization$ | async;
|
||||||
<button
|
@if (isBreadcrumbingEnabled$ | async) {
|
||||||
bitBadge
|
<button
|
||||||
class="!tw-align-middle"
|
bitBadge
|
||||||
(click)="changePlan(organization)"
|
class="!tw-align-middle"
|
||||||
*ngIf="isBreadcrumbingEnabled$ | async"
|
(click)="changePlan(organization)"
|
||||||
slot="title-suffix"
|
slot="title-suffix"
|
||||||
type="button"
|
type="button"
|
||||||
variant="primary"
|
variant="primary"
|
||||||
>
|
>
|
||||||
{{ "upgrade" | i18n }}
|
{{ "upgrade" | i18n }}
|
||||||
</button>
|
</button>
|
||||||
|
}
|
||||||
</app-header>
|
</app-header>
|
||||||
|
|
||||||
<bit-container>
|
<bit-container>
|
||||||
<ng-container *ngIf="loading">
|
@if (loading) {
|
||||||
<i
|
<i
|
||||||
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
class="bwi bwi-spinner bwi-spin tw-text-muted"
|
||||||
title="{{ 'loading' | i18n }}"
|
title="{{ 'loading' | i18n }}"
|
||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
></i>
|
></i>
|
||||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||||
</ng-container>
|
}
|
||||||
<bit-table *ngIf="!loading">
|
@if (!loading) {
|
||||||
<ng-template body>
|
<bit-table>
|
||||||
<tr bitRow *ngFor="let p of policies">
|
<ng-template body>
|
||||||
<td bitCell *ngIf="p.display(organization)" ngPreserveWhitespaces>
|
@for (p of policies; track p.name) {
|
||||||
<button type="button" bitLink (click)="edit(p)">{{ p.name | i18n }}</button>
|
@if (p.display(organization, configService) | async) {
|
||||||
<span bitBadge variant="success" *ngIf="policiesEnabledMap.get(p.type)">{{
|
<tr bitRow>
|
||||||
"on" | i18n
|
<td bitCell ngPreserveWhitespaces>
|
||||||
}}</span>
|
<button type="button" bitLink (click)="edit(p)">{{ p.name | i18n }}</button>
|
||||||
<small class="tw-text-muted tw-block">{{ p.description | i18n }}</small>
|
@if (policiesEnabledMap.get(p.type)) {
|
||||||
</td>
|
<span bitBadge variant="success">{{ "on" | i18n }}</span>
|
||||||
</tr>
|
}
|
||||||
</ng-template>
|
<small class="tw-text-muted tw-block">{{ p.description | i18n }}</small>
|
||||||
</bit-table>
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</ng-template>
|
||||||
|
</bit-table>
|
||||||
|
}
|
||||||
</bit-container>
|
</bit-container>
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga
|
|||||||
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
|
import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
import { OrganizationBillingServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
import { OrganizationBillingServiceAbstraction } from "@bitwarden/common/billing/abstractions";
|
||||||
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";
|
||||||
import {
|
import {
|
||||||
@@ -25,7 +24,7 @@ import {
|
|||||||
import { All } from "@bitwarden/web-vault/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model";
|
import { All } from "@bitwarden/web-vault/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model";
|
||||||
|
|
||||||
import { PolicyListService } from "../../core/policy-list.service";
|
import { PolicyListService } from "../../core/policy-list.service";
|
||||||
import { BasePolicy, RestrictedItemTypesPolicy } from "../policies";
|
import { BasePolicy } from "../policies";
|
||||||
import { CollectionDialogTabType } from "../shared/components/collection-dialog";
|
import { CollectionDialogTabType } from "../shared/components/collection-dialog";
|
||||||
|
|
||||||
import { PolicyEditComponent, PolicyEditDialogResult } from "./policy-edit.component";
|
import { PolicyEditComponent, PolicyEditDialogResult } from "./policy-edit.component";
|
||||||
@@ -53,7 +52,7 @@ export class PoliciesComponent implements OnInit {
|
|||||||
private policyListService: PolicyListService,
|
private policyListService: PolicyListService,
|
||||||
private organizationBillingService: OrganizationBillingServiceAbstraction,
|
private organizationBillingService: OrganizationBillingServiceAbstraction,
|
||||||
private dialogService: DialogService,
|
private dialogService: DialogService,
|
||||||
private configService: ConfigService,
|
protected configService: ConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
@@ -71,35 +70,31 @@ export class PoliciesComponent implements OnInit {
|
|||||||
await this.load();
|
await this.load();
|
||||||
|
|
||||||
// Handle policies component launch from Event message
|
// Handle policies component launch from Event message
|
||||||
/* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */
|
this.route.queryParams
|
||||||
this.route.queryParams.pipe(first()).subscribe(async (qParams) => {
|
.pipe(first())
|
||||||
if (qParams.policyId != null) {
|
/* eslint-disable-next-line rxjs-angular/prefer-takeuntil, rxjs/no-async-subscribe, rxjs/no-nested-subscribe */
|
||||||
const policyIdFromEvents: string = qParams.policyId;
|
.subscribe(async (qParams) => {
|
||||||
for (const orgPolicy of this.orgPolicies) {
|
if (qParams.policyId != null) {
|
||||||
if (orgPolicy.id === policyIdFromEvents) {
|
const policyIdFromEvents: string = qParams.policyId;
|
||||||
for (let i = 0; i < this.policies.length; i++) {
|
for (const orgPolicy of this.orgPolicies) {
|
||||||
if (this.policies[i].type === orgPolicy.type) {
|
if (orgPolicy.id === policyIdFromEvents) {
|
||||||
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
for (let i = 0; i < this.policies.length; i++) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
if (this.policies[i].type === orgPolicy.type) {
|
||||||
this.edit(this.policies[i]);
|
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
|
||||||
break;
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||||
|
this.edit(this.policies[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
if (
|
|
||||||
(await this.configService.getFeatureFlag(FeatureFlag.RemoveCardItemTypePolicy)) &&
|
|
||||||
this.policyListService.getPolicies().every((p) => !(p instanceof RestrictedItemTypesPolicy))
|
|
||||||
) {
|
|
||||||
this.policyListService.addPolicies([new RestrictedItemTypesPolicy()]);
|
|
||||||
}
|
|
||||||
const response = await this.policyApiService.getPolicies(this.organizationId);
|
const response = await this.policyApiService.getPolicies(this.organizationId);
|
||||||
this.orgPolicies = response.data != null && response.data.length > 0 ? response.data : [];
|
this.orgPolicies = response.data != null && response.data.length > 0 ? response.data : [];
|
||||||
this.orgPolicies.forEach((op) => {
|
this.orgPolicies.forEach((op) => {
|
||||||
|
|||||||
@@ -22,7 +22,9 @@
|
|||||||
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
<span class="tw-sr-only">{{ "loading" | i18n }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div [hidden]="loading">
|
<div [hidden]="loading">
|
||||||
<p bitTypography="body1">{{ policy.description | i18n }}</p>
|
@if (policy.showDescription) {
|
||||||
|
<p bitTypography="body1">{{ policy.description | i18n }}</p>
|
||||||
|
}
|
||||||
<ng-template #policyForm></ng-template>
|
<ng-template #policyForm></ng-template>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|||||||
@@ -128,13 +128,20 @@ export class PolicyEditComponent implements AfterViewInit {
|
|||||||
}
|
}
|
||||||
|
|
||||||
submit = async () => {
|
submit = async () => {
|
||||||
|
if ((await this.policyComponent.confirm()) == false) {
|
||||||
|
this.dialogRef.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let request: PolicyRequest;
|
let request: PolicyRequest;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
request = await this.policyComponent.buildRequest();
|
request = await this.policyComponent.buildRequest();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.toastService.showToast({ variant: "error", title: null, message: e.message });
|
this.toastService.showToast({ variant: "error", title: null, message: e.message });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.policyApiService.putPolicy(this.data.organizationId, this.data.policy.type, request);
|
await this.policyApiService.putPolicy(this.data.organizationId, this.data.policy.type, request);
|
||||||
this.toastService.showToast({
|
this.toastService.showToast({
|
||||||
variant: "success",
|
variant: "success",
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
import { of } from "rxjs";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
|
|
||||||
import { BasePolicy, BasePolicyComponent } from "./base-policy.component";
|
import { BasePolicy, BasePolicyComponent } from "./base-policy.component";
|
||||||
|
|
||||||
@@ -11,8 +13,8 @@ export class RequireSsoPolicy extends BasePolicy {
|
|||||||
type = PolicyType.RequireSso;
|
type = PolicyType.RequireSso;
|
||||||
component = RequireSsoPolicyComponent;
|
component = RequireSsoPolicyComponent;
|
||||||
|
|
||||||
display(organization: Organization) {
|
display(organization: Organization, configService: ConfigService) {
|
||||||
return organization.useSso;
|
return of(organization.useSso);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Component, OnInit } from "@angular/core";
|
import { Component, OnInit } from "@angular/core";
|
||||||
import { FormBuilder } from "@angular/forms";
|
import { FormBuilder } from "@angular/forms";
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom, of } from "rxjs";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getOrganizationById,
|
getOrganizationById,
|
||||||
@@ -10,6 +10,7 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
|||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
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 { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
|
|
||||||
import { BasePolicy, BasePolicyComponent } from "./base-policy.component";
|
import { BasePolicy, BasePolicyComponent } from "./base-policy.component";
|
||||||
|
|
||||||
@@ -19,8 +20,8 @@ export class ResetPasswordPolicy extends BasePolicy {
|
|||||||
type = PolicyType.ResetPassword;
|
type = PolicyType.ResetPassword;
|
||||||
component = ResetPasswordPolicyComponent;
|
component = ResetPasswordPolicyComponent;
|
||||||
|
|
||||||
display(organization: Organization) {
|
display(organization: Organization, configService: ConfigService) {
|
||||||
return organization.useResetPassword;
|
return of(organization.useResetPassword);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -52,6 +53,10 @@ export class ResetPasswordPolicyComponent extends BasePolicyComponent implements
|
|||||||
throw new Error("No user found.");
|
throw new Error("No user found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!this.policyResponse) {
|
||||||
|
throw new Error("Policies not found");
|
||||||
|
}
|
||||||
|
|
||||||
const organization = await firstValueFrom(
|
const organization = await firstValueFrom(
|
||||||
this.organizationService
|
this.organizationService
|
||||||
.organizations$(userId)
|
.organizations$(userId)
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
import { Observable } from "rxjs";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
|
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";
|
||||||
|
|
||||||
@@ -9,6 +13,10 @@ export class RestrictedItemTypesPolicy extends BasePolicy {
|
|||||||
description = "restrictedItemTypePolicyDesc";
|
description = "restrictedItemTypePolicyDesc";
|
||||||
type = PolicyType.RestrictedItemTypes;
|
type = PolicyType.RestrictedItemTypes;
|
||||||
component = RestrictedItemTypesPolicyComponent;
|
component = RestrictedItemTypesPolicyComponent;
|
||||||
|
|
||||||
|
display(organization: Organization, configService: ConfigService): Observable<boolean> {
|
||||||
|
return configService.getFeatureFlag$(FeatureFlag.RemoveCardItemTypePolicy);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ export class SingleOrgPolicyComponent extends BasePolicyComponent implements OnI
|
|||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
|
|
||||||
|
if (!this.policyResponse) {
|
||||||
|
throw new Error("Policies not found");
|
||||||
|
}
|
||||||
if (!this.policyResponse.canToggleState) {
|
if (!this.policyResponse.canToggleState) {
|
||||||
this.enabled.disable();
|
this.enabled.disable();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
<p>
|
||||||
|
{{ "organizationDataOwnershipContent" | i18n }}
|
||||||
|
<a
|
||||||
|
bitLink
|
||||||
|
href="https://bitwarden.com/resources/credential-lifecycle-management/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ "organizationDataOwnershipContentAnchor" | i18n }}.
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<bit-form-control>
|
||||||
|
<input type="checkbox" bitCheckbox [formControl]="enabled" id="enabled" />
|
||||||
|
<bit-label>{{ "turnOn" | i18n }}</bit-label>
|
||||||
|
</bit-form-control>
|
||||||
|
|
||||||
|
<ng-template #dialog>
|
||||||
|
<bit-simple-dialog background="alt">
|
||||||
|
<span bitDialogTitle>{{ "organizationDataOwnershipWarningTitle" | i18n }}</span>
|
||||||
|
<ng-container bitDialogContent>
|
||||||
|
<div class="tw-text-left tw-overflow-hidden">
|
||||||
|
{{ "organizationDataOwnershipWarningContentTop" | i18n }}
|
||||||
|
<div class="tw-flex tw-flex-col tw-p-2">
|
||||||
|
<ul class="tw-list-disc tw-pl-5 tw-space-y-2 tw-break-words tw-mb-0">
|
||||||
|
<li>
|
||||||
|
{{ "organizationDataOwnershipWarning1" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{{ "organizationDataOwnershipWarning2" | i18n }}
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
{{ "organizationDataOwnershipWarning3" | i18n }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{ "organizationDataOwnershipWarningContentBottom" | i18n }}
|
||||||
|
<a
|
||||||
|
bitLink
|
||||||
|
href="https://bitwarden.com/resources/credential-lifecycle-management/"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
{{ "organizationDataOwnershipContentAnchor" | i18n }}.
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</ng-container>
|
||||||
|
<ng-container bitDialogFooter>
|
||||||
|
<span class="tw-flex tw-gap-2">
|
||||||
|
<button bitButton buttonType="primary" [bitDialogClose]="true" type="submit">
|
||||||
|
{{ "continue" | i18n }}
|
||||||
|
</button>
|
||||||
|
<button bitButton buttonType="secondary" [bitDialogClose]="false" type="button">
|
||||||
|
{{ "cancel" | i18n }}
|
||||||
|
</button>
|
||||||
|
</span>
|
||||||
|
</ng-container>
|
||||||
|
</bit-simple-dialog>
|
||||||
|
</ng-template>
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
import { Component, OnInit, TemplateRef, ViewChild } from "@angular/core";
|
||||||
|
import { lastValueFrom, Observable } from "rxjs";
|
||||||
|
|
||||||
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
|
import { DialogService } from "@bitwarden/components";
|
||||||
|
|
||||||
|
import { SharedModule } from "../../../shared";
|
||||||
|
|
||||||
|
import { BasePolicy, BasePolicyComponent } from "./base-policy.component";
|
||||||
|
|
||||||
|
export class vNextOrganizationDataOwnershipPolicy extends BasePolicy {
|
||||||
|
name = "organizationDataOwnership";
|
||||||
|
description = "organizationDataOwnershipDesc";
|
||||||
|
type = PolicyType.OrganizationDataOwnership;
|
||||||
|
component = vNextOrganizationDataOwnershipPolicyComponent;
|
||||||
|
showDescription = false;
|
||||||
|
|
||||||
|
override display(organization: Organization, configService: ConfigService): Observable<boolean> {
|
||||||
|
return configService.getFeatureFlag$(FeatureFlag.CreateDefaultLocation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "vnext-policy-organization-data-ownership",
|
||||||
|
templateUrl: "vnext-organization-data-ownership.component.html",
|
||||||
|
standalone: true,
|
||||||
|
imports: [SharedModule],
|
||||||
|
})
|
||||||
|
export class vNextOrganizationDataOwnershipPolicyComponent
|
||||||
|
extends BasePolicyComponent
|
||||||
|
implements OnInit
|
||||||
|
{
|
||||||
|
constructor(private dialogService: DialogService) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewChild("dialog", { static: true }) warningContent!: TemplateRef<unknown>;
|
||||||
|
|
||||||
|
override async confirm(): Promise<boolean> {
|
||||||
|
if (this.policyResponse?.enabled && !this.enabled.value) {
|
||||||
|
const dialogRef = this.dialogService.open(this.warningContent);
|
||||||
|
const result = await lastValueFrom(dialogRef.closed);
|
||||||
|
return Boolean(result);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -35,12 +35,14 @@ import {
|
|||||||
MasterPasswordPolicy,
|
MasterPasswordPolicy,
|
||||||
PasswordGeneratorPolicy,
|
PasswordGeneratorPolicy,
|
||||||
OrganizationDataOwnershipPolicy,
|
OrganizationDataOwnershipPolicy,
|
||||||
|
vNextOrganizationDataOwnershipPolicy,
|
||||||
RequireSsoPolicy,
|
RequireSsoPolicy,
|
||||||
ResetPasswordPolicy,
|
ResetPasswordPolicy,
|
||||||
SendOptionsPolicy,
|
SendOptionsPolicy,
|
||||||
SingleOrgPolicy,
|
SingleOrgPolicy,
|
||||||
TwoFactorAuthenticationPolicy,
|
TwoFactorAuthenticationPolicy,
|
||||||
RemoveUnlockWithPinPolicy,
|
RemoveUnlockWithPinPolicy,
|
||||||
|
RestrictedItemTypesPolicy,
|
||||||
} from "./admin-console/organizations/policies";
|
} from "./admin-console/organizations/policies";
|
||||||
|
|
||||||
const BroadcasterSubscriptionId = "AppComponent";
|
const BroadcasterSubscriptionId = "AppComponent";
|
||||||
@@ -244,8 +246,10 @@ export class AppComponent implements OnDestroy, OnInit {
|
|||||||
new SingleOrgPolicy(),
|
new SingleOrgPolicy(),
|
||||||
new RequireSsoPolicy(),
|
new RequireSsoPolicy(),
|
||||||
new OrganizationDataOwnershipPolicy(),
|
new OrganizationDataOwnershipPolicy(),
|
||||||
|
new vNextOrganizationDataOwnershipPolicy(),
|
||||||
new DisableSendPolicy(),
|
new DisableSendPolicy(),
|
||||||
new SendOptionsPolicy(),
|
new SendOptionsPolicy(),
|
||||||
|
new RestrictedItemTypesPolicy(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5429,6 +5429,37 @@
|
|||||||
"organizationDataOwnership": {
|
"organizationDataOwnership": {
|
||||||
"message": "Enforce organization data ownership"
|
"message": "Enforce organization data ownership"
|
||||||
},
|
},
|
||||||
|
"organizationDataOwnershipDesc": {
|
||||||
|
"message": "Require all items to be owned by an organization, removing the option to store items at the account level.",
|
||||||
|
"description": "This is the policy description shown in the policy list."
|
||||||
|
},
|
||||||
|
"organizationDataOwnershipContent": {
|
||||||
|
"message": "All items will be owned and saved to the organization, enabling organization-wide controls, visibility, and reporting. When turned on, a default collection be available for each member to store items. Learn more about managing the ",
|
||||||
|
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'All items will be owned and saved to the organization, enabling organization-wide controls, visibility, and reporting. When turned on, a default collection be available for each member to store items. Learn more about managing the credential lifecycle.'"
|
||||||
|
},
|
||||||
|
"organizationDataOwnershipContentAnchor":{
|
||||||
|
"message": "credential lifecycle",
|
||||||
|
"description": "This will be used as a hyperlink"
|
||||||
|
},
|
||||||
|
"organizationDataOwnershipWarningTitle":{
|
||||||
|
"message": "Are you sure you want to proceed?"
|
||||||
|
},
|
||||||
|
"organizationDataOwnershipWarning1":{
|
||||||
|
"message": "will remain accessible to members"
|
||||||
|
},
|
||||||
|
"organizationDataOwnershipWarning2":{
|
||||||
|
"message": "will not be automatically selected when creating new items"
|
||||||
|
},
|
||||||
|
"organizationDataOwnershipWarning3":{
|
||||||
|
"message": "cannot be managed from the Admin Console until the user is offboarded"
|
||||||
|
},
|
||||||
|
"organizationDataOwnershipWarningContentTop":{
|
||||||
|
"message": "By turning this policy off, the default collection: "
|
||||||
|
},
|
||||||
|
"organizationDataOwnershipWarningContentBottom":{
|
||||||
|
"message": "Learn more about the ",
|
||||||
|
"description": "This will be used as part of a larger sentence, broken up to include links. The full sentence will read 'Learn more about the credential lifecycle.'"
|
||||||
|
},
|
||||||
"personalOwnership": {
|
"personalOwnership": {
|
||||||
"message": "Remove individual vault"
|
"message": "Remove individual vault"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
|
import { of } from "rxjs";
|
||||||
|
|
||||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||||
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import {
|
import {
|
||||||
BasePolicy,
|
BasePolicy,
|
||||||
BasePolicyComponent,
|
BasePolicyComponent,
|
||||||
@@ -13,8 +15,8 @@ export class ActivateAutofillPolicy extends BasePolicy {
|
|||||||
type = PolicyType.ActivateAutofill;
|
type = PolicyType.ActivateAutofill;
|
||||||
component = ActivateAutofillPolicyComponent;
|
component = ActivateAutofillPolicyComponent;
|
||||||
|
|
||||||
display(organization: Organization) {
|
display(organization: Organization, configService: ConfigService) {
|
||||||
return organization.useActivateAutofillPolicy;
|
return of(organization.useActivateAutofillPolicy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
|
||||||
// @ts-strict-ignore
|
|
||||||
import { PolicyType } from "../../enums";
|
import { PolicyType } from "../../enums";
|
||||||
|
|
||||||
export class PolicyRequest {
|
export type PolicyRequest = {
|
||||||
type: PolicyType;
|
type: PolicyType;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
data: any;
|
data: any;
|
||||||
}
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user