diff --git a/.gitignore b/.gitignore index 0fa968aa47c..0f609335b63 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ Thumbs.db *.launch .settings/ *.sublime-workspace +.claude # Visual Studio Code .vscode/* diff --git a/apps/web/src/app/admin-console/core/policy-list.service.ts b/apps/web/src/app/admin-console/core/policy-list.service.ts deleted file mode 100644 index bb207006906..00000000000 --- a/apps/web/src/app/admin-console/core/policy-list.service.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { BasePolicy } from "../organizations/policies"; - -export class PolicyListService { - private policies: BasePolicy[] = []; - - addPolicies(policies: BasePolicy[]) { - this.policies.push(...policies); - } - - getPolicies(): BasePolicy[] { - return this.policies; - } -} diff --git a/apps/web/src/app/admin-console/organizations/policies/README.md b/apps/web/src/app/admin-console/organizations/policies/README.md new file mode 100644 index 00000000000..e3b4efdbbf7 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/README.md @@ -0,0 +1,234 @@ +# Adding a New Policy in Admin Console + +This README explains how to add a new policy type to the Admin Console. Policies are used to control organizational behavior and security settings for their members. + +This README does not cover checking the policy status in order to enforce it in the domain code. + +## Overview + +Each policy consists of three main components: + +1. **Policy Type Enum** - Defines the policy type identifier +2. **Policy Definition & Component** - Implements the UI and business logic +3. **Registration** - Registers the policy in the application + +## Step 1: Adding the Enum + +Add your new policy type to the `PolicyType` enum. + +**Important**: You must also add the corresponding PolicyType enum value on the server. + +Example: + +```typescript +export enum PolicyType { + // ... existing policies + YourNewPolicy = 21, // Use the next available number +} +``` + +## Step 2: Creating the Policy Definition and Component + +### Policy Licensing and Location + +The location where you create your policy depends on its licensing: + +- **Open Source (OSS) Policies**: Create in `apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/` +- **Bitwarden Licensed Policies**: Create in `bitwarden_license/bit-web/src/app/admin-console/policies/` + +Most policies should be OSS licensed unless they specifically relate to premium/enterprise features that are part of Bitwarden's commercial offerings. + +Create a new component file in the appropriate `policy-edit-definitions/` folder following the naming pattern `your-policy-name.component.ts`. + +**Note:** you may also create the policy files in your own team's code if you prefer to own your own definition. The same licensing considerations apply. + +### Basic Structure + +```typescript +import { Component } from "@angular/core"; +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; + +// Policy Definition Class +export class YourNewPolicy extends BasePolicyEditDefinition { + name = "yourPolicyNameTitle"; // i18n key for title + description = "yourPolicyNameDesc"; // i18n key for description + type = PolicyType.YourNewPolicy; // Reference to enum + component = YourNewPolicyComponent; // Reference to component +} + +// Policy Component Class +@Component({ + templateUrl: "your-policy-name.component.html", + imports: [SharedModule], +}) +export class YourNewPolicyComponent extends BasePolicyEditComponent { + // Component implementation +} +``` + +### Common Use Cases + +#### Simple Toggle Policy (No Additional Configuration) + +For policies that only need an enabled/disabled state: + +```typescript +export class SimpleTogglePolicy extends BasePolicyEditDefinition { + name = "simpleTogglePolicyTitle"; + description = "simpleTogglePolicyDesc"; + type = PolicyType.SimpleToggle; + component = SimpleTogglePolicyComponent; +} + +@Component({ + templateUrl: "simple-toggle.component.html", + imports: [SharedModule], +}) +export class SimpleTogglePolicyComponent extends BasePolicyEditComponent {} +``` + +Template (`simple-toggle.component.html`): + +```html + + + {{ "turnOn" | i18n }} + +``` + +#### Policy with Configuration Data + +For policies requiring additional settings beyond just enabled/disabled, you'll need to define a custom `data` FormGroup to handle the policy's configuration options: + +```typescript +import { FormBuilder, FormGroup, Validators } from "@angular/forms"; +import { ControlsOf } from "@bitwarden/angular/types/controls-of"; + +interface YourPolicyOptions { + minLength: number; + requireSpecialChar: boolean; +} + +@Component({ + templateUrl: "your-policy.component.html", + imports: [SharedModule], +}) +export class YourPolicyComponent extends BasePolicyEditComponent implements OnInit { + data: FormGroup> = this.formBuilder.group({ + minLength: [8, [Validators.min(1)]], + requireSpecialChar: [false], + }); + + constructor(private formBuilder: FormBuilder) { + super(); + } + + async ngOnInit() { + super.ngOnInit(); + // Additional initialization logic + } +} +``` + +Template (`your-policy.component.html`): + +```html + + + {{ "turnOn" | i18n }} + + + + + {{ "minimumLength" | i18n }} + + + + + + {{ "requireSpecialCharacter" | i18n }} + + +``` + +#### Feature Flagged Policy + +To hide a policy behind a feature flag using ConfigService: + +```typescript +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; + +export class NewPolicyBeta extends BasePolicyEditDefinition { + name = "newPolicyTitle"; + description = "newPolicyDesc"; + type = PolicyType.NewPolicy; + component = NewPolicyComponent; + + // Only show if feature flag is enabled + display$(organization: Organization, configService: ConfigService) { + return configService.getFeatureFlag$(FeatureFlag.YourNewPolicyFeature); + } +} +``` + +#### Policy related to Organization Features + +To show a policy only when the organization has a specific plan feature: + +```typescript +import { of } from "rxjs"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; + +export class RequireSsoPolicy extends BasePolicyEditDefinition { + name = "requireSsoTitle"; + description = "requireSsoDesc"; + type = PolicyType.RequireSso; + component = RequireSsoPolicyComponent; + + // Only show if organization has SSO enabled + display$(organization: Organization, configService: ConfigService) { + return of(organization.useSso); + } +} +``` + +## Step 3: Registering the Policy + +### Export from Index File + +Add your policy to the barrel file in its folder: + +```typescript +export { YourNewPolicy } from "./your-policy-name.component"; +``` + +### Register in Policy Register + +Add your policy to the appropriate register in `policy-edit-register.ts`: + +```typescript +import { + // ... existing imports + YourNewPolicy, +} from "./policy-edit-definitions"; + +export const ossPolicyEditRegister: BasePolicyEditDefinition[] = [ + // ... existing policies + new YourNewPolicy(), +]; +``` + +**Note**: Use `ossPolicyEditRegister` for open-source policies and `bitPolicyEditRegister` for Bitwarden Licensed policies. + +## Testing Your Policy + +1. Build and run the application +2. Navigate to Admin Console → Policies +3. Verify your policy appears in the list +4. Test the policy configuration UI +5. Verify policy data saves correctly diff --git a/apps/web/src/app/admin-console/organizations/policies/base-policy-edit.component.ts b/apps/web/src/app/admin-console/organizations/policies/base-policy-edit.component.ts new file mode 100644 index 00000000000..2e5faea4702 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/base-policy-edit.component.ts @@ -0,0 +1,119 @@ +import { Directive, Input, OnInit } from "@angular/core"; +import { FormControl, UntypedFormGroup } from "@angular/forms"; +import { Observable, of } from "rxjs"; +import { Constructor } from "type-fest"; + +import { PolicyType } from "@bitwarden/common/admin-console/enums"; +import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; +import { PolicyRequest } from "@bitwarden/common/admin-console/models/request/policy.request"; +import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; + +/** + * A metadata class that defines how a policy is displayed in the Admin Console Policies page for editing. + * Add this to the `ossPolicyRegister` or `bitPolicyRegister` file to register it in the application. + */ +export abstract class BasePolicyEditDefinition { + /** + * i18n string for the policy name. + */ + abstract name: string; + /** + * i18n string for the policy description. + * This is shown in the list of policies. + */ + abstract description: string; + /** + * The PolicyType enum that this policy represents. + */ + abstract type: PolicyType; + /** + * The component used to edit this policy. See {@link BasePolicyEditComponent}. + */ + abstract component: Constructor; + + /** + * If true, the {@link 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; + + /** + * A method that determines whether to display this policy in the Admin Console Policies page. + * The default implementation will always display the policy. + * This can be used to hide the policy based on the organization's plan features or a feature flag value. + * Note: this only hides the policy for editing in Admin Console, it does not affect its enforcement + * if it has already been turned on. Enforcement should be feature flagged separately. + */ + display$(organization: Organization, configService: ConfigService): Observable { + return of(true); + } +} + +/** + * A component used to edit the policy settings in Admin Console. It is rendered inside the PolicyEditDialogComponent. + * This should contain the form controls used to edit the policy (including the Enabled checkbox) and any additional + * warnings or callouts. + * See existing implementations as a guide. + */ +@Directive() +export abstract class BasePolicyEditComponent implements OnInit { + @Input() policyResponse: PolicyResponse | undefined; + @Input() policy: BasePolicyEditDefinition | undefined; + + /** + * Whether the policy is enabled. + */ + enabled = new FormControl(false); + + /** + * An optional FormGroup for additional policy configuration. Required for more complex policies only. + */ + data: UntypedFormGroup | undefined; + + ngOnInit(): void { + this.enabled.setValue(this.policyResponse?.enabled ?? false); + + if (this.policyResponse?.data != null) { + this.loadData(); + } + } + + buildRequest() { + if (!this.policy) { + throw new Error("Policy was not found"); + } + + const request: PolicyRequest = { + type: this.policy.type, + enabled: this.enabled.value ?? false, + data: this.buildRequestData(), + }; + + return Promise.resolve(request); + } + + /** + * This is called before the policy is saved. If it returns false, it will not be saved + * and the user will remain on the policy edit dialog. + * This can be used to trigger an additional confirmation modal before saving. + * */ + confirm(): Promise | boolean { + return true; + } + + protected loadData() { + this.data?.patchValue(this.policyResponse?.data ?? {}); + } + + /** + * Transforms the {@link data} FormGroup to the policy data model for saving. + */ + protected buildRequestData() { + if (this.data != null) { + return this.data.value; + } + + return null; + } +} diff --git a/apps/web/src/app/admin-console/organizations/policies/base-policy.component.ts b/apps/web/src/app/admin-console/organizations/policies/base-policy.component.ts deleted file mode 100644 index 3af99644dd2..00000000000 --- a/apps/web/src/app/admin-console/organizations/policies/base-policy.component.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { Directive, Input, OnInit } from "@angular/core"; -import { UntypedFormControl, UntypedFormGroup } from "@angular/forms"; -import { Observable, of } from "rxjs"; - -import { PolicyType } from "@bitwarden/common/admin-console/enums"; -import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; -import { PolicyRequest } from "@bitwarden/common/admin-console/models/request/policy.request"; -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 { - abstract name: string; - abstract description: string; - abstract type: PolicyType; - abstract component: any; - - /** - * 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 { - return of(true); - } -} - -@Directive() -export abstract class BasePolicyComponent implements OnInit { - @Input() policyResponse: PolicyResponse | undefined; - @Input() policy: BasePolicy | undefined; - - enabled = new UntypedFormControl(false); - data: UntypedFormGroup | undefined; - - ngOnInit(): void { - this.enabled.setValue(this.policyResponse?.enabled); - - if (this.policyResponse?.data != null) { - this.loadData(); - } - } - - buildRequest() { - if (!this.policy) { - throw new Error("Policy was not found"); - } - - const request: PolicyRequest = { - type: this.policy.type, - enabled: this.enabled.value, - data: this.buildRequestData(), - }; - - return Promise.resolve(request); - } - - /** - * Enable optional validation before sumitting a respose for policy submission - * */ - confirm(): Promise | boolean { - return true; - } - - protected loadData() { - this.data?.patchValue(this.policyResponse?.data ?? {}); - } - - protected buildRequestData() { - if (this.data != null) { - return this.data.value; - } - - return null; - } -} diff --git a/apps/web/src/app/admin-console/organizations/policies/index.ts b/apps/web/src/app/admin-console/organizations/policies/index.ts index 6b6b2303b2f..624e5132faf 100644 --- a/apps/web/src/app/admin-console/organizations/policies/index.ts +++ b/apps/web/src/app/admin-console/organizations/policies/index.ts @@ -1,15 +1,4 @@ -export * from "./policies.module"; -export { BasePolicy, BasePolicyComponent } from "./base-policy.component"; -export { DisableSendPolicy } from "./disable-send.component"; -export { MasterPasswordPolicy } from "./master-password.component"; -export { PasswordGeneratorPolicy } from "./password-generator.component"; -export { vNextOrganizationDataOwnershipPolicy } from "./vnext-organization-data-ownership.component"; -export { OrganizationDataOwnershipPolicy } from "./organization-data-ownership.component"; -export { RequireSsoPolicy } from "./require-sso.component"; -export { ResetPasswordPolicy } from "./reset-password.component"; -export { SendOptionsPolicy } from "./send-options.component"; -export { SingleOrgPolicy } from "./single-org.component"; -export { TwoFactorAuthenticationPolicy } from "./two-factor-authentication.component"; export { PoliciesComponent } from "./policies.component"; -export { RemoveUnlockWithPinPolicy } from "./remove-unlock-with-pin.component"; -export { RestrictedItemTypesPolicy } from "./restricted-item-types.component"; +export { ossPolicyEditRegister } from "./policy-edit-register"; +export { BasePolicyEditDefinition, BasePolicyEditComponent } from "./base-policy-edit.component"; +export { POLICY_EDIT_REGISTER } from "./policy-register-token"; diff --git a/apps/web/src/app/admin-console/organizations/policies/policies.component.html b/apps/web/src/app/admin-console/organizations/policies/policies.component.html index 843d1d18d59..ea14986749f 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policies.component.html +++ b/apps/web/src/app/admin-console/organizations/policies/policies.component.html @@ -14,7 +14,7 @@ @for (p of policies; track p.name) { - @if (p.display(organization, configService) | async) { + @if (p.display$(organization, configService) | async) { diff --git a/apps/web/src/app/admin-console/organizations/policies/policies.component.ts b/apps/web/src/app/admin-console/organizations/policies/policies.component.ts index 094f0262629..45383133687 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policies.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policies.component.ts @@ -16,22 +16,31 @@ import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/ import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { DialogService } from "@bitwarden/components"; +import { safeProvider } from "@bitwarden/ui-common"; -import { PolicyListService } from "../../core/policy-list.service"; -import { BasePolicy } from "../policies"; +import { HeaderModule } from "../../../layouts/header/header.module"; +import { SharedModule } from "../../../shared"; -import { PolicyEditComponent, PolicyEditDialogResult } from "./policy-edit.component"; +import { BasePolicyEditDefinition } from "./base-policy-edit.component"; +import { PolicyEditDialogComponent } from "./policy-edit-dialog.component"; +import { PolicyListService } from "./policy-list.service"; +import { POLICY_EDIT_REGISTER } from "./policy-register-token"; @Component({ - selector: "app-org-policies", templateUrl: "policies.component.html", - standalone: false, + imports: [SharedModule, HeaderModule], + providers: [ + safeProvider({ + provide: PolicyListService, + deps: [POLICY_EDIT_REGISTER], + }), + ], }) export class PoliciesComponent implements OnInit { loading = true; organizationId: string; - policies: BasePolicy[]; - organization$: Observable; + policies: readonly BasePolicyEditDefinition[]; + protected organization$: Observable; private orgPolicies: PolicyResponse[]; protected policiesEnabledMap: Map = new Map(); @@ -97,8 +106,8 @@ export class PoliciesComponent implements OnInit { this.loading = false; } - async edit(policy: BasePolicy) { - const dialogRef = PolicyEditComponent.open(this.dialogService, { + async edit(policy: BasePolicyEditDefinition) { + const dialogRef = PolicyEditDialogComponent.open(this.dialogService, { data: { policy: policy, organizationId: this.organizationId, @@ -106,7 +115,7 @@ export class PoliciesComponent implements OnInit { }); const result = await lastValueFrom(dialogRef.closed); - if (result === PolicyEditDialogResult.Saved) { + if (result == "saved") { await this.load(); } } diff --git a/apps/web/src/app/admin-console/organizations/policies/policies.module.ts b/apps/web/src/app/admin-console/organizations/policies/policies.module.ts deleted file mode 100644 index 95b22497eba..00000000000 --- a/apps/web/src/app/admin-console/organizations/policies/policies.module.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { NgModule } from "@angular/core"; - -import { HeaderModule } from "../../../layouts/header/header.module"; -import { SharedModule } from "../../../shared"; - -import { DisableSendPolicyComponent } from "./disable-send.component"; -import { MasterPasswordPolicyComponent } from "./master-password.component"; -import { OrganizationDataOwnershipPolicyComponent } from "./organization-data-ownership.component"; -import { PasswordGeneratorPolicyComponent } from "./password-generator.component"; -import { PoliciesComponent } from "./policies.component"; -import { PolicyEditComponent } from "./policy-edit.component"; -import { RemoveUnlockWithPinPolicyComponent } from "./remove-unlock-with-pin.component"; -import { RequireSsoPolicyComponent } from "./require-sso.component"; -import { ResetPasswordPolicyComponent } from "./reset-password.component"; -import { RestrictedItemTypesPolicyComponent } from "./restricted-item-types.component"; -import { SendOptionsPolicyComponent } from "./send-options.component"; -import { SingleOrgPolicyComponent } from "./single-org.component"; -import { TwoFactorAuthenticationPolicyComponent } from "./two-factor-authentication.component"; - -@NgModule({ - imports: [SharedModule, HeaderModule], - declarations: [ - DisableSendPolicyComponent, - MasterPasswordPolicyComponent, - PasswordGeneratorPolicyComponent, - OrganizationDataOwnershipPolicyComponent, - RequireSsoPolicyComponent, - ResetPasswordPolicyComponent, - SendOptionsPolicyComponent, - SingleOrgPolicyComponent, - TwoFactorAuthenticationPolicyComponent, - PoliciesComponent, - PolicyEditComponent, - RemoveUnlockWithPinPolicyComponent, - RestrictedItemTypesPolicyComponent, - ], - exports: [ - DisableSendPolicyComponent, - MasterPasswordPolicyComponent, - PasswordGeneratorPolicyComponent, - OrganizationDataOwnershipPolicyComponent, - RequireSsoPolicyComponent, - ResetPasswordPolicyComponent, - SendOptionsPolicyComponent, - SingleOrgPolicyComponent, - TwoFactorAuthenticationPolicyComponent, - PoliciesComponent, - PolicyEditComponent, - RemoveUnlockWithPinPolicyComponent, - ], -}) -export class PoliciesModule {} diff --git a/apps/web/src/app/admin-console/organizations/policies/disable-send.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/disable-send.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/disable-send.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/disable-send.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/disable-send.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/disable-send.component.ts similarity index 50% rename from apps/web/src/app/admin-console/organizations/policies/disable-send.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/disable-send.component.ts index b323ac00d34..3b4df75e555 100644 --- a/apps/web/src/app/admin-console/organizations/policies/disable-send.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/disable-send.component.ts @@ -2,9 +2,10 @@ import { Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; -import { BasePolicy, BasePolicyComponent } from "./base-policy.component"; +import { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; -export class DisableSendPolicy extends BasePolicy { +export class DisableSendPolicy extends BasePolicyEditDefinition { name = "disableSend"; description = "disableSendPolicyDesc"; type = PolicyType.DisableSend; @@ -12,8 +13,7 @@ export class DisableSendPolicy extends BasePolicy { } @Component({ - selector: "policy-disable-send", templateUrl: "disable-send.component.html", - standalone: false, + imports: [SharedModule], }) -export class DisableSendPolicyComponent extends BasePolicyComponent {} +export class DisableSendPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/index.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/index.ts new file mode 100644 index 00000000000..13f29ab68f7 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/index.ts @@ -0,0 +1,15 @@ +export { DisableSendPolicy } from "./disable-send.component"; +export { MasterPasswordPolicy } from "./master-password.component"; +export { OrganizationDataOwnershipPolicy } from "./organization-data-ownership.component"; +export { PasswordGeneratorPolicy } from "./password-generator.component"; +export { RemoveUnlockWithPinPolicy } from "./remove-unlock-with-pin.component"; +export { RequireSsoPolicy } from "./require-sso.component"; +export { ResetPasswordPolicy } from "./reset-password.component"; +export { RestrictedItemTypesPolicy } from "./restricted-item-types.component"; +export { SendOptionsPolicy } from "./send-options.component"; +export { SingleOrgPolicy } from "./single-org.component"; +export { TwoFactorAuthenticationPolicy } from "./two-factor-authentication.component"; +export { + vNextOrganizationDataOwnershipPolicy, + vNextOrganizationDataOwnershipPolicyComponent, +} from "./vnext-organization-data-ownership.component"; diff --git a/apps/web/src/app/admin-console/organizations/policies/master-password.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/master-password.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/master-password.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.ts similarity index 88% rename from apps/web/src/app/admin-console/organizations/policies/master-password.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.ts index 54cf1be88fc..fe3d76a0907 100644 --- a/apps/web/src/app/admin-console/organizations/policies/master-password.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/master-password.component.ts @@ -16,9 +16,10 @@ import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { BasePolicy, BasePolicyComponent } from "./base-policy.component"; +import { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; -export class MasterPasswordPolicy extends BasePolicy { +export class MasterPasswordPolicy extends BasePolicyEditDefinition { name = "masterPassPolicyTitle"; description = "masterPassPolicyDesc"; type = PolicyType.MasterPassword; @@ -26,11 +27,10 @@ export class MasterPasswordPolicy extends BasePolicy { } @Component({ - selector: "policy-master-password", templateUrl: "master-password.component.html", - standalone: false, + imports: [SharedModule], }) -export class MasterPasswordPolicyComponent extends BasePolicyComponent implements OnInit { +export class MasterPasswordPolicyComponent extends BasePolicyEditComponent implements OnInit { MinPasswordLength = Utils.minimumPasswordLength; data: FormGroup> = this.formBuilder.group({ diff --git a/apps/web/src/app/admin-console/organizations/policies/organization-data-ownership.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/organization-data-ownership.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/organization-data-ownership.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts similarity index 70% rename from apps/web/src/app/admin-console/organizations/policies/organization-data-ownership.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts index beb9fd5752a..94094b76f69 100644 --- a/apps/web/src/app/admin-console/organizations/policies/organization-data-ownership.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts @@ -6,15 +6,16 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga 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 { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; -export class OrganizationDataOwnershipPolicy extends BasePolicy { +export class OrganizationDataOwnershipPolicy extends BasePolicyEditDefinition { name = "organizationDataOwnership"; description = "personalOwnershipPolicyDesc"; type = PolicyType.OrganizationDataOwnership; component = OrganizationDataOwnershipPolicyComponent; - display(organization: Organization, configService: ConfigService): Observable { + display$(organization: Organization, configService: ConfigService): Observable { return configService .getFeatureFlag$(FeatureFlag.CreateDefaultLocation) .pipe(map((enabled) => !enabled)); @@ -22,8 +23,7 @@ export class OrganizationDataOwnershipPolicy extends BasePolicy { } @Component({ - selector: "policy-organization-data-ownership", templateUrl: "organization-data-ownership.component.html", - standalone: false, + imports: [SharedModule], }) -export class OrganizationDataOwnershipPolicyComponent extends BasePolicyComponent {} +export class OrganizationDataOwnershipPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/password-generator.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/password-generator.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/password-generator.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/password-generator.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/password-generator.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/password-generator.component.ts similarity index 94% rename from apps/web/src/app/admin-console/organizations/policies/password-generator.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/password-generator.component.ts index 26f87f333eb..e26d37bfdf2 100644 --- a/apps/web/src/app/admin-console/organizations/policies/password-generator.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/password-generator.component.ts @@ -9,9 +9,10 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { BuiltIn, Profile } from "@bitwarden/generator-core"; -import { BasePolicy, BasePolicyComponent } from "./base-policy.component"; +import { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; -export class PasswordGeneratorPolicy extends BasePolicy { +export class PasswordGeneratorPolicy extends BasePolicyEditDefinition { name = "passwordGenerator"; description = "passwordGeneratorPolicyDesc"; type = PolicyType.PasswordGenerator; @@ -19,11 +20,10 @@ export class PasswordGeneratorPolicy extends BasePolicy { } @Component({ - selector: "policy-password-generator", templateUrl: "password-generator.component.html", - standalone: false, + imports: [SharedModule], }) -export class PasswordGeneratorPolicyComponent extends BasePolicyComponent { +export class PasswordGeneratorPolicyComponent extends BasePolicyEditComponent { // these properties forward the application default settings to the UI // for HTML attribute bindings protected readonly minLengthMin = diff --git a/apps/web/src/app/admin-console/organizations/policies/remove-unlock-with-pin.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/remove-unlock-with-pin.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/remove-unlock-with-pin.component.spec.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.spec.ts similarity index 86% rename from apps/web/src/app/admin-console/organizations/policies/remove-unlock-with-pin.component.spec.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.spec.ts index 9058cd22f3e..f6df56cd83a 100644 --- a/apps/web/src/app/admin-console/organizations/policies/remove-unlock-with-pin.component.spec.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.spec.ts @@ -1,11 +1,8 @@ -import { CommonModule } from "@angular/common"; import { NO_ERRORS_SCHEMA } from "@angular/core"; import { ComponentFixture, TestBed } from "@angular/core/testing"; -import { ReactiveFormsModule } from "@angular/forms"; import { By } from "@angular/platform-browser"; import { mock } from "jest-mock-extended"; -import { I18nPipe } from "@bitwarden/angular/platform/pipes/i18n.pipe"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; @@ -33,8 +30,6 @@ describe("RemoveUnlockWithPinPolicyComponent", () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [CommonModule, ReactiveFormsModule], - declarations: [RemoveUnlockWithPinPolicyComponent, I18nPipe], providers: [ { provide: I18nService, useValue: mock() }, { provide: I18nService, useValue: i18nService }, @@ -102,9 +97,6 @@ describe("RemoveUnlockWithPinPolicyComponent", () => { const bitLabelElement = fixture.debugElement.query(By.css("bit-label")); expect(bitLabelElement).not.toBeNull(); - const textNodes = bitLabelElement.childNodes - .filter((node) => node.nativeNode.nodeType === Node.TEXT_NODE) - .map((node) => node.nativeNode.wholeText?.trim()); - expect(textNodes).toContain("Turn on"); + expect(bitLabelElement.nativeElement.textContent.trim()).toBe("Turn on"); }); }); diff --git a/apps/web/src/app/admin-console/organizations/policies/remove-unlock-with-pin.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.ts similarity index 61% rename from apps/web/src/app/admin-console/organizations/policies/remove-unlock-with-pin.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.ts index 0d0f42b603e..e95ef8a1422 100644 --- a/apps/web/src/app/admin-console/organizations/policies/remove-unlock-with-pin.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/remove-unlock-with-pin.component.ts @@ -2,9 +2,10 @@ import { Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; -import { BasePolicy, BasePolicyComponent } from "./base-policy.component"; +import { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; -export class RemoveUnlockWithPinPolicy extends BasePolicy { +export class RemoveUnlockWithPinPolicy extends BasePolicyEditDefinition { name = "removeUnlockWithPinPolicyTitle"; description = "removeUnlockWithPinPolicyDesc"; type = PolicyType.RemoveUnlockWithPin; @@ -12,8 +13,7 @@ export class RemoveUnlockWithPinPolicy extends BasePolicy { } @Component({ - selector: "remove-unlock-with-pin", templateUrl: "remove-unlock-with-pin.component.html", - standalone: false, + imports: [SharedModule], }) -export class RemoveUnlockWithPinPolicyComponent extends BasePolicyComponent {} +export class RemoveUnlockWithPinPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/require-sso.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/require-sso.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/require-sso.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/require-sso.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/require-sso.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/require-sso.component.ts similarity index 59% rename from apps/web/src/app/admin-console/organizations/policies/require-sso.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/require-sso.component.ts index 3a0d196c593..3f28c0cb068 100644 --- a/apps/web/src/app/admin-console/organizations/policies/require-sso.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/require-sso.component.ts @@ -5,22 +5,22 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; 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 { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; -export class RequireSsoPolicy extends BasePolicy { +export class RequireSsoPolicy extends BasePolicyEditDefinition { name = "requireSso"; description = "requireSsoPolicyDesc"; type = PolicyType.RequireSso; component = RequireSsoPolicyComponent; - display(organization: Organization, configService: ConfigService) { + display$(organization: Organization, configService: ConfigService) { return of(organization.useSso); } } @Component({ - selector: "policy-require-sso", templateUrl: "require-sso.component.html", - standalone: false, + imports: [SharedModule], }) -export class RequireSsoPolicyComponent extends BasePolicyComponent {} +export class RequireSsoPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/reset-password.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/reset-password.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/reset-password.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/reset-password.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/reset-password.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/reset-password.component.ts similarity index 82% rename from apps/web/src/app/admin-console/organizations/policies/reset-password.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/reset-password.component.ts index 93a42285fbc..fafb0b32398 100644 --- a/apps/web/src/app/admin-console/organizations/policies/reset-password.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/reset-password.component.ts @@ -12,25 +12,25 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv 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 { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; -export class ResetPasswordPolicy extends BasePolicy { +export class ResetPasswordPolicy extends BasePolicyEditDefinition { name = "accountRecoveryPolicy"; description = "accountRecoveryPolicyDesc"; type = PolicyType.ResetPassword; component = ResetPasswordPolicyComponent; - display(organization: Organization, configService: ConfigService) { + display$(organization: Organization, configService: ConfigService) { return of(organization.useResetPassword); } } @Component({ - selector: "policy-reset-password", templateUrl: "reset-password.component.html", - standalone: false, + imports: [SharedModule], }) -export class ResetPasswordPolicyComponent extends BasePolicyComponent implements OnInit { +export class ResetPasswordPolicyComponent extends BasePolicyEditComponent implements OnInit { data = this.formBuilder.group({ autoEnrollEnabled: false, }); diff --git a/apps/web/src/app/admin-console/organizations/policies/restricted-item-types.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/restricted-item-types.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/restricted-item-types.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts similarity index 70% rename from apps/web/src/app/admin-console/organizations/policies/restricted-item-types.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts index 6cad0fc0170..51cba689693 100644 --- a/apps/web/src/app/admin-console/organizations/policies/restricted-item-types.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/restricted-item-types.component.ts @@ -6,25 +6,25 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga 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 { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; -export class RestrictedItemTypesPolicy extends BasePolicy { +export class RestrictedItemTypesPolicy extends BasePolicyEditDefinition { name = "restrictedItemTypePolicy"; description = "restrictedItemTypePolicyDesc"; type = PolicyType.RestrictedItemTypes; component = RestrictedItemTypesPolicyComponent; - display(organization: Organization, configService: ConfigService): Observable { + display$(organization: Organization, configService: ConfigService): Observable { return configService.getFeatureFlag$(FeatureFlag.RemoveCardItemTypePolicy); } } @Component({ - selector: "policy-restricted-item-types", templateUrl: "restricted-item-types.component.html", - standalone: false, + imports: [SharedModule], }) -export class RestrictedItemTypesPolicyComponent extends BasePolicyComponent { +export class RestrictedItemTypesPolicyComponent extends BasePolicyEditComponent { constructor() { super(); } diff --git a/apps/web/src/app/admin-console/organizations/policies/send-options.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/send-options.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/send-options.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/send-options.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/send-options.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/send-options.component.ts similarity index 62% rename from apps/web/src/app/admin-console/organizations/policies/send-options.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/send-options.component.ts index 9a0a8871296..e581ed2f4c7 100644 --- a/apps/web/src/app/admin-console/organizations/policies/send-options.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/send-options.component.ts @@ -3,9 +3,10 @@ import { UntypedFormBuilder } from "@angular/forms"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; -import { BasePolicy, BasePolicyComponent } from "./base-policy.component"; +import { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; -export class SendOptionsPolicy extends BasePolicy { +export class SendOptionsPolicy extends BasePolicyEditDefinition { name = "sendOptions"; description = "sendOptionsPolicyDesc"; type = PolicyType.SendOptions; @@ -13,11 +14,10 @@ export class SendOptionsPolicy extends BasePolicy { } @Component({ - selector: "policy-send-options", templateUrl: "send-options.component.html", - standalone: false, + imports: [SharedModule], }) -export class SendOptionsPolicyComponent extends BasePolicyComponent { +export class SendOptionsPolicyComponent extends BasePolicyEditComponent { data = this.formBuilder.group({ disableHideEmail: false, }); diff --git a/apps/web/src/app/admin-console/organizations/policies/single-org.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/single-org.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/single-org.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/single-org.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/single-org.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/single-org.component.ts similarity index 61% rename from apps/web/src/app/admin-console/organizations/policies/single-org.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/single-org.component.ts index 613253ef8d9..ecaa86b03bc 100644 --- a/apps/web/src/app/admin-console/organizations/policies/single-org.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/single-org.component.ts @@ -2,9 +2,10 @@ import { Component, OnInit } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; -import { BasePolicy, BasePolicyComponent } from "./base-policy.component"; +import { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; -export class SingleOrgPolicy extends BasePolicy { +export class SingleOrgPolicy extends BasePolicyEditDefinition { name = "singleOrg"; description = "singleOrgPolicyDesc"; type = PolicyType.SingleOrg; @@ -12,11 +13,10 @@ export class SingleOrgPolicy extends BasePolicy { } @Component({ - selector: "policy-single-org", templateUrl: "single-org.component.html", - standalone: false, + imports: [SharedModule], }) -export class SingleOrgPolicyComponent extends BasePolicyComponent implements OnInit { +export class SingleOrgPolicyComponent extends BasePolicyEditComponent implements OnInit { async ngOnInit() { super.ngOnInit(); diff --git a/apps/web/src/app/admin-console/organizations/policies/two-factor-authentication.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/two-factor-authentication.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/two-factor-authentication.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/two-factor-authentication.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/two-factor-authentication.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/two-factor-authentication.component.ts similarity index 60% rename from apps/web/src/app/admin-console/organizations/policies/two-factor-authentication.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/two-factor-authentication.component.ts index 691e12c72f6..13b7660c4e7 100644 --- a/apps/web/src/app/admin-console/organizations/policies/two-factor-authentication.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/two-factor-authentication.component.ts @@ -2,9 +2,10 @@ import { Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; -import { BasePolicy, BasePolicyComponent } from "./base-policy.component"; +import { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; -export class TwoFactorAuthenticationPolicy extends BasePolicy { +export class TwoFactorAuthenticationPolicy extends BasePolicyEditDefinition { name = "twoStepLoginPolicyTitle"; description = "twoStepLoginPolicyDesc"; type = PolicyType.TwoFactorAuthentication; @@ -12,8 +13,7 @@ export class TwoFactorAuthenticationPolicy extends BasePolicy { } @Component({ - selector: "policy-two-factor-authentication", templateUrl: "two-factor-authentication.component.html", - standalone: false, + imports: [SharedModule], }) -export class TwoFactorAuthenticationPolicyComponent extends BasePolicyComponent {} +export class TwoFactorAuthenticationPolicyComponent extends BasePolicyEditComponent {} diff --git a/apps/web/src/app/admin-console/organizations/policies/vnext-organization-data-ownership.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/vnext-organization-data-ownership.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/vnext-organization-data-ownership.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts similarity index 88% rename from apps/web/src/app/admin-console/organizations/policies/vnext-organization-data-ownership.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts index 89964e94395..2234d5c7437 100644 --- a/apps/web/src/app/admin-console/organizations/policies/vnext-organization-data-ownership.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts @@ -12,9 +12,8 @@ import { OrgKey } from "@bitwarden/common/types/key"; import { DialogService } from "@bitwarden/components"; import { EncString } from "@bitwarden/sdk-internal"; -import { SharedModule } from "../../../shared"; - -import { BasePolicy, BasePolicyComponent } from "./base-policy.component"; +import { SharedModule } from "../../../../shared"; +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; interface VNextPolicyRequest { policy: PolicyRequest; @@ -23,26 +22,24 @@ interface VNextPolicyRequest { }; } -export class vNextOrganizationDataOwnershipPolicy extends BasePolicy { +export class vNextOrganizationDataOwnershipPolicy extends BasePolicyEditDefinition { name = "organizationDataOwnership"; description = "organizationDataOwnershipDesc"; type = PolicyType.OrganizationDataOwnership; component = vNextOrganizationDataOwnershipPolicyComponent; showDescription = false; - override display(organization: Organization, configService: ConfigService): Observable { + override display$(organization: Organization, configService: ConfigService): Observable { 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 + extends BasePolicyEditComponent implements OnInit { constructor( @@ -74,7 +71,7 @@ export class vNextOrganizationDataOwnershipPolicyComponent const request: VNextPolicyRequest = { policy: { type: this.policy.type, - enabled: this.enabled.value, + enabled: this.enabled.value ?? false, data: this.buildRequestData(), }, metadata: { diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.html b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialog.component.html similarity index 100% rename from apps/web/src/app/admin-console/organizations/policies/policy-edit.component.html rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-dialog.component.html diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialog.component.ts similarity index 67% rename from apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts rename to apps/web/src/app/admin-console/organizations/policies/policy-edit-dialog.component.ts index ad1099af3cd..f0672d0f861 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialog.component.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { AfterViewInit, ChangeDetectorRef, @@ -9,7 +7,7 @@ import { ViewContainerRef, } from "@angular/core"; import { FormBuilder } from "@angular/forms"; -import { Observable, map, firstValueFrom, switchMap } from "rxjs"; +import { Observable, map, firstValueFrom, switchMap, filter, of } from "rxjs"; import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abstractions/policy/policy-api.service.abstraction"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; @@ -29,37 +27,38 @@ import { } from "@bitwarden/components"; import { KeyService } from "@bitwarden/key-management"; -import { BasePolicy, BasePolicyComponent } from "../policies"; -import { vNextOrganizationDataOwnershipPolicyComponent } from "../policies/vnext-organization-data-ownership.component"; +import { SharedModule } from "../../../shared"; + +import { BasePolicyEditDefinition, BasePolicyEditComponent } from "./base-policy-edit.component"; +import { vNextOrganizationDataOwnershipPolicyComponent } from "./policy-edit-definitions"; export type PolicyEditDialogData = { - /** Returns policy abstracts. */ - policy: BasePolicy; - /** Returns a unique organization id */ + /** + * The metadata containing information about how to display and edit the policy. + */ + policy: BasePolicyEditDefinition; + /** + * The organization ID for the policy. + */ organizationId: string; }; -// FIXME: update to use a const object instead of a typescript enum -// eslint-disable-next-line @bitwarden/platform/no-enums -export enum PolicyEditDialogResult { - Saved = "saved", -} +export type PolicyEditDialogResult = "saved"; + @Component({ - selector: "app-policy-edit", - templateUrl: "policy-edit.component.html", - standalone: false, + templateUrl: "policy-edit-dialog.component.html", + imports: [SharedModule], }) -export class PolicyEditComponent implements AfterViewInit { +export class PolicyEditDialogComponent implements AfterViewInit { @ViewChild("policyForm", { read: ViewContainerRef, static: true }) - policyFormRef: ViewContainerRef; + policyFormRef: ViewContainerRef | undefined; policyType = PolicyType; loading = true; enabled = false; - saveDisabled$: Observable; - policyComponent: BasePolicyComponent; + saveDisabled$: Observable = of(false); + policyComponent: BasePolicyEditComponent | undefined; - private policyResponse: PolicyResponse; formGroup = this.formBuilder.group({ enabled: [this.enabled], }); @@ -75,35 +74,43 @@ export class PolicyEditComponent implements AfterViewInit { private configService: ConfigService, private keyService: KeyService, ) {} - get policy(): BasePolicy { + + get policy(): BasePolicyEditDefinition { return this.data.policy; } + /** + * Instantiates the child policy component and inserts it into the view. + */ async ngAfterViewInit() { - await this.load(); + const policyResponse = await this.load(); this.loading = false; - this.policyComponent = this.policyFormRef.createComponent(this.data.policy.component) - .instance as BasePolicyComponent; - this.policyComponent.policy = this.data.policy; - this.policyComponent.policyResponse = this.policyResponse; + if (!this.policyFormRef) { + throw new Error("Template not initialized."); + } - this.saveDisabled$ = this.policyComponent.data.statusChanges.pipe( - map((status) => status !== "VALID" || !this.policyResponse.canToggleState), - ); + this.policyComponent = this.policyFormRef.createComponent(this.data.policy.component).instance; + this.policyComponent.policy = this.data.policy; + this.policyComponent.policyResponse = policyResponse; + + if (this.policyComponent.data) { + // If the policy has additional configuration, disable the save button if the form state is invalid + this.saveDisabled$ = this.policyComponent.data.statusChanges.pipe( + map((status) => status !== "VALID" || !policyResponse.canToggleState), + ); + } this.cdr.detectChanges(); } async load() { try { - this.policyResponse = await this.policyApiService.getPolicy( - this.data.organizationId, - this.data.policy.type, - ); - } catch (e) { + return await this.policyApiService.getPolicy(this.data.organizationId, this.data.policy.type); + } catch (e: any) { + // No policy exists yet, instantiate an empty one if (e.statusCode === 404) { - this.policyResponse = new PolicyResponse({ Enabled: false }); + return new PolicyResponse({ Enabled: false }); } else { throw e; } @@ -111,6 +118,10 @@ export class PolicyEditComponent implements AfterViewInit { } submit = async () => { + if (!this.policyComponent) { + throw new Error("PolicyComponent not initialized."); + } + if ((await this.policyComponent.confirm()) == false) { this.dialogRef.close(); return; @@ -128,14 +139,12 @@ export class PolicyEditComponent implements AfterViewInit { this.toastService.showToast({ variant: "success", - title: null, message: this.i18nService.t("editedPolicyId", this.i18nService.t(this.data.policy.name)), }); - this.dialogRef.close(PolicyEditDialogResult.Saved); - } catch (error) { + this.dialogRef.close("saved"); + } catch (error: any) { this.toastService.showToast({ variant: "error", - title: null, message: error.message, }); } @@ -150,6 +159,10 @@ export class PolicyEditComponent implements AfterViewInit { } private async handleStandardSubmission(): Promise { + if (!this.policyComponent) { + throw new Error("PolicyComponent not initialized."); + } + const request = await this.policyComponent.buildRequest(); await this.policyApiService.putPolicy(this.data.organizationId, this.data.policy.type, request); } @@ -161,10 +174,8 @@ export class PolicyEditComponent implements AfterViewInit { this.accountService.activeAccount$.pipe( getUserId, switchMap((userId) => this.keyService.orgKeys$(userId)), - map( - (orgKeys: { [key: OrganizationId]: any }) => - orgKeys[this.data.organizationId as OrganizationId] ?? null, - ), + filter((orgKeys) => orgKeys != null), + map((orgKeys) => orgKeys[this.data.organizationId as OrganizationId] ?? null), ), ); @@ -181,6 +192,6 @@ export class PolicyEditComponent implements AfterViewInit { ); } static open = (dialogService: DialogService, config: DialogConfig) => { - return dialogService.open(PolicyEditComponent, config); + return dialogService.open(PolicyEditDialogComponent, config); }; } diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-register.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-register.ts new file mode 100644 index 00000000000..3a4ba9a710f --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-register.ts @@ -0,0 +1,34 @@ +import { BasePolicyEditDefinition } from "./base-policy-edit.component"; +import { + DisableSendPolicy, + MasterPasswordPolicy, + OrganizationDataOwnershipPolicy, + PasswordGeneratorPolicy, + RemoveUnlockWithPinPolicy, + RequireSsoPolicy, + ResetPasswordPolicy, + RestrictedItemTypesPolicy, + SendOptionsPolicy, + SingleOrgPolicy, + TwoFactorAuthenticationPolicy, + vNextOrganizationDataOwnershipPolicy, +} from "./policy-edit-definitions"; + +/** + * The policy register for OSS policies. + * Add your policy definition here if it is under the OSS license. + */ +export const ossPolicyEditRegister: BasePolicyEditDefinition[] = [ + new TwoFactorAuthenticationPolicy(), + new MasterPasswordPolicy(), + new RemoveUnlockWithPinPolicy(), + new ResetPasswordPolicy(), + new PasswordGeneratorPolicy(), + new SingleOrgPolicy(), + new RequireSsoPolicy(), + new OrganizationDataOwnershipPolicy(), + new vNextOrganizationDataOwnershipPolicy(), + new DisableSendPolicy(), + new SendOptionsPolicy(), + new RestrictedItemTypesPolicy(), +]; diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-list.service.ts b/apps/web/src/app/admin-console/organizations/policies/policy-list.service.ts new file mode 100644 index 00000000000..0434338f2f0 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/policy-list.service.ts @@ -0,0 +1,13 @@ +import { BasePolicyEditDefinition } from "./base-policy-edit.component"; + +export class PolicyListService { + private policies: readonly BasePolicyEditDefinition[]; + + constructor(policies: BasePolicyEditDefinition[]) { + this.policies = Object.freeze([...policies]); + } + + getPolicies() { + return this.policies; + } +} diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-register-token.ts b/apps/web/src/app/admin-console/organizations/policies/policy-register-token.ts new file mode 100644 index 00000000000..3c46af32e88 --- /dev/null +++ b/apps/web/src/app/admin-console/organizations/policies/policy-register-token.ts @@ -0,0 +1,7 @@ +import { SafeInjectionToken } from "@bitwarden/ui-common"; + +import { BasePolicyEditDefinition } from "./base-policy-edit.component"; + +export const POLICY_EDIT_REGISTER = new SafeInjectionToken( + "POLICY_EDIT_REGISTER", +); diff --git a/apps/web/src/app/admin-console/organizations/settings/organization-settings.module.ts b/apps/web/src/app/admin-console/organizations/settings/organization-settings.module.ts index 7065e8e799c..27a6226f964 100644 --- a/apps/web/src/app/admin-console/organizations/settings/organization-settings.module.ts +++ b/apps/web/src/app/admin-console/organizations/settings/organization-settings.module.ts @@ -7,7 +7,6 @@ import { DangerZoneComponent } from "../../../auth/settings/account/danger-zone. import { HeaderModule } from "../../../layouts/header/header.module"; import { SharedModule } from "../../../shared"; import { AccountFingerprintComponent } from "../../../shared/components/account-fingerprint/account-fingerprint.component"; -import { PoliciesModule } from "../../organizations/policies"; import { AccountComponent } from "./account.component"; import { OrganizationSettingsRoutingModule } from "./organization-settings-routing.module"; @@ -16,7 +15,6 @@ import { TwoFactorSetupComponent } from "./two-factor-setup.component"; @NgModule({ imports: [ SharedModule, - PoliciesModule, OrganizationSettingsRoutingModule, AccountFingerprintComponent, DangerZoneComponent, diff --git a/apps/web/src/app/app.component.ts b/apps/web/src/app/app.component.ts index 1cb95250611..60911173308 100644 --- a/apps/web/src/app/app.component.ts +++ b/apps/web/src/app/app.component.ts @@ -30,22 +30,6 @@ import { SearchService } from "@bitwarden/common/vault/abstractions/search.servi import { DialogService, ToastService } from "@bitwarden/components"; import { KeyService, BiometricStateService } from "@bitwarden/key-management"; -import { PolicyListService } from "./admin-console/core/policy-list.service"; -import { - DisableSendPolicy, - MasterPasswordPolicy, - PasswordGeneratorPolicy, - OrganizationDataOwnershipPolicy, - vNextOrganizationDataOwnershipPolicy, - RequireSsoPolicy, - ResetPasswordPolicy, - SendOptionsPolicy, - SingleOrgPolicy, - TwoFactorAuthenticationPolicy, - RemoveUnlockWithPinPolicy, - RestrictedItemTypesPolicy, -} from "./admin-console/organizations/policies"; - const BroadcasterSubscriptionId = "AppComponent"; const IdleTimeout = 60000 * 10; // 10 minutes @@ -79,7 +63,6 @@ export class AppComponent implements OnDestroy, OnInit { private serverNotificationsService: ServerNotificationsService, private stateService: StateService, private eventUploadService: EventUploadService, - protected policyListService: PolicyListService, protected configService: ConfigService, private dialogService: DialogService, private biometricStateService: BiometricStateService, @@ -238,21 +221,6 @@ export class AppComponent implements OnDestroy, OnInit { } }); }); - - this.policyListService.addPolicies([ - new TwoFactorAuthenticationPolicy(), - new MasterPasswordPolicy(), - new RemoveUnlockWithPinPolicy(), - new ResetPasswordPolicy(), - new PasswordGeneratorPolicy(), - new SingleOrgPolicy(), - new RequireSsoPolicy(), - new OrganizationDataOwnershipPolicy(), - new vNextOrganizationDataOwnershipPolicy(), - new DisableSendPolicy(), - new SendOptionsPolicy(), - new RestrictedItemTypesPolicy(), - ]); } ngOnDestroy() { diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index 22386144732..b3ce39d5021 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -113,7 +113,10 @@ import { DefaultSshImportPromptService, SshImportPromptService } from "@bitwarde import { WebOrganizationInviteService } from "@bitwarden/web-vault/app/auth/core/services/organization-invite/web-organization-invite.service"; import { flagEnabled } from "../../utils/flags"; -import { PolicyListService } from "../admin-console/core/policy-list.service"; +import { + POLICY_EDIT_REGISTER, + ossPolicyEditRegister, +} from "../admin-console/organizations/policies"; import { WebChangePasswordService, WebRegistrationFinishService, @@ -152,7 +155,10 @@ const safeProviders: SafeProvider[] = [ safeProvider(InitService), safeProvider(RouterService), safeProvider(EventService), - safeProvider(PolicyListService), + safeProvider({ + provide: POLICY_EDIT_REGISTER, + useValue: ossPolicyEditRegister, + }), safeProvider({ provide: DEFAULT_VAULT_TIMEOUT, deps: [PlatformUtilsService], diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/index.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/index.ts new file mode 100644 index 00000000000..c2cd83f838c --- /dev/null +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/index.ts @@ -0,0 +1 @@ +export { bitPolicyEditRegister } from "./policy-edit-register"; diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/activate-autofill.component.html b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/activate-autofill.component.html similarity index 100% rename from bitwarden_license/bit-web/src/app/admin-console/policies/activate-autofill.component.html rename to bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/activate-autofill.component.html diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/activate-autofill.component.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/activate-autofill.component.ts similarity index 71% rename from bitwarden_license/bit-web/src/app/admin-console/policies/activate-autofill.component.ts rename to bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/activate-autofill.component.ts index 821509b43e2..17efc017136 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/policies/activate-autofill.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/activate-autofill.component.ts @@ -5,11 +5,12 @@ import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { Organization } from "@bitwarden/common/admin-console/models/domain/organization"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { - BasePolicy, - BasePolicyComponent, -} from "@bitwarden/web-vault/app/admin-console/organizations/policies/base-policy.component"; + BasePolicyEditDefinition, + BasePolicyEditComponent, +} from "@bitwarden/web-vault/app/admin-console/organizations/policies"; +import { SharedModule } from "@bitwarden/web-vault/app/shared"; -export class ActivateAutofillPolicy extends BasePolicy { +export class ActivateAutofillPolicy extends BasePolicyEditDefinition { name = "activateAutofill"; description = "activateAutofillPolicyDesc"; type = PolicyType.ActivateAutofill; @@ -21,8 +22,7 @@ export class ActivateAutofillPolicy extends BasePolicy { } @Component({ - selector: "policy-activate-autofill", templateUrl: "activate-autofill.component.html", - standalone: false, + imports: [SharedModule], }) -export class ActivateAutofillPolicyComponent extends BasePolicyComponent {} +export class ActivateAutofillPolicyComponent extends BasePolicyEditComponent {} diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/automatic-app-login.component.html b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/automatic-app-login.component.html similarity index 100% rename from bitwarden_license/bit-web/src/app/admin-console/policies/automatic-app-login.component.html rename to bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/automatic-app-login.component.html diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/automatic-app-login.component.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/automatic-app-login.component.ts similarity index 76% rename from bitwarden_license/bit-web/src/app/admin-console/policies/automatic-app-login.component.ts rename to bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/automatic-app-login.component.ts index 1a478fb4393..7dadc04c6f4 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/policies/automatic-app-login.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/automatic-app-login.component.ts @@ -5,11 +5,12 @@ import { FormBuilder, FormControl } from "@angular/forms"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { - BasePolicy, - BasePolicyComponent, -} from "@bitwarden/web-vault/app/admin-console/organizations/policies/base-policy.component"; + BasePolicyEditDefinition, + BasePolicyEditComponent, +} from "@bitwarden/web-vault/app/admin-console/organizations/policies"; +import { SharedModule } from "@bitwarden/web-vault/app/shared"; -export class AutomaticAppLoginPolicy extends BasePolicy { +export class AutomaticAppLoginPolicy extends BasePolicyEditDefinition { name = "automaticAppLogin"; description = "automaticAppLoginDesc"; type = PolicyType.AutomaticAppLogIn; @@ -17,11 +18,10 @@ export class AutomaticAppLoginPolicy extends BasePolicy { } @Component({ - selector: "policy-automatic-app-login", templateUrl: "automatic-app-login.component.html", - standalone: false, + imports: [SharedModule], }) -export class AutomaticAppLoginPolicyComponent extends BasePolicyComponent { +export class AutomaticAppLoginPolicyComponent extends BasePolicyEditComponent { data = this.formBuilder.group({ idpHost: new FormControl(null), }); diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/disable-personal-vault-export.component.html b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/disable-personal-vault-export.component.html similarity index 100% rename from bitwarden_license/bit-web/src/app/admin-console/policies/disable-personal-vault-export.component.html rename to bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/disable-personal-vault-export.component.html diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/disable-personal-vault-export.component.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/disable-personal-vault-export.component.ts similarity index 75% rename from bitwarden_license/bit-web/src/app/admin-console/policies/disable-personal-vault-export.component.ts rename to bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/disable-personal-vault-export.component.ts index c274e58ccac..d93fb50b0e2 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/policies/disable-personal-vault-export.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/disable-personal-vault-export.component.ts @@ -2,11 +2,12 @@ import { Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { - BasePolicy, - BasePolicyComponent, -} from "@bitwarden/web-vault/app/admin-console/organizations/policies/base-policy.component"; + BasePolicyEditDefinition, + BasePolicyEditComponent, +} from "@bitwarden/web-vault/app/admin-console/organizations/policies"; +import { SharedModule } from "@bitwarden/web-vault/app/shared"; -export class DisablePersonalVaultExportPolicy extends BasePolicy { +export class DisablePersonalVaultExportPolicy extends BasePolicyEditDefinition { name = "disablePersonalVaultExport"; description = "disablePersonalVaultExportDescription"; type = PolicyType.DisablePersonalVaultExport; @@ -14,8 +15,7 @@ export class DisablePersonalVaultExportPolicy extends BasePolicy { } @Component({ - selector: "policy-disable-personal-vault-export", templateUrl: "disable-personal-vault-export.component.html", - standalone: false, + imports: [SharedModule], }) -export class DisablePersonalVaultExportPolicyComponent extends BasePolicyComponent {} +export class DisablePersonalVaultExportPolicyComponent extends BasePolicyEditComponent {} diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/index.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/index.ts new file mode 100644 index 00000000000..8c4be2eeea1 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/index.ts @@ -0,0 +1,4 @@ +export { ActivateAutofillPolicy } from "./activate-autofill.component"; +export { AutomaticAppLoginPolicy } from "./automatic-app-login.component"; +export { DisablePersonalVaultExportPolicy } from "./disable-personal-vault-export.component"; +export { MaximumVaultTimeoutPolicy } from "./maximum-vault-timeout.component"; diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/maximum-vault-timeout.component.html b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/maximum-vault-timeout.component.html similarity index 100% rename from bitwarden_license/bit-web/src/app/admin-console/policies/maximum-vault-timeout.component.html rename to bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/maximum-vault-timeout.component.html diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/maximum-vault-timeout.component.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/maximum-vault-timeout.component.ts similarity index 90% rename from bitwarden_license/bit-web/src/app/admin-console/policies/maximum-vault-timeout.component.ts rename to bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/maximum-vault-timeout.component.ts index a5b9ad47f6e..160ce9aeb20 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/policies/maximum-vault-timeout.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-definitions/maximum-vault-timeout.component.ts @@ -8,11 +8,12 @@ import { PolicyRequest } from "@bitwarden/common/admin-console/models/request/po import { VaultTimeoutAction } from "@bitwarden/common/key-management/vault-timeout"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { - BasePolicy, - BasePolicyComponent, -} from "@bitwarden/web-vault/app/admin-console/organizations/policies/base-policy.component"; + BasePolicyEditDefinition, + BasePolicyEditComponent, +} from "@bitwarden/web-vault/app/admin-console/organizations/policies"; +import { SharedModule } from "@bitwarden/web-vault/app/shared"; -export class MaximumVaultTimeoutPolicy extends BasePolicy { +export class MaximumVaultTimeoutPolicy extends BasePolicyEditDefinition { name = "maximumVaultTimeout"; description = "maximumVaultTimeoutDesc"; type = PolicyType.MaximumVaultTimeout; @@ -20,11 +21,10 @@ export class MaximumVaultTimeoutPolicy extends BasePolicy { } @Component({ - selector: "policy-maximum-timeout", templateUrl: "maximum-vault-timeout.component.html", - standalone: false, + imports: [SharedModule], }) -export class MaximumVaultTimeoutPolicyComponent extends BasePolicyComponent { +export class MaximumVaultTimeoutPolicyComponent extends BasePolicyEditComponent { vaultTimeoutActionOptions: { name: string; value: string }[]; data = this.formBuilder.group({ hours: new FormControl(null), diff --git a/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-register.ts b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-register.ts new file mode 100644 index 00000000000..3438e706f10 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/admin-console/policies/policy-edit-register.ts @@ -0,0 +1,28 @@ +import { + ossPolicyEditRegister, + BasePolicyEditDefinition, +} from "@bitwarden/web-vault/app/admin-console/organizations/policies"; + +import { FreeFamiliesSponsorshipPolicy } from "../../billing/policies/free-families-sponsorship.component"; + +import { + ActivateAutofillPolicy, + AutomaticAppLoginPolicy, + DisablePersonalVaultExportPolicy, + MaximumVaultTimeoutPolicy, +} from "./policy-edit-definitions"; + +/** + * The policy register for Bitwarden Licensed policies. + * Add your policy definition here if it is under the Bitwarden License. + * It will not appear in the web vault when running in OSS mode. + */ +const policyEditRegister: BasePolicyEditDefinition[] = [ + new MaximumVaultTimeoutPolicy(), + new DisablePersonalVaultExportPolicy(), + new FreeFamiliesSponsorshipPolicy(), + new ActivateAutofillPolicy(), + new AutomaticAppLoginPolicy(), +]; + +export const bitPolicyEditRegister = ossPolicyEditRegister.concat(policyEditRegister); diff --git a/bitwarden_license/bit-web/src/app/app.component.ts b/bitwarden_license/bit-web/src/app/app.component.ts index ca6a5ea8f62..abfb79b8f18 100644 --- a/bitwarden_license/bit-web/src/app/app.component.ts +++ b/bitwarden_license/bit-web/src/app/app.component.ts @@ -1,31 +1,10 @@ -import { Component, OnInit } from "@angular/core"; +import { Component } from "@angular/core"; import { AppComponent as BaseAppComponent } from "@bitwarden/web-vault/app/app.component"; -import { ActivateAutofillPolicy } from "./admin-console/policies/activate-autofill.component"; -import { AutomaticAppLoginPolicy } from "./admin-console/policies/automatic-app-login.component"; -import { DisablePersonalVaultExportPolicy } from "./admin-console/policies/disable-personal-vault-export.component"; -import { MaximumVaultTimeoutPolicy } from "./admin-console/policies/maximum-vault-timeout.component"; -import { FreeFamiliesSponsorshipPolicy } from "./billing/policies/free-families-sponsorship.component"; - @Component({ selector: "app-root", templateUrl: "../../../../apps/web/src/app/app.component.html", standalone: false, }) -export class AppComponent extends BaseAppComponent implements OnInit { - ngOnInit() { - super.ngOnInit(); - - this.policyListService.addPolicies([ - new MaximumVaultTimeoutPolicy(), - new DisablePersonalVaultExportPolicy(), - new FreeFamiliesSponsorshipPolicy(), - new ActivateAutofillPolicy(), - ]); - - if (!this.policyListService.getPolicies().some((p) => p instanceof AutomaticAppLoginPolicy)) { - this.policyListService.addPolicies([new AutomaticAppLoginPolicy()]); - } - } -} +export class AppComponent extends BaseAppComponent {} diff --git a/bitwarden_license/bit-web/src/app/app.module.ts b/bitwarden_license/bit-web/src/app/app.module.ts index b665dee3b17..e9696a4549f 100644 --- a/bitwarden_license/bit-web/src/app/app.module.ts +++ b/bitwarden_license/bit-web/src/app/app.module.ts @@ -6,19 +6,17 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { RouterModule } from "@angular/router"; import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { safeProvider } from "@bitwarden/ui-common"; +import { POLICY_EDIT_REGISTER } from "@bitwarden/web-vault/app/admin-console/organizations/policies"; import { CoreModule } from "@bitwarden/web-vault/app/core"; import { OssRoutingModule } from "@bitwarden/web-vault/app/oss-routing.module"; import { OssModule } from "@bitwarden/web-vault/app/oss.module"; import { WildcardRoutingModule } from "@bitwarden/web-vault/app/wildcard-routing.module"; import { OrganizationsModule } from "./admin-console/organizations/organizations.module"; -import { ActivateAutofillPolicyComponent } from "./admin-console/policies/activate-autofill.component"; -import { AutomaticAppLoginPolicyComponent } from "./admin-console/policies/automatic-app-login.component"; -import { DisablePersonalVaultExportPolicyComponent } from "./admin-console/policies/disable-personal-vault-export.component"; -import { MaximumVaultTimeoutPolicyComponent } from "./admin-console/policies/maximum-vault-timeout.component"; +import { bitPolicyEditRegister } from "./admin-console/policies"; import { AppRoutingModule } from "./app-routing.module"; import { AppComponent } from "./app.component"; -import { FreeFamiliesSponsorshipPolicyComponent } from "./billing/policies/free-families-sponsorship.component"; import { AccessIntelligenceModule } from "./dirt/access-intelligence/access-intelligence.module"; /** @@ -44,14 +42,13 @@ import { AccessIntelligenceModule } from "./dirt/access-intelligence/access-inte RouterModule, WildcardRoutingModule, // Needs to be last to catch all non-existing routes ], - declarations: [ - AppComponent, - DisablePersonalVaultExportPolicyComponent, - MaximumVaultTimeoutPolicyComponent, - ActivateAutofillPolicyComponent, - AutomaticAppLoginPolicyComponent, - FreeFamiliesSponsorshipPolicyComponent, - ], + declarations: [AppComponent], bootstrap: [AppComponent], + providers: [ + safeProvider({ + provide: POLICY_EDIT_REGISTER, + useValue: bitPolicyEditRegister, + }), + ], }) export class AppModule {} diff --git a/bitwarden_license/bit-web/src/app/billing/policies/free-families-sponsorship.component.ts b/bitwarden_license/bit-web/src/app/billing/policies/free-families-sponsorship.component.ts index dd808300988..db5ef3ba62f 100644 --- a/bitwarden_license/bit-web/src/app/billing/policies/free-families-sponsorship.component.ts +++ b/bitwarden_license/bit-web/src/app/billing/policies/free-families-sponsorship.component.ts @@ -2,11 +2,12 @@ import { Component } from "@angular/core"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { - BasePolicy, - BasePolicyComponent, -} from "@bitwarden/web-vault/app/admin-console/organizations/policies/base-policy.component"; + BasePolicyEditDefinition, + BasePolicyEditComponent, +} from "@bitwarden/web-vault/app/admin-console/organizations/policies"; +import { SharedModule } from "@bitwarden/web-vault/app/shared"; -export class FreeFamiliesSponsorshipPolicy extends BasePolicy { +export class FreeFamiliesSponsorshipPolicy extends BasePolicyEditDefinition { name = "freeFamiliesSponsorship"; description = "freeFamiliesSponsorshipPolicyDesc"; type = PolicyType.FreeFamiliesSponsorshipPolicy; @@ -14,8 +15,7 @@ export class FreeFamiliesSponsorshipPolicy extends BasePolicy { } @Component({ - selector: "policy-free-families-sponsorship", templateUrl: "free-families-sponsorship.component.html", - standalone: false, + imports: [SharedModule], }) -export class FreeFamiliesSponsorshipPolicyComponent extends BasePolicyComponent {} +export class FreeFamiliesSponsorshipPolicyComponent extends BasePolicyEditComponent {}