diff --git a/src/app/accounts/login.component.ts b/src/app/accounts/login.component.ts index 5367a9f3be..1e34fcd50b 100644 --- a/src/app/accounts/login.component.ts +++ b/src/app/accounts/login.component.ts @@ -21,6 +21,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'; @@ -46,11 +47,15 @@ export class LoginComponent extends BaseLoginComponent implements OnDestroy { environmentService: EnvironmentService, passwordGenerationService: PasswordGenerationService, cryptoFunctionService: CryptoFunctionService, storageService: StorageService, private broadcasterService: BroadcasterService, private ngZone: NgZone, - private messagingService: MessagingService) { + private messagingService: MessagingService, private userService: UserService) { super(authService, router, platformUtilsService, i18nService, stateService, environmentService, passwordGenerationService, cryptoFunctionService, storageService); super.onSuccessfulLogin = () => { - return syncService.fullSync(true); + return syncService.fullSync(true).then(async () => { + if (await this.userService.getForcePasswordReset()) { + this.router.navigate(['update-temp-password']); + } + }); }; } diff --git a/src/app/accounts/set-password.component.ts b/src/app/accounts/set-password.component.ts index df3d6b76e6..d4b7361967 100644 --- a/src/app/accounts/set-password.component.ts +++ b/src/app/accounts/set-password.component.ts @@ -40,6 +40,13 @@ export class SetPasswordComponent extends BaseSetPasswordComponent implements On private broadcasterService: BroadcasterService, private ngZone: NgZone) { 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() { diff --git a/src/app/accounts/sso.component.ts b/src/app/accounts/sso.component.ts index f2cb0039c5..0b63a2008b 100644 --- a/src/app/accounts/sso.component.ts +++ b/src/app/accounts/sso.component.ts @@ -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'; @@ -28,7 +29,7 @@ export class SsoComponent extends BaseSsoComponent { storageService: StorageService, stateService: StateService, platformUtilsService: PlatformUtilsService, apiService: ApiService, cryptoFunctionService: CryptoFunctionService, environmentService: EnvironmentService, - passwordGenerationService: PasswordGenerationService) { + passwordGenerationService: PasswordGenerationService, private userService: UserService) { super(authService, router, i18nService, route, storageService, stateService, platformUtilsService, apiService, cryptoFunctionService, environmentService, passwordGenerationService); super.onSuccessfulLogin = () => { @@ -36,5 +37,12 @@ export class SsoComponent extends BaseSsoComponent { }; this.redirectUri = 'bitwarden://sso-callback'; this.clientId = 'desktop'; + super.onSuccessfulLoginNavigate = async () => { + if (await this.userService.getForcePasswordReset()) { + this.router.navigate(['update-temp-password']); + } else { + this.router.navigate([this.successRoute]); + } + }; } } diff --git a/src/app/accounts/two-factor.component.ts b/src/app/accounts/two-factor.component.ts index e9eb59a622..f81c39289b 100644 --- a/src/app/accounts/two-factor.component.ts +++ b/src/app/accounts/two-factor.component.ts @@ -22,6 +22,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 { ModalComponent } from 'jslib-angular/components/modal.component'; import { TwoFactorComponent as BaseTwoFactorComponent } from 'jslib-angular/components/two-factor.component'; @@ -39,11 +40,16 @@ export class TwoFactorComponent extends BaseTwoFactorComponent { i18nService: I18nService, apiService: ApiService, platformUtilsService: PlatformUtilsService, syncService: SyncService, environmentService: EnvironmentService, private componentFactoryResolver: ComponentFactoryResolver, - stateService: StateService, storageService: StorageService, route: ActivatedRoute) { + stateService: StateService, storageService: StorageService, route: ActivatedRoute, + private userService: UserService) { super(authService, router, i18nService, apiService, platformUtilsService, window, environmentService, stateService, storageService, route); super.onSuccessfulLogin = () => { - return syncService.fullSync(true); + return syncService.fullSync(true).then(async () => { + if (await this.userService.getForcePasswordReset()) { + this.router.navigate(['update-temp-password']); + } + }); }; } diff --git a/src/app/accounts/update-temp-password.component.html b/src/app/accounts/update-temp-password.component.html new file mode 100644 index 0000000000..95b54f0e67 --- /dev/null +++ b/src/app/accounts/update-temp-password.component.html @@ -0,0 +1,75 @@ +
+
+ + {{'updateMasterPasswordWarning' | i18n}} + +
+
+
+
+
+ + +
+
+ + + +
+
+
+
+
+
+
+
+
+
+
+
+ + +
+
+ + + +
+
+
+
+
+
+
+ + +
+
+ +
+
+ + {{'logOut' | i18n}} +
+
+
diff --git a/src/app/accounts/update-temp-password.component.ts b/src/app/accounts/update-temp-password.component.ts new file mode 100644 index 0000000000..fb0923b598 --- /dev/null +++ b/src/app/accounts/update-temp-password.component.ts @@ -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); + } +} diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 02ad272b50..7346ba6943 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -15,6 +15,7 @@ import { RegisterComponent } from './accounts/register.component'; import { SetPasswordComponent } from './accounts/set-password.component'; import { SsoComponent } from './accounts/sso.component'; import { TwoFactorComponent } from './accounts/two-factor.component'; +import { UpdateTempPasswordComponent } from './accounts/update-temp-password.component'; import { SendComponent } from './send/send.component'; @@ -48,6 +49,11 @@ const routes: Routes = [ component: SendComponent, canActivate: [AuthGuardService], }, + { + path: 'update-temp-password', + component: UpdateTempPasswordComponent, + canActivate: [AuthGuardService], + }, ]; @NgModule({ diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 4a0bd7f5ab..21753882b5 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -26,6 +26,7 @@ import { SettingsComponent } from './accounts/settings.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 { CalloutComponent } from 'jslib-angular/components/callout.component'; import { IconComponent } from 'jslib-angular/components/icon.component'; @@ -215,6 +216,7 @@ registerLocaleData(localeZhTw, 'zh-TW'); TrueFalseValueDirective, TwoFactorComponent, TwoFactorOptionsComponent, + UpdateTempPasswordComponent, VaultComponent, ViewComponent, ], diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 21e82378cd..f3133f5a87 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -1693,5 +1693,14 @@ }, "passwordConfirmationDesc": { "message": "This action is protected. To continue, please re-enter your master password to verify your identity." + }, + "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." } } diff --git a/src/scss/pages.scss b/src/scss/pages.scss index bec20b258e..109a7f80a9 100644 --- a/src/scss/pages.scss +++ b/src/scss/pages.scss @@ -23,7 +23,7 @@ } } -#register-page, #hint-page, #two-factor-page { +#register-page, #hint-page, #two-factor-page, #update-temp-password-page { padding-top: 20px; .content { @@ -39,7 +39,7 @@ } } -#login-page, #register-page, #hint-page, #two-factor-page, #lock-page { +#login-page, #register-page, #hint-page, #two-factor-page, #lock-page, #update-temp-password-page { .content { width: 300px; transition: width 0.25s linear; @@ -184,7 +184,7 @@ } } -#register-page { +#register-page, #update-temp-password-page { .content { width: 400px; }