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 { SymmetricCryptoKey } from 'jslib-common/models/domain/symmetricCryptoKey';
import { KeysRequest } from 'jslib-common/models/request/keysRequest'; 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 { SetPasswordRequest } from 'jslib-common/models/request/setPasswordRequest';
import { ChangePasswordComponent as BaseChangePasswordComponent } from './change-password.component'; import { ChangePasswordComponent as BaseChangePasswordComponent } from './change-password.component';
import { HashPurpose } from 'jslib-common/enums/hashPurpose'; import { HashPurpose } from 'jslib-common/enums/hashPurpose';
import { KdfType } from 'jslib-common/enums/kdfType'; import { KdfType } from 'jslib-common/enums/kdfType';
import { PolicyType } from 'jslib-common/enums/policyType';
import { Utils } from 'jslib-common/misc/utils';
@Directive() @Directive()
export class SetPasswordComponent extends BaseChangePasswordComponent { export class SetPasswordComponent extends BaseChangePasswordComponent {
@ -31,6 +35,8 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
showPassword: boolean = false; showPassword: boolean = false;
hint: string = ''; hint: string = '';
identifier: string = null; identifier: string = null;
orgId: string;
resetPasswordAutoEnroll = false;
onSuccessfulChangePassword: () => Promise<any>; onSuccessfulChangePassword: () => Promise<any>;
successRoute = 'vault'; 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(); super.ngOnInit();
} }
@ -69,31 +84,45 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
async performSubmitActions(masterPasswordHash: string, key: SymmetricCryptoKey, async performSubmitActions(masterPasswordHash: string, key: SymmetricCryptoKey,
encKey: [SymmetricCryptoKey, EncString]) { 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]); 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 { 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.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) { if (this.onSuccessfulChangePassword != null) {
this.onSuccessfulChangePassword(); this.onSuccessfulChangePassword();
} else { } else {
@ -108,4 +137,16 @@ export class SetPasswordComponent extends BaseChangePasswordComponent {
this.showPassword = !this.showPassword; this.showPassword = !this.showPassword;
document.getElementById(confirmField ? 'masterPasswordRetype' : 'masterPassword').focus(); 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>; isAuthenticated: () => Promise<boolean>;
canAccessPremium: () => Promise<boolean>; canAccessPremium: () => Promise<boolean>;
getOrganization: (id: string) => Promise<Organization>; getOrganization: (id: string) => Promise<Organization>;
getOrganizationByIdentifier: (identifier: string) => Promise<Organization>;
getAllOrganizations: () => Promise<Organization[]>; getAllOrganizations: () => Promise<Organization[]>;
replaceOrganizations: (organizations: { [id: string]: OrganizationData; }) => Promise<any>; replaceOrganizations: (organizations: { [id: string]: OrganizationData; }) => Promise<any>;
clearOrganizations: (userId: string) => Promise<any>; clearOrganizations: (userId: string) => Promise<any>;

View File

@ -10,4 +10,15 @@ export class SetPasswordRequest {
kdf: KdfType; kdf: KdfType;
kdfIterations: number; kdfIterations: number;
orgIdentifier: string; 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]); 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[]> { async getAllOrganizations(): Promise<Organization[]> {
const userId = await this.getUserId(); const userId = await this.getUserId();
const organizations = await this.storageService.get<{ [id: string]: OrganizationData; }>( const organizations = await this.storageService.get<{ [id: string]: OrganizationData; }>(