From e7e5816ded9e356ba49a35bd4e2fd57eb42fbbe4 Mon Sep 17 00:00:00 2001 From: Vincent Salucci <26154748+vincentsalucci@users.noreply.github.com> Date: Tue, 3 Mar 2020 10:20:28 -0600 Subject: [PATCH] Enforce Master Password Policies (Change/Register) (#478) * Initial commit for change password mp policy enforcement * Initial commit of mp policy for registering * Testing Register component * Final testing complete * Reverting service module URLs * Requested changes and build fix * Updated submit function --- src/app/accounts/register.component.html | 19 +++++++++ src/app/accounts/register.component.ts | 21 +++++++++- .../manage/policy-edit.component.html | 4 +- .../settings/change-password.component.html | 16 +++++++ src/app/settings/change-password.component.ts | 18 +++++++- src/locales/en/messages.json | 42 +++++++++++++++++++ 6 files changed, 116 insertions(+), 4 deletions(-) diff --git a/src/app/accounts/register.component.html b/src/app/accounts/register.component.html index 74d6b1d72d..657f5af004 100644 --- a/src/app/accounts/register.component.html +++ b/src/app/accounts/register.component.html @@ -21,6 +21,25 @@ {{'yourNameDesc' | i18n}}
+ +

{{'masterPasswordPolicyInEffect' | i18n}}

+ +
diff --git a/src/app/accounts/register.component.ts b/src/app/accounts/register.component.ts index f6042ae1de..a0080475dc 100644 --- a/src/app/accounts/register.component.ts +++ b/src/app/accounts/register.component.ts @@ -10,10 +10,12 @@ import { CryptoService } from 'jslib/abstractions/crypto.service'; import { I18nService } from 'jslib/abstractions/i18n.service'; import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service'; import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; +import { PolicyService } from 'jslib/abstractions/policy.service'; import { StateService } from 'jslib/abstractions/state.service'; import { RegisterComponent as BaseRegisterComponent } from 'jslib/angular/components/register.component'; +import { MasterPasswordPolicyOptions } from 'jslib/models/domain/masterPasswordPolicyOptions'; import { Policy } from 'jslib/models/domain/policy'; import { PolicyData } from 'jslib/models/data/policyData'; @@ -27,12 +29,13 @@ export class RegisterComponent extends BaseRegisterComponent { showTerms = true; private policies: Policy[]; + enforcedPolicyOptions: MasterPasswordPolicyOptions; constructor(authService: AuthService, router: Router, i18nService: I18nService, cryptoService: CryptoService, apiService: ApiService, private route: ActivatedRoute, stateService: StateService, platformUtilsService: PlatformUtilsService, - passwordGenerationService: PasswordGenerationService) { + passwordGenerationService: PasswordGenerationService, private policyService: PolicyService) { super(authService, router, i18nService, cryptoService, apiService, stateService, platformUtilsService, passwordGenerationService); this.showTerms = !platformUtilsService.isSelfHost(); @@ -65,5 +68,21 @@ export class RegisterComponent extends BaseRegisterComponent { } } catch { } } + + if (this.policies != null) { + this.enforcedPolicyOptions = await this.policyService.getMasterPasswordPolicyOptions(this.policies); + } + } + + async submit() { + if (this.enforcedPolicyOptions != null && + !this.policyService.evaluateMasterPassword(this.masterPasswordScore, this.masterPassword, + this.enforcedPolicyOptions)) { + this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('masterPasswordPolicyRequirementsNotMet')); + return; + } + + await super.submit(); } } diff --git a/src/app/organizations/manage/policy-edit.component.html b/src/app/organizations/manage/policy-edit.component.html index d3dca0de37..23c3277756 100644 --- a/src/app/organizations/manage/policy-edit.component.html +++ b/src/app/organizations/manage/policy-edit.component.html @@ -33,7 +33,7 @@
-
@@ -110,4 +110,4 @@
- \ No newline at end of file + diff --git a/src/app/settings/change-password.component.html b/src/app/settings/change-password.component.html index d95bf0babf..9274fb58ca 100644 --- a/src/app/settings/change-password.component.html +++ b/src/app/settings/change-password.component.html @@ -1,4 +1,20 @@ {{'loggedOutWarning' | i18n}} + +

{{'masterPasswordPolicyInEffect' | i18n}}

+ +
+
diff --git a/src/app/settings/change-password.component.ts b/src/app/settings/change-password.component.ts index 1c9b4774c8..dd5cac871d 100644 --- a/src/app/settings/change-password.component.ts +++ b/src/app/settings/change-password.component.ts @@ -14,10 +14,12 @@ import { I18nService } from 'jslib/abstractions/i18n.service'; import { MessagingService } from 'jslib/abstractions/messaging.service'; import { PasswordGenerationService } from 'jslib/abstractions/passwordGeneration.service'; import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; +import { PolicyService } from 'jslib/abstractions/policy.service'; import { SyncService } from 'jslib/abstractions/sync.service'; import { UserService } from 'jslib/abstractions/user.service'; import { CipherString } from 'jslib/models/domain/cipherString'; +import { MasterPasswordPolicyOptions } from 'jslib/models/domain/masterPasswordPolicyOptions'; import { SymmetricCryptoKey } from 'jslib/models/domain/symmetricCryptoKey'; import { CipherWithIdRequest } from 'jslib/models/request/cipherWithIdRequest'; @@ -36,6 +38,7 @@ export class ChangePasswordComponent implements OnInit { formPromise: Promise; masterPasswordScore: number; rotateEncKey = false; + enforcedPolicyOptions: MasterPasswordPolicyOptions; private masterPasswordStrengthTimeout: any; private email: string; @@ -45,10 +48,12 @@ export class ChangePasswordComponent implements OnInit { private cryptoService: CryptoService, private messagingService: MessagingService, private userService: UserService, private passwordGenerationService: PasswordGenerationService, private platformUtilsService: PlatformUtilsService, private folderService: FolderService, - private cipherService: CipherService, private syncService: SyncService) { } + private cipherService: CipherService, private syncService: SyncService, + private policyService: PolicyService) { } async ngOnInit() { this.email = await this.userService.getEmail(); + this.enforcedPolicyOptions = await this.policyService.getMasterPasswordPolicyOptions(); } async submit() { @@ -77,6 +82,17 @@ export class ChangePasswordComponent implements OnInit { const strengthResult = this.passwordGenerationService.passwordStrength(this.newMasterPassword, this.getPasswordStrengthUserInput()); + + if (this.enforcedPolicyOptions != null && + !this.policyService.evaluateMasterPassword( + strengthResult.score, + this.newMasterPassword, + this.enforcedPolicyOptions)) { + this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'), + this.i18nService.t('masterPasswordPolicyRequirementsNotMet')); + return; + } + if (strengthResult != null && strengthResult.score < 3) { const result = await this.platformUtilsService.showDialog(this.i18nService.t('weakMasterPasswordDesc'), this.i18nService.t('weakMasterPassword'), this.i18nService.t('yes'), this.i18nService.t('no'), diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index df4286f9d1..14a287415f 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -2989,5 +2989,47 @@ }, "passwordGeneratorPolicyInEffect": { "message": "One or more organization policies are affecting your generator settings." + }, + "masterPasswordPolicyInEffect": { + "message": "One or more organization policies require your master password to meet the following requirements:" + }, + "policyInEffectMinComplexity": { + "message": "Minimum complexity score of $SCORE$", + "placeholders": { + "score": { + "content": "$1", + "example": "4" + } + } + }, + "policyInEffectMinLength": { + "message": "Minimum length of $LENGTH$", + "placeholders": { + "length": { + "content": "$1", + "example": "14" + } + } + }, + "policyInEffectUppercase": { + "message": "Contain one or more uppercase characters" + }, + "policyInEffectLowercase": { + "message": "Contain one or more lowercase characters" + }, + "policyInEffectNumbers": { + "message": "Contain one or more numbers" + }, + "policyInEffectSpecial": { + "message": "Contain one or more of the following special characters $CHARS$", + "placeholders": { + "chars": { + "content": "$1", + "example": "!@#$%^&*" + } + } + }, + "masterPasswordPolicyRequirementsNotMet": { + "message": "Your new master password does not meet the policy requirements." } }