From 30419a625fbdeefee1b0d0c24ee1d26064b17ba2 Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Tue, 31 Aug 2021 06:52:57 +1000 Subject: [PATCH] Move policy checks within policyService (#466) * Move policy logic within policyService * Remove unneeded import * Clean up unused code * Fix linting * Enforce policies from accepting org invite * Only exempt owner or admin from policies * Use canManagePolicies as exemption criteria * Make orgUser status check more semantic Co-authored-by: Addison Beck Co-authored-by: Addison Beck --- angular/src/components/add-edit.component.ts | 20 ++++-------- .../src/components/send/add-edit.component.ts | 22 ++----------- angular/src/components/send/send.component.ts | 10 +----- common/src/abstractions/policy.service.ts | 6 ++-- common/src/models/domain/organization.ts | 4 +++ common/src/services/policy.service.ts | 31 +++++++++++++++++++ 6 files changed, 49 insertions(+), 44 deletions(-) diff --git a/angular/src/components/add-edit.component.ts b/angular/src/components/add-edit.component.ts index f91397a13d..ef85118ddb 100644 --- a/angular/src/components/add-edit.component.ts +++ b/angular/src/components/add-edit.component.ts @@ -164,28 +164,20 @@ export class AddEditComponent implements OnInit { } async init() { - const policies = await this.policyService.getAll(PolicyType.PersonalOwnership); const myEmail = await this.userService.getEmail(); this.ownershipOptions.push({ name: myEmail, value: null }); const orgs = await this.userService.getAllOrganizations(); orgs.sort(Utils.getSortFunction(this.i18nService, 'name')).forEach(o => { if (o.enabled && o.status === OrganizationUserStatusType.Confirmed) { this.ownershipOptions.push({ name: o.name, value: o.id }); - if (policies != null && o.usePolicies && !o.canManagePolicies && this.allowPersonal) { - for (const policy of policies) { - if (policy.organizationId === o.id && policy.enabled) { - this.allowPersonal = false; - this.ownershipOptions.splice(0, 1); - // Default to the organization who owns this policy for now (if necessary) - if (this.organizationId == null) { - this.organizationId = o.id; - } - break; - } - } - } } }); + + if (this.allowPersonal && await this.policyService.policyAppliesToUser(PolicyType.PersonalOwnership)) { + this.allowPersonal = false; + this.ownershipOptions.splice(0, 1); + } + this.writeableCollections = await this.loadCollections(); } diff --git a/angular/src/components/send/add-edit.component.ts b/angular/src/components/send/add-edit.component.ts index 0c3832d86e..b4b5c927bb 100644 --- a/angular/src/components/send/add-edit.component.ts +++ b/angular/src/components/send/add-edit.component.ts @@ -7,7 +7,6 @@ import { Output } from '@angular/core'; -import { OrganizationUserStatusType } from 'jslib-common/enums/organizationUserStatusType'; import { PolicyType } from 'jslib-common/enums/policyType'; import { SendType } from 'jslib-common/enums/sendType'; @@ -103,24 +102,9 @@ export class AddEditComponent implements OnInit { } async load() { - const disableSendPolicies = await this.policyService.getAll(PolicyType.DisableSend); - const organizations = await this.userService.getAllOrganizations(); - this.disableSend = organizations.some(o => { - return o.enabled && - o.status === OrganizationUserStatusType.Confirmed && - o.usePolicies && - !o.canManagePolicies && - disableSendPolicies.some(p => p.organizationId === o.id && p.enabled); - }); - - const sendOptionsPolicies = await this.policyService.getAll(PolicyType.SendOptions); - this.disableHideEmail = await organizations.some(o => { - return o.enabled && - o.status === OrganizationUserStatusType.Confirmed && - o.usePolicies && - !o.canManagePolicies && - sendOptionsPolicies.some(p => p.organizationId === o.id && p.enabled && p.data.disableHideEmail); - }); + this.disableSend = await this.policyService.policyAppliesToUser(PolicyType.DisableSend); + this.disableHideEmail = await this.policyService.policyAppliesToUser(PolicyType.SendOptions, + p => p.data.disableHideEmail); this.canAccessPremium = await this.userService.canAccessPremium(); this.emailVerified = await this.userService.getEmailVerified(); diff --git a/angular/src/components/send/send.component.ts b/angular/src/components/send/send.component.ts index 577fe7056f..6ece39be3c 100644 --- a/angular/src/components/send/send.component.ts +++ b/angular/src/components/send/send.component.ts @@ -51,15 +51,7 @@ export class SendComponent implements OnInit { protected policyService: PolicyService, protected userService: UserService) { } async ngOnInit() { - const policies = await this.policyService.getAll(PolicyType.DisableSend); - const organizations = await this.userService.getAllOrganizations(); - this.disableSend = organizations.some(o => { - return o.enabled && - o.status === OrganizationUserStatusType.Confirmed && - o.usePolicies && - !o.canManagePolicies && - policies.some(p => p.organizationId === o.id && p.enabled); - }); + this.disableSend = await this.policyService.policyAppliesToUser(PolicyType.DisableSend); } async load(filter: (send: SendView) => boolean = null) { diff --git a/common/src/abstractions/policy.service.ts b/common/src/abstractions/policy.service.ts index b823f2fbfd..6acc8253fc 100644 --- a/common/src/abstractions/policy.service.ts +++ b/common/src/abstractions/policy.service.ts @@ -4,16 +4,17 @@ import { MasterPasswordPolicyOptions } from '../models/domain/masterPasswordPoli import { Policy } from '../models/domain/policy'; import { ResetPasswordPolicyOptions } from '../models/domain/resetPasswordPolicyOptions'; -import { PolicyType } from '../enums/policyType'; - import { ListResponse } from '../models/response/listResponse'; import { PolicyResponse } from '../models/response/policyResponse'; +import { PolicyType } from '../enums/policyType'; + export abstract class PolicyService { policyCache: Policy[]; clearCache: () => void; getAll: (type?: PolicyType) => Promise; + getPolicyForOrganization: (policyType: PolicyType, organizationId: string) => Promise; replace: (policies: { [id: string]: PolicyData; }) => Promise; clear: (userId: string) => Promise; getMasterPasswordPolicyOptions: (policies?: Policy[]) => Promise; @@ -21,4 +22,5 @@ export abstract class PolicyService { enforcedPolicyOptions?: MasterPasswordPolicyOptions) => boolean; getResetPasswordPolicyOptions: (policies: Policy[], orgId: string) => [ResetPasswordPolicyOptions, boolean]; mapPoliciesFromToken: (policiesResponse: ListResponse) => Policy[]; + policyAppliesToUser: (policyType: PolicyType, policyFilter?: (policy: Policy) => boolean) => Promise; } diff --git a/common/src/models/domain/organization.ts b/common/src/models/domain/organization.ts index b6bc5e0e75..a7376956c1 100644 --- a/common/src/models/domain/organization.ts +++ b/common/src/models/domain/organization.ts @@ -135,4 +135,8 @@ export class Organization { get canManageUsersPassword() { return this.isAdmin || this.permissions.manageResetPassword; } + + get isExemptFromPolicies() { + return this.canManagePolicies; + } } diff --git a/common/src/services/policy.service.ts b/common/src/services/policy.service.ts index 92807a0f99..54f77f8102 100644 --- a/common/src/services/policy.service.ts +++ b/common/src/services/policy.service.ts @@ -8,6 +8,7 @@ import { MasterPasswordPolicyOptions } from '../models/domain/masterPasswordPoli import { Policy } from '../models/domain/policy'; import { ResetPasswordPolicyOptions } from '../models/domain/resetPasswordPolicyOptions'; +import { OrganizationUserStatusType } from '../enums/organizationUserStatusType'; import { PolicyType } from '../enums/policyType'; import { ListResponse } from '../models/response/listResponse'; @@ -47,6 +48,11 @@ export class PolicyService implements PolicyServiceAbstraction { } } + async getPolicyForOrganization(policyType: PolicyType, organizationId: string): Promise { + const policies = await this.getAll(policyType); + return policies.find(p => p.organizationId === organizationId); + } + async replace(policies: { [id: string]: PolicyData; }): Promise { const userId = await this.userService.getUserId(); await this.storageService.save(Keys.policiesPrefix + userId, policies); @@ -164,4 +170,29 @@ export class PolicyService implements PolicyServiceAbstraction { const policiesData = policiesResponse.data.map(p => new PolicyData(p)); return policiesData.map(p => new Policy(p)); } + + async policyAppliesToUser(policyType: PolicyType, policyFilter?: (policy: Policy) => boolean) { + if (policyFilter == null) { + policyFilter = (policy: Policy) => true; + } + + const policies = await this.getAll(policyType); + const organizations = await this.userService.getAllOrganizations(); + + const filteredPolicies = policies + .filter(p => + p.enabled && + p.type === policyType && + policyFilter(p)) + .map(p => p.organizationId); + + const policySet = new Set(filteredPolicies); + + return organizations.some(o => + o.enabled && + o.status >= OrganizationUserStatusType.Accepted && + o.usePolicies && + !o.isExemptFromPolicies && + policySet.has(o.id)); + } }