diff --git a/src/app/accounts/login.component.ts b/src/app/accounts/login.component.ts index 5367a9f3bef..1e34fcd50bf 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 df3d6b76e6d..d4b73619677 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 f2cb0039c5b..0b63a2008b8 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 e9eb59a6222..f81c39289bb 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 00000000000..95b54f0e67c --- /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 00000000000..fb0923b598b --- /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 02ad272b50d..7346ba69430 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 4a0bd7f5ab0..21753882b50 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 21e82378cd2..f3133f5a873 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 bec20b258e6..109a7f80a9a 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; }