mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 06:13:38 +00:00
[Key Connector] QA fixes for CLI and Desktop (#544)
* Make UserVerificationService compatible with CLI * Refactor error handling * Fix i18n key name * Add apiUseKeyConnector flag to TokenResponse * Always require keyConnectorUrl to be passed in * Throw errors in userVerificationService * Use requestOTP in UserVerificationService * Remove unused deps * Fix linting
This commit is contained in:
@@ -10,6 +10,7 @@ import {
|
|||||||
|
|
||||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||||
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
import { KeyConnectorService } from 'jslib-common/abstractions/keyConnector.service';
|
||||||
|
import { UserVerificationService } from 'jslib-common/abstractions/userVerification.service';
|
||||||
|
|
||||||
import { VerificationType } from 'jslib-common/enums/verificationType';
|
import { VerificationType } from 'jslib-common/enums/verificationType';
|
||||||
|
|
||||||
@@ -34,7 +35,8 @@ export class VerifyMasterPasswordComponent implements ControlValueAccessor, OnIn
|
|||||||
|
|
||||||
private onChange: (value: Verification) => void;
|
private onChange: (value: Verification) => void;
|
||||||
|
|
||||||
constructor(private keyConnectorService: KeyConnectorService, private apiService: ApiService) { }
|
constructor(private keyConnectorService: KeyConnectorService,
|
||||||
|
private userVerificationService: UserVerificationService) { }
|
||||||
|
|
||||||
async ngOnInit() {
|
async ngOnInit() {
|
||||||
this.usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
|
this.usesKeyConnector = await this.keyConnectorService.getUsesKeyConnector();
|
||||||
@@ -54,7 +56,7 @@ export class VerifyMasterPasswordComponent implements ControlValueAccessor, OnIn
|
|||||||
async requestOTP() {
|
async requestOTP() {
|
||||||
if (this.usesKeyConnector) {
|
if (this.usesKeyConnector) {
|
||||||
this.disableRequestOTP = true;
|
this.disableRequestOTP = true;
|
||||||
await this.apiService.postAccountRequestOTP();
|
await this.userVerificationService.requestOTP();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,4 +6,5 @@ export abstract class UserVerificationService {
|
|||||||
buildRequest: <T extends SecretVerificationRequest> (verification: Verification,
|
buildRequest: <T extends SecretVerificationRequest> (verification: Verification,
|
||||||
requestClass?: new () => T, alreadyHashed?: boolean) => Promise<T>;
|
requestClass?: new () => T, alreadyHashed?: boolean) => Promise<T>;
|
||||||
verifyUser: (verification: Verification) => Promise<boolean>;
|
verifyUser: (verification: Verification) => Promise<boolean>;
|
||||||
|
requestOTP: () => Promise<void>;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export class IdentityTokenResponse extends BaseResponse {
|
|||||||
kdf: KdfType;
|
kdf: KdfType;
|
||||||
kdfIterations: number;
|
kdfIterations: number;
|
||||||
forcePasswordReset: boolean;
|
forcePasswordReset: boolean;
|
||||||
|
apiUseKeyConnector: boolean;
|
||||||
keyConnectorUrl: string;
|
keyConnectorUrl: string;
|
||||||
|
|
||||||
constructor(response: any) {
|
constructor(response: any) {
|
||||||
@@ -31,6 +32,7 @@ export class IdentityTokenResponse extends BaseResponse {
|
|||||||
this.kdf = this.getResponseProperty('Kdf');
|
this.kdf = this.getResponseProperty('Kdf');
|
||||||
this.kdfIterations = this.getResponseProperty('KdfIterations');
|
this.kdfIterations = this.getResponseProperty('KdfIterations');
|
||||||
this.forcePasswordReset = this.getResponseProperty('ForcePasswordReset');
|
this.forcePasswordReset = this.getResponseProperty('ForcePasswordReset');
|
||||||
|
this.apiUseKeyConnector = this.getResponseProperty('ApiUseKeyConnector');
|
||||||
this.keyConnectorUrl = this.getResponseProperty('KeyConnectorUrl');
|
this.keyConnectorUrl = this.getResponseProperty('KeyConnectorUrl');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -370,8 +370,9 @@ export class AuthService implements AuthServiceAbstraction {
|
|||||||
|
|
||||||
if (tokenResponse.keyConnectorUrl != null) {
|
if (tokenResponse.keyConnectorUrl != null) {
|
||||||
await this.keyConnectorService.getAndSetKey(tokenResponse.keyConnectorUrl);
|
await this.keyConnectorService.getAndSetKey(tokenResponse.keyConnectorUrl);
|
||||||
} else if (this.environmentService.getKeyConnectorUrl() != null) {
|
} else if (tokenResponse.apiUseKeyConnector) {
|
||||||
await this.keyConnectorService.getAndSetKey();
|
const keyConnectorUrl = this.environmentService.getKeyConnectorUrl();
|
||||||
|
await this.keyConnectorService.getAndSetKey(keyConnectorUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.cryptoService.setEncKey(tokenResponse.key);
|
await this.cryptoService.setEncKey(tokenResponse.key);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { ApiService } from '../abstractions/api.service';
|
import { ApiService } from '../abstractions/api.service';
|
||||||
import { CryptoService } from '../abstractions/crypto.service';
|
import { CryptoService } from '../abstractions/crypto.service';
|
||||||
import { EnvironmentService } from '../abstractions/environment.service';
|
|
||||||
import { KeyConnectorService as KeyConnectorServiceAbstraction } from '../abstractions/keyConnector.service';
|
import { KeyConnectorService as KeyConnectorServiceAbstraction } from '../abstractions/keyConnector.service';
|
||||||
import { LogService } from '../abstractions/log.service';
|
import { LogService } from '../abstractions/log.service';
|
||||||
import { StorageService } from '../abstractions/storage.service';
|
import { StorageService } from '../abstractions/storage.service';
|
||||||
@@ -25,8 +24,7 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
|
|||||||
|
|
||||||
constructor(private storageService: StorageService, private userService: UserService,
|
constructor(private storageService: StorageService, private userService: UserService,
|
||||||
private cryptoService: CryptoService, private apiService: ApiService,
|
private cryptoService: CryptoService, private apiService: ApiService,
|
||||||
private environmentService: EnvironmentService, private tokenService: TokenService,
|
private tokenService: TokenService, private logService: LogService) { }
|
||||||
private logService: LogService) { }
|
|
||||||
|
|
||||||
setUsesKeyConnector(usesKeyConnector: boolean) {
|
setUsesKeyConnector(usesKeyConnector: boolean) {
|
||||||
this.usesKeyConnector = usesKeyConnector;
|
this.usesKeyConnector = usesKeyConnector;
|
||||||
@@ -59,15 +57,7 @@ export class KeyConnectorService implements KeyConnectorServiceAbstraction {
|
|||||||
await this.apiService.postConvertToKeyConnector();
|
await this.apiService.postConvertToKeyConnector();
|
||||||
}
|
}
|
||||||
|
|
||||||
async getAndSetKey(url?: string) {
|
async getAndSetKey(url: string) {
|
||||||
if (url == null) {
|
|
||||||
url = this.environmentService.getKeyConnectorUrl();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url == null) {
|
|
||||||
throw new Error('No Key Connector URL found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const userKeyResponse = await this.apiService.getUserKeyFromKeyConnector(url);
|
const userKeyResponse = await this.apiService.getUserKeyFromKeyConnector(url);
|
||||||
const keyArr = Utils.fromB64ToArray(userKeyResponse.key);
|
const keyArr = Utils.fromB64ToArray(userKeyResponse.key);
|
||||||
|
|||||||
@@ -1,12 +1,8 @@
|
|||||||
import { Injectable } from '@angular/core';
|
|
||||||
|
|
||||||
import { UserVerificationService as UserVerificationServiceAbstraction } from '../abstractions/userVerification.service';
|
import { UserVerificationService as UserVerificationServiceAbstraction } from '../abstractions/userVerification.service';
|
||||||
|
|
||||||
import { ApiService } from '../abstractions/api.service';
|
import { ApiService } from '../abstractions/api.service';
|
||||||
import { CryptoService } from '../abstractions/crypto.service';
|
import { CryptoService } from '../abstractions/crypto.service';
|
||||||
import { I18nService } from '../abstractions/i18n.service';
|
import { I18nService } from '../abstractions/i18n.service';
|
||||||
import { LogService } from '../abstractions/log.service';
|
|
||||||
import { PlatformUtilsService } from '../abstractions/platformUtils.service';
|
|
||||||
|
|
||||||
import { VerificationType } from '../enums/verificationType';
|
import { VerificationType } from '../enums/verificationType';
|
||||||
|
|
||||||
@@ -15,17 +11,13 @@ import { SecretVerificationRequest } from '../models/request/secretVerificationR
|
|||||||
|
|
||||||
import { Verification } from '../types/verification';
|
import { Verification } from '../types/verification';
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class UserVerificationService implements UserVerificationServiceAbstraction {
|
export class UserVerificationService implements UserVerificationServiceAbstraction {
|
||||||
constructor(private cryptoService: CryptoService, private i18nService: I18nService,
|
constructor(private cryptoService: CryptoService, private i18nService: I18nService,
|
||||||
private platformUtilsService: PlatformUtilsService, private apiService: ApiService,
|
private apiService: ApiService) { }
|
||||||
private logService: LogService) { }
|
|
||||||
|
|
||||||
async buildRequest<T extends SecretVerificationRequest>(verification: Verification,
|
async buildRequest<T extends SecretVerificationRequest>(verification: Verification,
|
||||||
requestClass?: new () => T, alreadyHashed?: boolean) {
|
requestClass?: new () => T, alreadyHashed?: boolean) {
|
||||||
if (verification?.secret == null || verification.secret === '') {
|
this.validateInput(verification);
|
||||||
throw new Error('No secret provided for verification.');
|
|
||||||
}
|
|
||||||
|
|
||||||
const request = requestClass != null
|
const request = requestClass != null
|
||||||
? new requestClass()
|
? new requestClass()
|
||||||
@@ -43,28 +35,35 @@ export class UserVerificationService implements UserVerificationServiceAbstracti
|
|||||||
}
|
}
|
||||||
|
|
||||||
async verifyUser(verification: Verification): Promise<boolean> {
|
async verifyUser(verification: Verification): Promise<boolean> {
|
||||||
if (verification?.secret == null || verification.secret === '') {
|
this.validateInput(verification);
|
||||||
throw new Error('No secret provided for verification.');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verification.type === VerificationType.OTP) {
|
if (verification.type === VerificationType.OTP) {
|
||||||
const request = new VerifyOTPRequest(verification.secret);
|
const request = new VerifyOTPRequest(verification.secret);
|
||||||
try {
|
try {
|
||||||
await this.apiService.postAccountVerifyOTP(request);
|
await this.apiService.postAccountVerifyOTP(request);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error(e);
|
throw new Error(this.i18nService.t('invalidVerificationCode'));
|
||||||
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
|
|
||||||
this.i18nService.t('invalidVerificationCode'));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const passwordValid = await this.cryptoService.compareAndUpdateKeyHash(verification.secret, null);
|
const passwordValid = await this.cryptoService.compareAndUpdateKeyHash(verification.secret, null);
|
||||||
if (!passwordValid) {
|
if (!passwordValid) {
|
||||||
this.platformUtilsService.showToast('error', this.i18nService.t('errorOccurred'),
|
throw new Error(this.i18nService.t('invalidMasterPassword'));
|
||||||
this.i18nService.t('invalidMasterPassword'));
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async requestOTP() {
|
||||||
|
await this.apiService.postAccountRequestOTP();
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateInput(verification: Verification) {
|
||||||
|
if (verification?.secret == null || verification.secret === '') {
|
||||||
|
if (verification.type === VerificationType.OTP) {
|
||||||
|
throw new Error(this.i18nService.t('verificationCodeRequired'));
|
||||||
|
} else {
|
||||||
|
throw new Error(this.i18nService.t('masterPassRequired'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user