1
0
mirror of https://github.com/bitwarden/browser.git synced 2024-11-06 09:20:43 +01:00

[SSO] Set password auto enroll update (#472)

* [SSO/Auto Enroll] Set Password enrolls new user

* Fixed typo

* Linter updates

* Cleanup // Constructor for SetPasswordRequest
This commit is contained in:
Vincent Salucci 2021-09-03 14:49:03 -05:00 committed by GitHub
parent 6c9485596c
commit ef743ea8ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 83 additions and 21 deletions

View File

@ -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<any>;
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);
}
}

View File

@ -21,6 +21,7 @@ export abstract class UserService {
isAuthenticated: () => Promise<boolean>;
canAccessPremium: () => Promise<boolean>;
getOrganization: (id: string) => Promise<Organization>;
getOrganizationByIdentifier: (identifier: string) => Promise<Organization>;
getAllOrganizations: () => Promise<Organization[]>;
replaceOrganizations: (organizations: { [id: string]: OrganizationData; }) => Promise<any>;
clearOrganizations: (userId: string) => Promise<any>;

View File

@ -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;
}
}

View File

@ -166,6 +166,15 @@ export class UserService implements UserServiceAbstraction {
return new Organization(organizations[id]);
}
async getOrganizationByIdentifier(identifier: string): Promise<Organization> {
const organizations = await this.getAllOrganizations();
if (organizations == null || organizations.length === 0) {
return null;
}
return organizations.find(o => o.identifier === identifier);
}
async getAllOrganizations(): Promise<Organization[]> {
const userId = await this.getUserId();
const organizations = await this.storageService.get<{ [id: string]: OrganizationData; }>(