import { Directive } from '@angular/core'; import { ActivatedRoute, Router } from '@angular/router'; import { first } from 'rxjs/operators'; import { ApiService } from 'jslib-common/abstractions/api.service'; import { CryptoService } from 'jslib-common/abstractions/crypto.service'; import { I18nService } from 'jslib-common/abstractions/i18n.service'; import { MessagingService } from 'jslib-common/abstractions/messaging.service'; import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service'; import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service'; import { PolicyService } from 'jslib-common/abstractions/policy.service'; import { StateService } from 'jslib-common/abstractions/state.service'; import { SyncService } from 'jslib-common/abstractions/sync.service'; 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 { Utils } from 'jslib-common/misc/utils'; @Directive() export class SetPasswordComponent extends BaseChangePasswordComponent { syncLoading: boolean = true; showPassword: boolean = false; hint: string = ''; identifier: string = null; orgId: string; resetPasswordAutoEnroll = false; onSuccessfulChangePassword: () => Promise; successRoute = 'vault'; constructor(i18nService: I18nService, cryptoService: CryptoService, messagingService: MessagingService, passwordGenerationService: PasswordGenerationService, platformUtilsService: PlatformUtilsService, policyService: PolicyService, protected router: Router, private apiService: ApiService, private syncService: SyncService, private route: ActivatedRoute, stateService: StateService) { super(i18nService, cryptoService, messagingService, passwordGenerationService, platformUtilsService, policyService, stateService); } async ngOnInit() { await this.syncService.fullSync(true); this.syncLoading = false; this.route.queryParams.pipe(first()).subscribe(async qParams => { if (qParams.identifier != null) { this.identifier = qParams.identifier; } }); // Automatic Enrollment Detection if (this.identifier != null) { try { const response = await this.apiService.getOrganizationAutoEnrollStatus(this.identifier); this.orgId = response.id; this.resetPasswordAutoEnroll = response.resetPasswordEnabled; } catch { this.platformUtilsService.showToast('error', null, this.i18nService.t('errorOccurred')); } } super.ngOnInit(); } async setupSubmitActions() { this.kdf = KdfType.PBKDF2_SHA256; const useLowerKdf = this.platformUtilsService.isIE(); this.kdfIterations = useLowerKdf ? 10000 : 100000; return true; } async performSubmitActions(masterPasswordHash: string, key: SymmetricCryptoKey, encKey: [SymmetricCryptoKey, EncString]) { const keys = await this.cryptoService.makeKeyPair(encKey[0]); const request = new SetPasswordRequest( masterPasswordHash, encKey[1].encryptedString, this.hint, this.kdf, this.kdfIterations, this.identifier, new KeysRequest(keys[0], keys[1].encryptedString) ); try { 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.stateService.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; if (this.onSuccessfulChangePassword != null) { this.onSuccessfulChangePassword(); } else { this.router.navigate([this.successRoute]); } } catch { this.platformUtilsService.showToast('error', null, this.i18nService.t('errorOccurred')); } } togglePassword(confirmField: boolean) { this.showPassword = !this.showPassword; document.getElementById(confirmField ? 'masterPasswordRetype' : 'masterPassword').focus(); } private async onSetPasswordSuccess(key: SymmetricCryptoKey, encKey: [SymmetricCryptoKey, EncString], keys: [string, EncString]) { await this.stateService.setKdfType(this.kdf); await this.stateService.setKdfIterations(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); } }