diff --git a/src/app/organizations/manage/policies.component.html b/src/app/organizations/manage/policies.component.html index 35ec2dad8f..9dd98798d9 100644 --- a/src/app/organizations/manage/policies.component.html +++ b/src/app/organizations/manage/policies.component.html @@ -13,7 +13,7 @@ -
+ {{p.name}} {{'enabled' | i18n}} {{p.description}} diff --git a/src/app/organizations/manage/policies.component.ts b/src/app/organizations/manage/policies.component.ts index cc111757ac..e7fa5583a9 100644 --- a/src/app/organizations/manage/policies.component.ts +++ b/src/app/organizations/manage/policies.component.ts @@ -47,34 +47,7 @@ export class PoliciesComponent implements OnInit { constructor(private apiService: ApiService, private route: ActivatedRoute, private i18nService: I18nService, private componentFactoryResolver: ComponentFactoryResolver, private platformUtilsService: PlatformUtilsService, private userService: UserService, - private router: Router, private environmentService: EnvironmentService) { - this.policies = [ - { - name: i18nService.t('twoStepLogin'), - description: i18nService.t('twoStepLoginPolicyDesc'), - type: PolicyType.TwoFactorAuthentication, - enabled: false, - }, - { - name: i18nService.t('masterPass'), - description: i18nService.t('masterPassPolicyDesc'), - type: PolicyType.MasterPassword, - enabled: false, - }, - { - name: i18nService.t('passwordGenerator'), - description: i18nService.t('passwordGeneratorPolicyDesc'), - type: PolicyType.PasswordGenerator, - enabled: false, - }, - { - name: i18nService.t('onlyOrg'), - description: i18nService.t('onlyOrgDesc'), - type: PolicyType.OnlyOrg, - enabled: false, - }, - ]; - } + private router: Router, private environmentService: EnvironmentService) { } async ngOnInit() { this.route.parent.parent.params.subscribe(async (params) => { @@ -84,6 +57,43 @@ export class PoliciesComponent implements OnInit { this.router.navigate(['/organizations', this.organizationId]); return; } + this.policies = [ + { + name: this.i18nService.t('twoStepLogin'), + description: this.i18nService.t('twoStepLoginPolicyDesc'), + type: PolicyType.TwoFactorAuthentication, + enabled: false, + display: true, + }, + { + name: this.i18nService.t('masterPass'), + description: this.i18nService.t('masterPassPolicyDesc'), + type: PolicyType.MasterPassword, + enabled: false, + display: true, + }, + { + name: this.i18nService.t('passwordGenerator'), + description: this.i18nService.t('passwordGeneratorPolicyDesc'), + type: PolicyType.PasswordGenerator, + enabled: false, + display: true, + }, + { + name: this.i18nService.t('onlyOrg'), + description: this.i18nService.t('onlyOrgDesc'), + type: PolicyType.OnlyOrg, + enabled: false, + display: true, + }, + { + name: this.i18nService.t('requireSso'), + description: this.i18nService.t('requireSsoPolicyDesc'), + type: PolicyType.RequireSso, + enabled: false, + display: organization.useSso, + }, + ]; await this.load(); }); @@ -122,6 +132,7 @@ export class PoliciesComponent implements OnInit { childComponent.description = p.description; childComponent.type = p.type; childComponent.organizationId = this.organizationId; + childComponent.policiesEnabledMap = this.policiesEnabledMap; childComponent.onSavedPolicy.subscribe(() => { this.modal.close(); this.load(); diff --git a/src/app/organizations/manage/policy-edit.component.html b/src/app/organizations/manage/policy-edit.component.html index 24fdb07385..421d14ebd4 100644 --- a/src/app/organizations/manage/policy-edit.component.html +++ b/src/app/organizations/manage/policy-edit.component.html @@ -17,10 +17,13 @@ title="{{'warning' | i18n}}" icon="fa-warning"> {{'twoStepLoginPolicyWarning' | i18n}} - + {{'onlyOrgPolicyWarning' | i18n}} + + {{'requireSsoPolicyReq' | i18n}} +
= new Map(); @Output() onSavedPolicy = new EventEmitter(); policyType = PolicyType; @@ -127,45 +128,66 @@ export class PolicyEditComponent implements OnInit { } async submit() { - const request = new PolicyRequest(); - request.enabled = this.enabled; - request.type = this.type; - request.data = null; - switch (this.type) { - case PolicyType.PasswordGenerator: - request.data = { - defaultType: this.passGenDefaultType, - minLength: this.passGenMinLength || null, - useUpper: this.passGenUseUpper, - useLower: this.passGenUseLower, - useNumbers: this.passGenUseNumbers, - useSpecial: this.passGenUseSpecial, - minNumbers: this.passGenMinNumbers || null, - minSpecial: this.passGenMinSpecial || null, - minNumberWords: this.passGenMinNumberWords || null, - capitalize: this.passGenCapitalize, - includeNumber: this.passGenIncludeNumber, - }; - break; - case PolicyType.MasterPassword: - request.data = { - minComplexity: this.masterPassMinComplexity || null, - minLength: this.masterPassMinLength || null, - requireUpper: this.masterPassRequireUpper, - requireLower: this.masterPassRequireLower, - requireNumbers: this.masterPassRequireNumbers, - requireSpecial: this.masterPassRequireSpecial, - }; - break; - default: - break; + if (this.preValidate()) { + const request = new PolicyRequest(); + request.enabled = this.enabled; + request.type = this.type; + request.data = null; + switch (this.type) { + case PolicyType.PasswordGenerator: + request.data = { + defaultType: this.passGenDefaultType, + minLength: this.passGenMinLength || null, + useUpper: this.passGenUseUpper, + useLower: this.passGenUseLower, + useNumbers: this.passGenUseNumbers, + useSpecial: this.passGenUseSpecial, + minNumbers: this.passGenMinNumbers || null, + minSpecial: this.passGenMinSpecial || null, + minNumberWords: this.passGenMinNumberWords || null, + capitalize: this.passGenCapitalize, + includeNumber: this.passGenIncludeNumber, + }; + break; + case PolicyType.MasterPassword: + request.data = { + minComplexity: this.masterPassMinComplexity || null, + minLength: this.masterPassMinLength || null, + requireUpper: this.masterPassRequireUpper, + requireLower: this.masterPassRequireLower, + requireNumbers: this.masterPassRequireNumbers, + requireSpecial: this.masterPassRequireSpecial, + }; + break; + default: + break; + } + try { + this.formPromise = this.apiService.putPolicy(this.organizationId, this.type, request); + await this.formPromise; + this.analytics.eventTrack.next({ action: 'Edited Policy' }); + this.toasterService.popAsync('success', null, this.i18nService.t('editedPolicyId', this.name)); + this.onSavedPolicy.emit(); + } catch { } + } + } + + private preValidate(): boolean { + switch (this.type) { + case PolicyType.RequireSso: + if (!this.enabled) { // Don't need prevalidation checks if submitting to disable + return true; + } + // Have OnlyOrg policy enabled? + if (!(this.policiesEnabledMap.has(PolicyType.OnlyOrg) + && this.policiesEnabledMap.get(PolicyType.OnlyOrg))) { + this.toasterService.popAsync('error', null, this.i18nService.t('requireSsoPolicyReqError')); + return false; + } + return true; + + default: + return true; } - try { - this.formPromise = this.apiService.putPolicy(this.organizationId, this.type, request); - await this.formPromise; - this.analytics.eventTrack.next({ action: 'Edited Policy' }); - this.toasterService.popAsync('success', null, this.i18nService.t('editedPolicyId', this.name)); - this.onSavedPolicy.emit(); - } catch { } } } diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 7123bdffc8..f91cd04aa9 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -3221,5 +3221,20 @@ }, "onlyOrgPolicyWarning": { "message": "Organization members who are already a member of another organization will be removed from your organization." + }, + "requireSso": { + "message": "Single Sign-On Authentication" + }, + "requireSsoPolicyDesc": { + "message": "Require users to log in with the Enterprise Single Sign-On method." + }, + "prerequisite": { + "message": "Prerequisite" + }, + "requireSsoPolicyReq": { + "message": "The Single Organization enterprise policy must be enabled before activating this policy." + }, + "requireSsoPolicyReqError": { + "message": "Single Organization policy not enabled." } }