From ef743ea8ca3d4c3b05077ba72d391ab89347848f Mon Sep 17 00:00:00 2001 From: Vincent Salucci <26154748+vincentsalucci@users.noreply.github.com> Date: Fri, 3 Sep 2021 14:49:03 -0500 Subject: [PATCH] [SSO] Set password auto enroll update (#472) * [SSO/Auto Enroll] Set Password enrolls new user * Fixed typo * Linter updates * Cleanup // Constructor for SetPasswordRequest --- .../src/components/set-password.component.ts | 83 ++++++++++++++----- common/src/abstractions/user.service.ts | 1 + .../src/models/request/setPasswordRequest.ts | 11 +++ common/src/services/user.service.ts | 9 ++ 4 files changed, 83 insertions(+), 21 deletions(-) diff --git a/angular/src/components/set-password.component.ts b/angular/src/components/set-password.component.ts index 784b063a93..2b8a11de12 100644 --- a/angular/src/components/set-password.component.ts +++ b/angular/src/components/set-password.component.ts @@ -18,12 +18,16 @@ import { EncString } from 'jslib-common/models/domain/encString'; import { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey'; import { KeysRequest } from 'jslib-common/models/request/keysRequest'; +import { OrganizationUserResetPasswordEnrollmentRequest } from 'jslib-common/models/request/organizationUserResetPasswordEnrollmentRequest'; import { SetPasswordRequest } from 'jslib-common/models/request/setPasswordRequest'; import { ChangePasswordComponent as BaseChangePasswordComponent } from './change-password.component'; import { HashPurpose } from 'jslib-common/enums/hashPurpose'; import { KdfType } from 'jslib-common/enums/kdfType'; +import { PolicyType } from 'jslib-common/enums/policyType'; + +import { Utils } from 'jslib-common/misc/utils'; @Directive() export class SetPasswordComponent extends BaseChangePasswordComponent { @@ -31,6 +35,8 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { showPassword: boolean = false; hint: string = ''; identifier: string = null; + orgId: string; + resetPasswordAutoEnroll = false; onSuccessfulChangePassword: () => Promise; successRoute = 'vault'; @@ -57,6 +63,15 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { } }); + // Automatic Enrollment Detection + if (this.identifier != null) { + const org = await this.userService.getOrganizationByIdentifier(this.identifier); + this.orgId = org?.id; + const policyList = await this.policyService.getAll(PolicyType.ResetPassword); + const policyResult = this.policyService.getResetPasswordPolicyOptions(policyList, this.orgId); + this.resetPasswordAutoEnroll = policyResult[1] && policyResult[0].autoEnrollEnabled; + } + super.ngOnInit(); } @@ -69,31 +84,45 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { async performSubmitActions(masterPasswordHash: string, key: SymmetricCryptoKey, encKey: [SymmetricCryptoKey, EncString]) { - const request = new SetPasswordRequest(); - request.masterPasswordHash = masterPasswordHash; - request.key = encKey[1].encryptedString; - request.masterPasswordHint = this.hint; - request.kdf = this.kdf; - request.kdfIterations = this.kdfIterations; - request.orgIdentifier = this.identifier; - const keys = await this.cryptoService.makeKeyPair(encKey[0]); - request.keys = new KeysRequest(keys[0], keys[1].encryptedString); - + const request = new SetPasswordRequest( + masterPasswordHash, + encKey[1].encryptedString, + this.hint, + this.kdf, + this.kdfIterations, + this.identifier, + new KeysRequest(keys[0], keys[1].encryptedString) + ); try { - this.formPromise = this.apiService.setPassword(request); + if (this.resetPasswordAutoEnroll) { + this.formPromise = this.apiService.setPassword(request).then(async () => { + await this.onSetPasswordSuccess(key, encKey, keys); + return this.apiService.getOrganizationKeys(this.orgId); + }).then(async response => { + if (response == null) { + throw new Error(this.i18nService.t('resetPasswordOrgKeysError')); + } + const userId = await this.userService.getUserId(); + const publicKey = Utils.fromB64ToArray(response.publicKey); + + // RSA Encrypt user's encKey.key with organization public key + const userEncKey = await this.cryptoService.getEncKey(); + const encryptedKey = await this.cryptoService.rsaEncrypt(userEncKey.key, publicKey.buffer); + + const resetRequest = new OrganizationUserResetPasswordEnrollmentRequest(); + resetRequest.resetPasswordKey = encryptedKey.encryptedString; + + return this.apiService.putOrganizationUserResetPasswordEnrollment(this.orgId, userId, resetRequest); + }); + } else { + this.formPromise = this.apiService.setPassword(request).then(async () => { + await this.onSetPasswordSuccess(key, encKey, keys); + }); + } + await this.formPromise; - await this.userService.setInformation(await this.userService.getUserId(), await this.userService.getEmail(), - this.kdf, this.kdfIterations); - await this.cryptoService.setKey(key); - await this.cryptoService.setEncKey(encKey[1].encryptedString); - await this.cryptoService.setEncPrivateKey(keys[1].encryptedString); - - const localKeyHash = await this.cryptoService.hashPassword(this.masterPassword, key, - HashPurpose.LocalAuthorization); - await this.cryptoService.setKeyHash(localKeyHash); - if (this.onSuccessfulChangePassword != null) { this.onSuccessfulChangePassword(); } else { @@ -108,4 +137,16 @@ export class SetPasswordComponent extends BaseChangePasswordComponent { this.showPassword = !this.showPassword; document.getElementById(confirmField ? 'masterPasswordRetype' : 'masterPassword').focus(); } + + private async onSetPasswordSuccess(key: SymmetricCryptoKey, encKey: [SymmetricCryptoKey, EncString], keys: [string, EncString]) { + await this.userService.setInformation(await this.userService.getUserId(), await this.userService.getEmail(), + this.kdf, this.kdfIterations); + await this.cryptoService.setKey(key); + await this.cryptoService.setEncKey(encKey[1].encryptedString); + await this.cryptoService.setEncPrivateKey(keys[1].encryptedString); + + const localKeyHash = await this.cryptoService.hashPassword(this.masterPassword, key, + HashPurpose.LocalAuthorization); + await this.cryptoService.setKeyHash(localKeyHash); + } } diff --git a/common/src/abstractions/user.service.ts b/common/src/abstractions/user.service.ts index 1cdf453dd4..0cf6dc77c9 100644 --- a/common/src/abstractions/user.service.ts +++ b/common/src/abstractions/user.service.ts @@ -21,6 +21,7 @@ export abstract class UserService { isAuthenticated: () => Promise; canAccessPremium: () => Promise; getOrganization: (id: string) => Promise; + getOrganizationByIdentifier: (identifier: string) => Promise; getAllOrganizations: () => Promise; replaceOrganizations: (organizations: { [id: string]: OrganizationData; }) => Promise; clearOrganizations: (userId: string) => Promise; diff --git a/common/src/models/request/setPasswordRequest.ts b/common/src/models/request/setPasswordRequest.ts index fab35c4136..70c77e622e 100644 --- a/common/src/models/request/setPasswordRequest.ts +++ b/common/src/models/request/setPasswordRequest.ts @@ -10,4 +10,15 @@ export class SetPasswordRequest { kdf: KdfType; kdfIterations: number; orgIdentifier: string; + + constructor(masterPasswordHash: string, key: string, masterPasswordHint: string, kdf: KdfType, + kdfIterations: number, orgIdentifier: string, keys: KeysRequest) { + this.masterPasswordHash = masterPasswordHash; + this.key = key; + this.masterPasswordHint = masterPasswordHint; + this.kdf = kdf; + this.kdfIterations = kdfIterations; + this.orgIdentifier = orgIdentifier; + this.keys = keys; + } } diff --git a/common/src/services/user.service.ts b/common/src/services/user.service.ts index df99b369c8..f031babb6d 100644 --- a/common/src/services/user.service.ts +++ b/common/src/services/user.service.ts @@ -166,6 +166,15 @@ export class UserService implements UserServiceAbstraction { return new Organization(organizations[id]); } + async getOrganizationByIdentifier(identifier: string): Promise { + const organizations = await this.getAllOrganizations(); + if (organizations == null || organizations.length === 0) { + return null; + } + + return organizations.find(o => o.identifier === identifier); + } + async getAllOrganizations(): Promise { const userId = await this.getUserId(); const organizations = await this.storageService.get<{ [id: string]: OrganizationData; }>(