diff --git a/src/app/accounts/login.component.ts b/src/app/accounts/login.component.ts index 5367a9f3..1e34fcd5 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 df3d6b76..d4b73619 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 f2cb0039..0b63a200 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 e9eb59a6..f81c3928 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 00000000..95b54f0e --- /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 00000000..fb0923b5 --- /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 02ad272b..7346ba69 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 4a0bd7f5..21753882 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 21e82378..f3133f5a 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 bec20b25..109a7f80 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; }