mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-09 09:51:02 +01:00
[Reset Password] Update CLI to use auth result for forced reset (#505)
* [Reset Password] Update CLI to use auth result for forced reset * Fixed lint issues * Requested Changes and fixed sync bug * Only update password is not using an API key for auth
This commit is contained in:
parent
764dc40b36
commit
61ffb4f5d9
@ -10,14 +10,20 @@ import { ErrorResponse } from 'jslib-common/models/response/errorResponse';
|
|||||||
|
|
||||||
import { ApiService } from 'jslib-common/abstractions/api.service';
|
import { ApiService } from 'jslib-common/abstractions/api.service';
|
||||||
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
import { AuthService } from 'jslib-common/abstractions/auth.service';
|
||||||
|
import { CryptoService } from 'jslib-common/abstractions/crypto.service';
|
||||||
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
import { CryptoFunctionService } from 'jslib-common/abstractions/cryptoFunction.service';
|
||||||
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
import { EnvironmentService } from 'jslib-common/abstractions/environment.service';
|
||||||
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
import { I18nService } from 'jslib-common/abstractions/i18n.service';
|
||||||
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
import { PasswordGenerationService } from 'jslib-common/abstractions/passwordGeneration.service';
|
||||||
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.service';
|
||||||
|
import { PolicyService } from 'jslib-common/abstractions/policy.service';
|
||||||
|
import { SyncService } from 'jslib-common/abstractions/sync.service';
|
||||||
|
import { UserService } from 'jslib-common/abstractions/user.service';
|
||||||
|
|
||||||
import { Response } from '../models/response';
|
import { Response } from '../models/response';
|
||||||
|
|
||||||
|
import { UpdateTempPasswordRequest } from 'jslib-common/models/request/updateTempPasswordRequest';
|
||||||
|
|
||||||
import { MessageResponse } from '../models/response/messageResponse';
|
import { MessageResponse } from '../models/response/messageResponse';
|
||||||
|
|
||||||
import { NodeUtils } from 'jslib-common/misc/nodeUtils';
|
import { NodeUtils } from 'jslib-common/misc/nodeUtils';
|
||||||
@ -29,9 +35,11 @@ const open = require('open');
|
|||||||
export class LoginCommand {
|
export class LoginCommand {
|
||||||
protected validatedParams: () => Promise<any>;
|
protected validatedParams: () => Promise<any>;
|
||||||
protected success: () => Promise<MessageResponse>;
|
protected success: () => Promise<MessageResponse>;
|
||||||
|
protected logout: () => Promise<void>;
|
||||||
protected canInteract: boolean;
|
protected canInteract: boolean;
|
||||||
protected clientId: string;
|
protected clientId: string;
|
||||||
protected clientSecret: string;
|
protected clientSecret: string;
|
||||||
|
protected email: string;
|
||||||
|
|
||||||
private ssoRedirectUri: string = null;
|
private ssoRedirectUri: string = null;
|
||||||
|
|
||||||
@ -39,7 +47,8 @@ export class LoginCommand {
|
|||||||
protected i18nService: I18nService, protected environmentService: EnvironmentService,
|
protected i18nService: I18nService, protected environmentService: EnvironmentService,
|
||||||
protected passwordGenerationService: PasswordGenerationService,
|
protected passwordGenerationService: PasswordGenerationService,
|
||||||
protected cryptoFunctionService: CryptoFunctionService, protected platformUtilsService: PlatformUtilsService,
|
protected cryptoFunctionService: CryptoFunctionService, protected platformUtilsService: PlatformUtilsService,
|
||||||
clientId: string) {
|
protected userService: UserService, protected cryptoService: CryptoService,
|
||||||
|
protected policyService: PolicyService, clientId: string, private syncService: SyncService) {
|
||||||
this.clientId = clientId;
|
this.clientId = clientId;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,6 +98,7 @@ export class LoginCommand {
|
|||||||
if (email.indexOf('@') === -1) {
|
if (email.indexOf('@') === -1) {
|
||||||
return Response.badRequest('Email address is invalid.');
|
return Response.badRequest('Email address is invalid.');
|
||||||
}
|
}
|
||||||
|
this.email = email;
|
||||||
|
|
||||||
if (password == null || password === '') {
|
if (password == null || password === '') {
|
||||||
if (options.passwordfile) {
|
if (options.passwordfile) {
|
||||||
@ -244,6 +254,19 @@ export class LoginCommand {
|
|||||||
' through the web vault to set your master password.');
|
' through the web vault to set your master password.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle Updating Temp Password if NOT using an API Key for authentication
|
||||||
|
if (response.forcePasswordReset && (clientId == null && clientSecret == null)) {
|
||||||
|
await this.syncService.fullSync(true);
|
||||||
|
return await this.updateTempPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
return await this.handleSuccessResponse();
|
||||||
|
} catch (e) {
|
||||||
|
return Response.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async handleSuccessResponse(): Promise<Response> {
|
||||||
if (this.success != null) {
|
if (this.success != null) {
|
||||||
const res = await this.success();
|
const res = await this.success();
|
||||||
return Response.success(res);
|
return Response.success(res);
|
||||||
@ -251,11 +274,115 @@ export class LoginCommand {
|
|||||||
const res = new MessageResponse('You are logged in!', null);
|
const res = new MessageResponse('You are logged in!', null);
|
||||||
return Response.success(res);
|
return Response.success(res);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async updateTempPassword(error?: string): Promise<Response> {
|
||||||
|
// If no interaction available, alert user to use web vault
|
||||||
|
if (!this.canInteract) {
|
||||||
|
await this.logout();
|
||||||
|
this.authService.logOut(() => { /* Do nothing */ });
|
||||||
|
return Response.error(new MessageResponse('An organization administrator recently changed your master password. In order to access the vault, you must update your master password now via the web vault. You have been logged out.', null));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.email == null || this.email === 'undefined') {
|
||||||
|
this.email = await this.userService.getEmail();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get New Master Password
|
||||||
|
const baseMessage = 'An organization administrator recently changed your master password. In order to access the vault, you must update your master password now.\n' + 'Master password: ';
|
||||||
|
const firstMessage = error != null ? error + baseMessage : baseMessage;
|
||||||
|
const mp: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({
|
||||||
|
type: 'password',
|
||||||
|
name: 'password',
|
||||||
|
message: firstMessage,
|
||||||
|
});
|
||||||
|
const masterPassword = mp.password;
|
||||||
|
|
||||||
|
// Master Password Validation
|
||||||
|
if (masterPassword == null || masterPassword === '') {
|
||||||
|
return this.updateTempPassword('Master password is required.\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (masterPassword.length < 8) {
|
||||||
|
return this.updateTempPassword('Master password must be at least 8 characters long.\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strength & Policy Validation
|
||||||
|
const strengthResult = this.passwordGenerationService.passwordStrength(masterPassword,
|
||||||
|
this.getPasswordStrengthUserInput());
|
||||||
|
|
||||||
|
// Get New Master Password Re-type
|
||||||
|
const reTypeMessage = 'Re-type New Master password (Strength: ' + strengthResult.score + ')';
|
||||||
|
const retype: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({
|
||||||
|
type: 'password',
|
||||||
|
name: 'password',
|
||||||
|
message: reTypeMessage,
|
||||||
|
});
|
||||||
|
const masterPasswordRetype = retype.password;
|
||||||
|
|
||||||
|
// Re-type Validation
|
||||||
|
if (masterPassword !== masterPasswordRetype) {
|
||||||
|
return this.updateTempPassword('Master password confirmation does not match.\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Hint (optional)
|
||||||
|
const hint: inquirer.Answers = await inquirer.createPromptModule({ output: process.stderr })({
|
||||||
|
type: 'input',
|
||||||
|
name: 'input',
|
||||||
|
message: 'Master Password Hint (optional):',
|
||||||
|
});
|
||||||
|
const masterPasswordHint = hint.input;
|
||||||
|
|
||||||
|
// Retrieve details for key generation
|
||||||
|
const enforcedPolicyOptions = await this.policyService.getMasterPasswordPolicyOptions();
|
||||||
|
const kdf = await this.userService.getKdf();
|
||||||
|
const kdfIterations = await this.userService.getKdfIterations();
|
||||||
|
|
||||||
|
if (enforcedPolicyOptions != null &&
|
||||||
|
!this.policyService.evaluateMasterPassword(
|
||||||
|
strengthResult.score,
|
||||||
|
masterPassword,
|
||||||
|
enforcedPolicyOptions)) {
|
||||||
|
return this.updateTempPassword('Your new master password does not meet the policy requirements.\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Create new key and hash new password
|
||||||
|
const newKey = await this.cryptoService.makeKey(masterPassword, this.email.trim().toLowerCase(),
|
||||||
|
kdf, kdfIterations);
|
||||||
|
const newPasswordHash = await this.cryptoService.hashPassword(masterPassword, newKey);
|
||||||
|
|
||||||
|
// Grab user's current enc key
|
||||||
|
const userEncKey = await this.cryptoService.getEncKey();
|
||||||
|
|
||||||
|
// Create new encKey for the User
|
||||||
|
const newEncKey = await this.cryptoService.remakeEncKey(newKey, userEncKey);
|
||||||
|
|
||||||
|
// Create request
|
||||||
|
const request = new UpdateTempPasswordRequest();
|
||||||
|
request.key = newEncKey[1].encryptedString;
|
||||||
|
request.newMasterPasswordHash = newPasswordHash;
|
||||||
|
request.masterPasswordHint = masterPasswordHint;
|
||||||
|
|
||||||
|
// Update user's password
|
||||||
|
await this.apiService.putUpdateTempPassword(request);
|
||||||
|
return this.handleSuccessResponse();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
await this.logout();
|
||||||
|
this.authService.logOut(() => { /* Do nothing */ });
|
||||||
return Response.error(e);
|
return Response.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getPasswordStrengthUserInput() {
|
||||||
|
let userInput: string[] = [];
|
||||||
|
const atPosition = this.email.indexOf('@');
|
||||||
|
if (atPosition > -1) {
|
||||||
|
userInput = userInput.concat(this.email.substr(0, atPosition).trim().toLowerCase().split(/[^A-Za-z0-9]/));
|
||||||
|
}
|
||||||
|
return userInput;
|
||||||
|
}
|
||||||
|
|
||||||
private async apiClientId(): Promise<string> {
|
private async apiClientId(): Promise<string> {
|
||||||
let clientId: string = null;
|
let clientId: string = null;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user