1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-03-02 03:41:09 +01:00

Merge pull request #1995 from bitwarden/m-rpv1-force-reset

This commit is contained in:
Addison Beck 2021-08-11 15:50:15 -04:00 committed by GitHub
commit a61598253c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 216 additions and 9 deletions

View File

@ -1752,5 +1752,14 @@
},
"emailVerificationRequiredDesc": {
"message": "You must verify your email to use this feature. You can verify your email in the web vault."
},
"updatedMasterPassword": {
"message": "Updated Master Password"
},
"updateMasterPassword": {
"message": "Update Master Password"
},
"updateMasterPasswordWarning": {
"message": "Your Master Password was recently changed by an administrator in your organization. In order to access the vault, you must update it now. Proceeding will log you out of your current session, requiring you to log back in. Active sessions on other devices may continue to remain active for up to one hour."
}
}

View File

@ -10,6 +10,7 @@ import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.se
import { StateService } from 'jslib-common/abstractions/state.service';
import { StorageService } from 'jslib-common/abstractions/storage.service';
import { SyncService } from 'jslib-common/abstractions/sync.service';
import { UserService } from 'jslib-common/abstractions/user.service';
import { LoginComponent as BaseLoginComponent } from 'jslib-angular/components/login.component';
@ -23,10 +24,14 @@ export class LoginComponent extends BaseLoginComponent {
protected stateService: StateService, protected environmentService: EnvironmentService,
protected passwordGenerationService: PasswordGenerationService,
protected cryptoFunctionService: CryptoFunctionService, storageService: StorageService,
syncService: SyncService) {
syncService: SyncService, private userService: UserService) {
super(authService, router, platformUtilsService, i18nService, stateService, environmentService, passwordGenerationService, cryptoFunctionService, storageService);
super.onSuccessfulLogin = () => {
return syncService.fullSync(true);
super.onSuccessfulLogin = async () => {
await syncService.fullSync(true).then(async () => {
if (await this.userService.getForcePasswordReset()) {
this.router.navigate(['update-temp-password']);
}
});
};
super.successRoute = '/tabs/vault';
}

View File

@ -31,6 +31,13 @@ export class SetPasswordComponent extends BaseSetPasswordComponent {
syncService: SyncService, route: ActivatedRoute) {
super(i18nService, cryptoService, messagingService, userService, passwordGenerationService,
platformUtilsService, policyService, router, apiService, syncService, route);
super.onSuccessfulChangePassword = async () => {
if (await this.userService.getForcePasswordReset()) {
this.router.navigate(['update-temp-password']);
} else {
this.router.navigate([this.successRoute]);
}
};
}
get masterPasswordScoreWidth() {

View File

@ -15,6 +15,7 @@ import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.se
import { StateService } from 'jslib-common/abstractions/state.service';
import { StorageService } from 'jslib-common/abstractions/storage.service';
import { SyncService } from 'jslib-common/abstractions/sync.service';
import { UserService } from 'jslib-common/abstractions/user.service';
import { SsoComponent as BaseSsoComponent } from 'jslib-angular/components/sso.component';
import { BrowserApi } from '../../browser/browserApi';
@ -29,7 +30,7 @@ export class SsoComponent extends BaseSsoComponent {
storageService: StorageService, stateService: StateService,
platformUtilsService: PlatformUtilsService, apiService: ApiService,
cryptoFunctionService: CryptoFunctionService, passwordGenerationService: PasswordGenerationService,
syncService: SyncService, environmentService: EnvironmentService) {
syncService: SyncService, environmentService: EnvironmentService, private userService: UserService) {
super(authService, router, i18nService, route, storageService, stateService, platformUtilsService,
apiService, cryptoFunctionService, environmentService, passwordGenerationService);
@ -44,5 +45,13 @@ export class SsoComponent extends BaseSsoComponent {
const thisWindow = window.open('', '_self');
thisWindow.close();
};
super.onSuccessfulLoginNavigate = async () => {
if (await this.userService.getForcePasswordReset()) {
this.router.navigate(['update-temp-password']);
} else {
this.router.navigate([this.successRoute]);
}
};
}
}

View File

@ -20,6 +20,7 @@ import { PlatformUtilsService } from 'jslib-common/abstractions/platformUtils.se
import { StateService } from 'jslib-common/abstractions/state.service';
import { StorageService } from 'jslib-common/abstractions/storage.service';
import { SyncService } from 'jslib-common/abstractions/sync.service';
import { UserService } from 'jslib-common/abstractions/user.service';
import { BroadcasterService } from 'jslib-angular/services/broadcaster.service';
@ -44,11 +45,16 @@ export class TwoFactorComponent extends BaseTwoFactorComponent {
environmentService: EnvironmentService, private ngZone: NgZone,
private broadcasterService: BroadcasterService, private changeDetectorRef: ChangeDetectorRef,
private popupUtilsService: PopupUtilsService, stateService: StateService,
storageService: StorageService, route: ActivatedRoute, private messagingService: MessagingService) {
storageService: StorageService, route: ActivatedRoute, private messagingService: MessagingService,
private userService: UserService) {
super(authService, router, i18nService, apiService, platformUtilsService, window, environmentService,
stateService, storageService, route);
super.onSuccessfulLogin = () => {
return syncService.fullSync(true);
super.onSuccessfulLogin = async () => {
return syncService.fullSync(true).then(async () => {
if (await this.userService.getForcePasswordReset()) {
this.router.navigate(['update-temp-password']);
};
});
};
super.successRoute = '/tabs/vault';
this.webAuthnNewTab = this.platformUtilsService.isFirefox() || this.platformUtilsService.isSafari();

View File

@ -0,0 +1,100 @@
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
<header>
<div class="left">
<a (click)="logOut()">{{'logOut' | i18n}}</a>
</div>
<div class="center">
<span class="title">{{'updateMasterPassword' | i18n}}</span>
</div>
<div class="right">
<button type="submit" appBlurClick [disabled]="form.loading">
<span [hidden]="form.loading">{{'submit' | i18n}}</span>
<i class="fa fa-spinner fa-lg fa-spin" [hidden]="!form.loading" aria-hidden="true"></i>
</button>
</div>
</header>
<content>
<app-callout type="warning" title="{{'updateMasterPassword' | i18n}}">
{{'updateMasterPasswordWarning' | i18n}}
</app-callout>
<app-callout type="info" *ngIf="enforcedPolicyOptions">
{{'masterPasswordPolicyInEffect' | i18n}}
<ul>
<li *ngIf="enforcedPolicyOptions?.minComplexity > 0">
{{'policyInEffectMinComplexity' | i18n : getPasswordScoreAlertDisplay()}}
</li>
<li *ngIf="enforcedPolicyOptions?.minLength > 0">
{{'policyInEffectMinLength' | i18n : enforcedPolicyOptions?.minLength.toString()}}
</li>
<li *ngIf="enforcedPolicyOptions?.requireUpper">{{'policyInEffectUppercase' | i18n}}</li>
<li *ngIf="enforcedPolicyOptions?.requireLower">{{'policyInEffectLowercase' | i18n}}</li>
<li *ngIf="enforcedPolicyOptions?.requireNumbers">{{'policyInEffectNumbers' | i18n}}</li>
<li *ngIf="enforcedPolicyOptions?.requireSpecial">
{{'policyInEffectSpecial' | i18n : '!@#$%^&*'}}
</li>
</ul>
</app-callout>
<div class="box">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<div class="box-content-row-flex">
<div class="row-main">
<label for="masterPassword">
{{'masterPass' | i18n}}
<strong class="sub-label text-{{masterPasswordScoreStyle.Color}}"
*ngIf="masterPasswordScoreStyle.Text">
{{masterPasswordScoreStyle.Text}}
</strong>
</label>
<input id="masterPassword" type="{{showPassword ? 'text' : 'password'}}"
name="MasterPassword" class="monospaced" [(ngModel)]="masterPassword" required
appInputVerbatim (input)="updatePasswordStrength()">
</div>
<div class="action-buttons">
<a class="row-btn" href="#" appStopClick appBlurClick
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(false)">
<i class="fa fa-lg" aria-hidden="true"
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
</a>
</div>
</div>
<div class="progress">
<div class="progress-bar bg-{{masterPasswordScoreStyle.Color}}" role="progressbar" aria-valuenow="0"
aria-valuemin="0" aria-valuemax="100" [ngStyle]="{width: (masterPasswordScoreStyle.Width + '%')}"
attr.aria-valuenow="{{masterPasswordScoreStyle.Width}}"></div>
</div>
</div>
</div>
</div>
<div class="box">
<div class="box-content">
<div class="box-content-row box-content-row-flex" appBoxRow>
<div class="row-main">
<label for="masterPasswordRetype">{{'reTypeMasterPass' | i18n}}</label>
<input id="masterPasswordRetype" type="{{showPassword ? 'text' : 'password'}}"
name="MasterPasswordRetype" class="monospaced" [(ngModel)]="masterPasswordRetype" required
appInputVerbatim>
</div>
<div class="action-buttons">
<a class="row-btn" href="#" appStopClick appBlurClick
appA11yTitle="{{'toggleVisibility' | i18n}}" (click)="togglePassword(true)">
<i class="fa fa-lg" aria-hidden="true"
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
</a>
</div>
</div>
</div>
</div>
<div class="box">
<div class="box-content">
<div class="box-content-row" appBoxRow>
<label for="hint">{{'masterPassHint' | i18n}}</label>
<input id="hint" type="text" name="Hint" [(ngModel)]="hint">
</div>
</div>
<div class="box-footer">
{{'masterPassHintDesc' | i18n}}
</div>
</div>
</content>
</form>

View File

@ -0,0 +1,62 @@
import { Component } from '@angular/core';
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 { UserService } from 'jslib-common/abstractions/user.service';
import { UpdateTempPasswordComponent as BaseUpdateTempPasswordComponent } from 'jslib-angular/components/update-temp-password.component';
interface MasterPasswordScore {
Color: string;
Text: string;
Width: number;
}
@Component({
selector: 'app-update-temp-password',
templateUrl: 'update-temp-password.component.html',
})
export class UpdateTempPasswordComponent extends BaseUpdateTempPasswordComponent {
get masterPasswordScoreStyle(): MasterPasswordScore {
const scoreWidth = this.masterPasswordScore == null ? 0 : (this.masterPasswordScore + 1) * 20;
switch (this.masterPasswordScore) {
case 4:
return {
Color: 'bg-success',
Text: 'strong',
Width: scoreWidth,
};
case 3:
return {
Color: 'bg-primary',
Text: 'good',
Width: scoreWidth,
};
case 2:
return {
Color: 'bg-warning',
Text: 'weak',
Width: scoreWidth,
};
default:
return {
Color: 'bg-danger',
Text: 'weak',
Width: scoreWidth,
};
}
}
constructor(i18nService: I18nService, platformUtilsService: PlatformUtilsService,
passwordGenerationService: PasswordGenerationService, policyService: PolicyService,
cryptoService: CryptoService, userService: UserService,
messagingService: MessagingService, apiService: ApiService) {
super(i18nService, platformUtilsService, passwordGenerationService, policyService, cryptoService,
userService, messagingService, apiService);
}
}

View File

@ -22,6 +22,7 @@ import { SetPasswordComponent } from './accounts/set-password.component';
import { SsoComponent } from './accounts/sso.component';
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
import { TwoFactorComponent } from './accounts/two-factor.component';
import { UpdateTempPasswordComponent } from './accounts/update-temp-password.component';
import { PasswordGeneratorHistoryComponent } from './generator/password-generator-history.component';
import { PasswordGeneratorComponent } from './generator/password-generator.component';
@ -261,6 +262,12 @@ const routes: Routes = [
canActivate: [AuthGuardService],
data: { state: 'edit-send' },
},
{
path: 'update-temp-password',
component: UpdateTempPasswordComponent,
canActivate: [AuthGuardService],
data: { state: 'update-temp-password' },
},
{
path: 'tabs',
component: TabsComponent,

View File

@ -20,6 +20,7 @@ import { SetPasswordComponent } from './accounts/set-password.component';
import { SsoComponent } from './accounts/sso.component';
import { TwoFactorOptionsComponent } from './accounts/two-factor-options.component';
import { TwoFactorComponent } from './accounts/two-factor.component';
import { UpdateTempPasswordComponent } from './accounts/update-temp-password.component';
import { PasswordGeneratorHistoryComponent } from './generator/password-generator-history.component';
import { PasswordGeneratorComponent } from './generator/password-generator.component';
@ -226,8 +227,10 @@ registerLocaleData(localeZhTw, 'zh-TW');
SendGroupingsComponent,
SendListComponent,
SendTypeComponent,
SetPasswordComponent,
SettingsComponent,
ShareComponent,
SsoComponent,
StopClickDirective,
StopPropDirective,
SyncComponent,
@ -235,9 +238,8 @@ registerLocaleData(localeZhTw, 'zh-TW');
TrueFalseValueDirective,
TwoFactorOptionsComponent,
TwoFactorComponent,
SsoComponent,
UpdateTempPasswordComponent,
ViewComponent,
SetPasswordComponent,
],
entryComponents: [],
providers: [