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 index e8e48f41716..eebfadbef30 100644 --- 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 @@ -6,8 +6,10 @@ 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 { VNextSavePolicyRequest } from "@bitwarden/common/admin-console/models/request/v-next-save-policy.request"; import { PolicyStatusResponse } from "@bitwarden/common/admin-console/models/response/policy-status.response"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { OrgKey } from "@bitwarden/common/types/key"; import { DialogConfig, DialogRef, DialogService } from "@bitwarden/components"; import type { PolicyEditDialogData, PolicyEditDialogResult } from "./policy-edit-dialog.component"; @@ -103,6 +105,19 @@ export abstract class BasePolicyEditComponent implements OnInit { } } + async buildVNextRequest(orgKey: OrgKey): Promise { + if (!this.policy) { + throw new Error("Policy was not found"); + } + + const request: VNextSavePolicyRequest = { + policy: await this.buildRequest(), + metadata: null, + }; + + return request; + } + buildRequest() { if (!this.policy) { throw new Error("Policy was not found"); diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts index e4a07b7440d..bae94940b05 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/organization-data-ownership.component.ts @@ -3,7 +3,7 @@ import { lastValueFrom, map, Observable } 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 { VNextSavePolicyRequest } from "@bitwarden/common/admin-console/models/request/v-next-save-policy.request"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; @@ -15,12 +15,9 @@ import { EncString } from "@bitwarden/sdk-internal"; import { SharedModule } from "../../../../shared"; import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; -export interface VNextPolicyRequest { - policy: PolicyRequest; - metadata: { - defaultUserCollectionName: string; - }; -} +type VNextSaveOrganizationDataOwnershipPolicyRequest = VNextSavePolicyRequest<{ + defaultUserCollectionName: string; +}>; export class OrganizationDataOwnershipPolicy extends BasePolicyEditDefinition { name = "organizationDataOwnership"; @@ -69,14 +66,16 @@ export class OrganizationDataOwnershipPolicyComponent return true; } - async buildVNextRequest(orgKey: OrgKey): Promise { + async buildVNextRequest( + orgKey: OrgKey, + ): Promise { if (!this.policy) { throw new Error("Policy was not found"); } const defaultUserCollectionName = await this.getEncryptedDefaultUserCollectionName(orgKey); - const request: VNextPolicyRequest = { + const request: VNextSaveOrganizationDataOwnershipPolicyRequest = { policy: { enabled: this.enabled.value ?? false, data: this.buildRequestData(), diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/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 index 21ab7fc71ba..da0e9ede217 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/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 @@ -6,6 +6,7 @@ import { mock } from "jest-mock-extended"; import { PolicyType } from "@bitwarden/common/admin-console/enums"; import { PolicyStatusResponse } from "@bitwarden/common/admin-console/models/response/policy-status.response"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { OrgKey } from "@bitwarden/common/types/key"; import { RemoveUnlockWithPinPolicy, @@ -96,4 +97,27 @@ describe("RemoveUnlockWithPinPolicyComponent", () => { expect(bitLabelElement).not.toBeNull(); expect(bitLabelElement.nativeElement.textContent.trim()).toBe("Turn on"); }); + + it("buildVNextRequest should delegate to buildRequest and wrap with null metadata", async () => { + component.policy = new RemoveUnlockWithPinPolicy(); + component.policyResponse = new PolicyStatusResponse({ + organizationId: "org1", + type: PolicyType.RemoveUnlockWithPin, + enabled: true, + }); + component.ngOnInit(); + + const buildRequestSpy = jest.spyOn(component, "buildRequest"); + + const result = await component.buildVNextRequest(mock()); + + expect(buildRequestSpy).toHaveBeenCalled(); + expect(result).toEqual({ + policy: { + enabled: true, + data: null, + }, + metadata: null, + }); + }); }); diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/vnext-organization-data-ownership.component.ts index e1b2f14d457..334d964f0bf 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-definitions/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,7 +12,7 @@ import { Observable } 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 { VNextSavePolicyRequest } from "@bitwarden/common/admin-console/models/request/v-next-save-policy.request"; import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; @@ -24,12 +24,9 @@ import { SharedModule } from "../../../../shared"; import { BasePolicyEditDefinition, BasePolicyEditComponent } from "../base-policy-edit.component"; import { OrganizationDataOwnershipPolicyDialogComponent } from "../policy-edit-dialogs"; -export interface VNextPolicyRequest { - policy: PolicyRequest; - metadata: { - defaultUserCollectionName: string; - }; -} +type VNextSaveOrganizationDataOwnershipPolicyRequest = VNextSavePolicyRequest<{ + defaultUserCollectionName: string; +}>; export class vNextOrganizationDataOwnershipPolicy extends BasePolicyEditDefinition { name = "centralizeDataOwnership"; @@ -67,14 +64,16 @@ export class vNextOrganizationDataOwnershipPolicyComponent protected steps = [this.policyForm, this.warningContent]; - async buildVNextRequest(orgKey: OrgKey): Promise { + async buildVNextRequest( + orgKey: OrgKey, + ): Promise { if (!this.policy) { throw new Error("Policy was not found"); } const defaultUserCollectionName = await this.getEncryptedDefaultUserCollectionName(orgKey); - const request: VNextPolicyRequest = { + const request: VNextSaveOrganizationDataOwnershipPolicyRequest = { policy: { enabled: this.enabled.value ?? false, data: this.buildRequestData(), diff --git a/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialog.component.ts b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialog.component.ts index b2f601d4e8b..6e225f9103a 100644 --- a/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/policies/policy-edit-dialog.component.ts @@ -11,6 +11,7 @@ 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"; +import { VNextSavePolicyRequest } from "@bitwarden/common/admin-console/models/request/v-next-save-policy.request"; import { PolicyResponse } from "@bitwarden/common/admin-console/models/response/policy.response"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; @@ -29,7 +30,6 @@ import { KeyService } from "@bitwarden/key-management"; import { SharedModule } from "../../../shared"; import { BasePolicyEditDefinition, BasePolicyEditComponent } from "./base-policy-edit.component"; -import { VNextPolicyRequest } from "./policy-edit-definitions/organization-data-ownership.component"; export type PolicyEditDialogData = { /** @@ -91,7 +91,7 @@ export class PolicyEditDialogComponent implements AfterViewInit { private hasVNextRequest( component: BasePolicyEditComponent, ): component is BasePolicyEditComponent & { - buildVNextRequest: (orgKey: OrgKey) => Promise; + buildVNextRequest: (orgKey: OrgKey) => Promise; } { return "buildVNextRequest" in component && typeof component.buildVNextRequest === "function"; } @@ -145,11 +145,7 @@ export class PolicyEditDialogComponent implements AfterViewInit { } try { - if (this.hasVNextRequest(this.policyComponent)) { - await this.handleVNextSubmission(this.policyComponent); - } else { - await this.handleStandardSubmission(); - } + await this.handleVNextSubmission(this.policyComponent); this.toastService.showToast({ variant: "success", @@ -164,20 +160,7 @@ export class PolicyEditDialogComponent 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); - } - - private async handleVNextSubmission( - policyComponent: BasePolicyEditComponent & { - buildVNextRequest: (orgKey: OrgKey) => Promise; - }, - ): Promise { + private async handleVNextSubmission(policyComponent: BasePolicyEditComponent): Promise { const orgKey = await firstValueFrom( this.accountService.activeAccount$.pipe( getUserId, diff --git a/libs/common/src/admin-console/models/request/v-next-save-policy.request.ts b/libs/common/src/admin-console/models/request/v-next-save-policy.request.ts new file mode 100644 index 00000000000..d0dcbb44cc9 --- /dev/null +++ b/libs/common/src/admin-console/models/request/v-next-save-policy.request.ts @@ -0,0 +1,6 @@ +import { PolicyRequest } from "./policy.request"; + +export interface VNextSavePolicyRequest> { + policy: PolicyRequest; + metadata: TMetadata | null; +}