mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 14:23:32 +00:00
[PM-6688] Use AccountService as account source (#8893)
* Use account service to track accounts and active account * Remove state service active account Observables. * Add email verified to account service * Do not store account info on logged out accounts * Add account activity tracking to account service * Use last account activity from account service * migrate or replicate account service data * Add `AccountActivityService` that handles storing account last active data * Move active and next active user to account service * Remove authenticated accounts from state object * Fold account activity into account service * Fix builds * Fix desktop app switch * Fix logging out non active user * Expand helper to handle new authenticated accounts location * Prefer view observable to tons of async pipes * Fix `npm run test:types` * Correct user activity sorting test * Be more precise about log out messaging * Fix dev compare errors All stored values are serializable, the next step wasn't necessary and was erroring on some types that lack `toString`. * If the account in unlocked on load of lock component, navigate away from lock screen * Handle no users case for auth service statuses * Specify account to switch to * Filter active account out of inactive accounts * Prefer constructor init * Improve comparator * Use helper methods internally * Fixup component tests * Clarify name * Ensure accounts object has only valid userIds * Capitalize const values * Prefer descriptive, single-responsibility guards * Update libs/common/src/state-migrations/migrate.ts Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com> * Fix merge * Add user Id validation activity for undefined was being set, which was resulting in requests for the auth status of `"undefined"` (string) userId, due to key enumeration. These changes stop that at both locations, as well as account add for good measure. --------- Co-authored-by: Justin Baur <19896123+justindbaur@users.noreply.github.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { Directive, NgZone, OnDestroy, OnInit } from "@angular/core";
|
||||
import { Router } from "@angular/router";
|
||||
import { firstValueFrom, Subject } from "rxjs";
|
||||
import { concatMap, take, takeUntil } from "rxjs/operators";
|
||||
import { concatMap, map, take, takeUntil } from "rxjs/operators";
|
||||
|
||||
import { PinCryptoServiceAbstraction } from "@bitwarden/auth/common";
|
||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||
@@ -11,10 +11,12 @@ import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abs
|
||||
import { InternalPolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { AuthService } from "@bitwarden/common/auth/abstractions/auth.service";
|
||||
import { DeviceTrustServiceAbstraction } from "@bitwarden/common/auth/abstractions/device-trust.service.abstraction";
|
||||
import { KdfConfigService } from "@bitwarden/common/auth/abstractions/kdf-config.service";
|
||||
import { InternalMasterPasswordServiceAbstraction } from "@bitwarden/common/auth/abstractions/master-password.service.abstraction";
|
||||
import { UserVerificationService } from "@bitwarden/common/auth/abstractions/user-verification/user-verification.service.abstraction";
|
||||
import { AuthenticationStatus } from "@bitwarden/common/auth/enums/authentication-status";
|
||||
import { ForceSetPasswordReason } from "@bitwarden/common/auth/models/domain/force-set-password-reason";
|
||||
import { SecretVerificationRequest } from "@bitwarden/common/auth/models/request/secret-verification.request";
|
||||
import { MasterPasswordPolicyResponse } from "@bitwarden/common/auth/models/response/master-password-policy.response";
|
||||
@@ -30,6 +32,7 @@ import { BiometricStateService } from "@bitwarden/common/platform/biometrics/bio
|
||||
import { HashPurpose, KeySuffixOptions } from "@bitwarden/common/platform/enums";
|
||||
import { PinLockType } from "@bitwarden/common/services/vault-timeout/vault-timeout-settings.service";
|
||||
import { PasswordStrengthServiceAbstraction } from "@bitwarden/common/tools/password-strength";
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
import { UserKey } from "@bitwarden/common/types/key";
|
||||
import { DialogService } from "@bitwarden/components";
|
||||
|
||||
@@ -46,6 +49,7 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
supportsBiometric: boolean;
|
||||
biometricLock: boolean;
|
||||
|
||||
private activeUserId: UserId;
|
||||
protected successRoute = "vault";
|
||||
protected forcePasswordResetRoute = "update-temp-password";
|
||||
protected onSuccessfulSubmit: () => Promise<void>;
|
||||
@@ -80,14 +84,16 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
protected pinCryptoService: PinCryptoServiceAbstraction,
|
||||
protected biometricStateService: BiometricStateService,
|
||||
protected accountService: AccountService,
|
||||
protected authService: AuthService,
|
||||
protected kdfConfigService: KdfConfigService,
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.stateService.activeAccount$
|
||||
this.accountService.activeAccount$
|
||||
.pipe(
|
||||
concatMap(async () => {
|
||||
await this.load();
|
||||
concatMap(async (account) => {
|
||||
this.activeUserId = account?.id;
|
||||
await this.load(account?.id);
|
||||
}),
|
||||
takeUntil(this.destroy$),
|
||||
)
|
||||
@@ -116,7 +122,7 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
|
||||
if (confirmed) {
|
||||
this.messagingService.send("logout");
|
||||
this.messagingService.send("logout", { userId: this.activeUserId });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,23 +327,35 @@ export class LockComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
private async load() {
|
||||
private async load(userId: UserId) {
|
||||
// TODO: Investigate PM-3515
|
||||
|
||||
// The loading of the lock component works as follows:
|
||||
// 1. First, is locking a valid timeout action? If not, we will log the user out.
|
||||
// 2. If locking IS a valid timeout action, we proceed to show the user the lock screen.
|
||||
// 1. If the user is unlocked, we're here in error so we navigate to the home page
|
||||
// 2. First, is locking a valid timeout action? If not, we will log the user out.
|
||||
// 3. If locking IS a valid timeout action, we proceed to show the user the lock screen.
|
||||
// The user will be able to unlock as follows:
|
||||
// - If they have a PIN set, they will be presented with the PIN input
|
||||
// - If they have a master password and no PIN, they will be presented with the master password input
|
||||
// - If they have biometrics enabled, they will be presented with the biometric prompt
|
||||
|
||||
const isUnlocked = await firstValueFrom(
|
||||
this.authService
|
||||
.authStatusFor$(userId)
|
||||
.pipe(map((status) => status === AuthenticationStatus.Unlocked)),
|
||||
);
|
||||
if (isUnlocked) {
|
||||
// navigate to home
|
||||
await this.router.navigate(["/"]);
|
||||
return;
|
||||
}
|
||||
|
||||
const availableVaultTimeoutActions = await firstValueFrom(
|
||||
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(),
|
||||
this.vaultTimeoutSettingsService.availableVaultTimeoutActions$(userId),
|
||||
);
|
||||
const supportsLock = availableVaultTimeoutActions.includes(VaultTimeoutAction.Lock);
|
||||
if (!supportsLock) {
|
||||
return await this.vaultTimeoutService.logOut();
|
||||
return await this.vaultTimeoutService.logOut(userId);
|
||||
}
|
||||
|
||||
this.pinStatus = await this.vaultTimeoutSettingsService.isPinLockSet();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Pipe, PipeTransform } from "@angular/core";
|
||||
|
||||
interface User {
|
||||
export interface User {
|
||||
name?: string;
|
||||
email?: string;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { Subject, firstValueFrom, takeUntil, map, BehaviorSubject, concatMap } f
|
||||
|
||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||
import { PolicyType } from "@bitwarden/common/admin-console/enums";
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
@@ -118,6 +119,7 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
||||
protected dialogService: DialogService,
|
||||
protected formBuilder: FormBuilder,
|
||||
protected billingAccountProfileStateService: BillingAccountProfileStateService,
|
||||
protected accountService: AccountService,
|
||||
) {
|
||||
this.typeOptions = [
|
||||
{ name: i18nService.t("sendTypeFile"), value: SendType.File, premium: true },
|
||||
@@ -215,7 +217,9 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.emailVerified = await this.stateService.getEmailVerified();
|
||||
this.emailVerified = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.emailVerified ?? false)),
|
||||
);
|
||||
|
||||
this.type = !this.canAccessPremium || !this.emailVerified ? SendType.Text : SendType.File;
|
||||
if (this.send == null) {
|
||||
|
||||
Reference in New Issue
Block a user