diff --git a/libs/common/src/admin-console/services/policy/default-policy.service.spec.ts b/libs/common/src/admin-console/services/policy/default-policy.service.spec.ts index 6b50f9befe..8ce1a78551 100644 --- a/libs/common/src/admin-console/services/policy/default-policy.service.spec.ts +++ b/libs/common/src/admin-console/services/policy/default-policy.service.spec.ts @@ -554,6 +554,77 @@ describe("PolicyService", () => { expect(result).toBe(false); }); + + describe("SingleOrg policy exemptions", () => { + it("returns true for SingleOrg policy when AutoConfirm is enabled, even for users who can manage policies", async () => { + singleUserState.nextState( + arrayToRecord([ + policyData("policy1", "org6", PolicyType.SingleOrg, true), + policyData("policy2", "org6", PolicyType.AutoConfirm, true), + ]), + ); + + const result = await firstValueFrom( + policyService.policyAppliesToUser$(PolicyType.SingleOrg, userId), + ); + + expect(result).toBe(true); + }); + + it("returns false for SingleOrg policy when user can manage policies and AutoConfirm is not enabled", async () => { + singleUserState.nextState( + arrayToRecord([policyData("policy1", "org6", PolicyType.SingleOrg, true)]), + ); + + const result = await firstValueFrom( + policyService.policyAppliesToUser$(PolicyType.SingleOrg, userId), + ); + + expect(result).toBe(false); + }); + + it("returns false for SingleOrg policy when user can manage policies and AutoConfirm is disabled", async () => { + singleUserState.nextState( + arrayToRecord([ + policyData("policy1", "org6", PolicyType.SingleOrg, true), + policyData("policy2", "org6", PolicyType.AutoConfirm, false), + ]), + ); + + const result = await firstValueFrom( + policyService.policyAppliesToUser$(PolicyType.SingleOrg, userId), + ); + + expect(result).toBe(false); + }); + + it("returns true for SingleOrg policy for regular users when AutoConfirm is not enabled", async () => { + singleUserState.nextState( + arrayToRecord([policyData("policy1", "org1", PolicyType.SingleOrg, true)]), + ); + + const result = await firstValueFrom( + policyService.policyAppliesToUser$(PolicyType.SingleOrg, userId), + ); + + expect(result).toBe(true); + }); + + it("returns true for SingleOrg policy when AutoConfirm is enabled in a different organization", async () => { + singleUserState.nextState( + arrayToRecord([ + policyData("policy1", "org6", PolicyType.SingleOrg, true), + policyData("policy2", "org1", PolicyType.AutoConfirm, true), + ]), + ); + + const result = await firstValueFrom( + policyService.policyAppliesToUser$(PolicyType.SingleOrg, userId), + ); + + expect(result).toBe(false); + }); + }); }); describe("combinePoliciesIntoMasterPasswordPolicyOptions", () => { diff --git a/libs/common/src/admin-console/services/policy/default-policy.service.ts b/libs/common/src/admin-console/services/policy/default-policy.service.ts index 5781dd938f..1107e88e79 100644 --- a/libs/common/src/admin-console/services/policy/default-policy.service.ts +++ b/libs/common/src/admin-console/services/policy/default-policy.service.ts @@ -40,18 +40,16 @@ export class DefaultPolicyService implements PolicyService { } policiesByType$(policyType: PolicyType, userId: UserId) { - const filteredPolicies$ = this.policies$(userId).pipe( - map((policies) => policies.filter((p) => p.type === policyType)), - ); - if (!userId) { throw new Error("No userId provided"); } + const allPolicies$ = this.policies$(userId); const organizations$ = this.organizationService.organizations$(userId); - return combineLatest([filteredPolicies$, organizations$]).pipe( + return combineLatest([allPolicies$, organizations$]).pipe( map(([policies, organizations]) => this.enforcedPolicyFilter(policies, organizations)), + map((policies) => policies.filter((p) => p.type === policyType)), ); } @@ -77,7 +75,7 @@ export class DefaultPolicyService implements PolicyService { policy.enabled && organization.status >= OrganizationUserStatusType.Accepted && organization.usePolicies && - !this.isExemptFromPolicy(policy.type, organization) + !this.isExemptFromPolicy(policy.type, organization, policies) ); }); } @@ -265,7 +263,11 @@ export class DefaultPolicyService implements PolicyService { * Determines whether an orgUser is exempt from a specific policy because of their role * Generally orgUsers who can manage policies are exempt from them, but some policies are stricter */ - private isExemptFromPolicy(policyType: PolicyType, organization: Organization) { + private isExemptFromPolicy( + policyType: PolicyType, + organization: Organization, + allPolicies: Policy[], + ) { switch (policyType) { case PolicyType.MaximumVaultTimeout: // Max Vault Timeout applies to everyone except owners @@ -286,6 +288,14 @@ export class DefaultPolicyService implements PolicyService { case PolicyType.OrganizationDataOwnership: // organization data ownership policy applies to everyone except admins and owners return organization.isAdmin; + case PolicyType.SingleOrg: + // Check if AutoConfirm policy is enabled for this organization + return allPolicies.find( + (p) => + p.organizationId === organization.id && p.type === PolicyType.AutoConfirm && p.enabled, + ) + ? false + : organization.canManagePolicies; default: return organization.canManagePolicies; }