mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 08:13:42 +00:00
Ps/pm 5533/migrate decrypted user key (#7970)
* Move user key memory state to state providers
Note: state service observable change is because these updates are no longer internal to the class, but reporter directly to account service through crypto service on update of a user key
* remove decrypted user key state
Note, we're going to move the encrypted cryptoSymmetric key (and associated master key encrypted user keys) as part of the master key service creation. Crypto service will no longer be responsible for the encrypted forms of user key.
* Deprecate notices belong on abstraction
* Allow for single-direction status updates
This is necessary since we don't want to have to guarantee that the update to logged out occurs after the update to locked.
* Remove deprecated subject
It turns out the set for cryptoMasterKey was also unused 🎉
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { BehaviorSubject, concatMap } from "rxjs";
|
||||
import { BehaviorSubject, Observable, map } from "rxjs";
|
||||
import { Jsonify, JsonValue } from "type-fest";
|
||||
|
||||
import { OrganizationData } from "../../admin-console/models/data/organization.data";
|
||||
@@ -20,7 +20,7 @@ import { UsernameGeneratorOptions } from "../../tools/generator/username";
|
||||
import { SendData } from "../../tools/send/models/data/send.data";
|
||||
import { SendView } from "../../tools/send/models/view/send.view";
|
||||
import { UserId } from "../../types/guid";
|
||||
import { DeviceKey, MasterKey, UserKey } from "../../types/key";
|
||||
import { DeviceKey, MasterKey } from "../../types/key";
|
||||
import { UriMatchType } from "../../vault/enums";
|
||||
import { CipherData } from "../../vault/models/data/cipher.data";
|
||||
import { LocalData } from "../../vault/models/data/local.data";
|
||||
@@ -87,8 +87,7 @@ export class StateService<
|
||||
protected activeAccountSubject = new BehaviorSubject<string | null>(null);
|
||||
activeAccount$ = this.activeAccountSubject.asObservable();
|
||||
|
||||
protected activeAccountUnlockedSubject = new BehaviorSubject<boolean>(false);
|
||||
activeAccountUnlocked$ = this.activeAccountUnlockedSubject.asObservable();
|
||||
activeAccountUnlocked$: Observable<boolean>;
|
||||
|
||||
private hasBeenInited = false;
|
||||
protected isRecoveredSession = false;
|
||||
@@ -109,22 +108,11 @@ export class StateService<
|
||||
private migrationRunner: MigrationRunner,
|
||||
protected useAccountCache: boolean = true,
|
||||
) {
|
||||
// If the account gets changed, verify the new account is unlocked
|
||||
this.activeAccountSubject
|
||||
.pipe(
|
||||
concatMap(async (userId) => {
|
||||
if (userId == null && this.activeAccountUnlockedSubject.getValue() == false) {
|
||||
return;
|
||||
} else if (userId == null) {
|
||||
this.activeAccountUnlockedSubject.next(false);
|
||||
}
|
||||
// FIXME: This should be refactored into AuthService or a similar service,
|
||||
// as checking for the existence of the crypto key is a low level
|
||||
// implementation detail.
|
||||
this.activeAccountUnlockedSubject.next((await this.getUserKey()) != null);
|
||||
}),
|
||||
)
|
||||
.subscribe();
|
||||
this.activeAccountUnlocked$ = this.accountService.activeAccount$.pipe(
|
||||
map((a) => {
|
||||
return a?.status === AuthenticationStatus.Unlocked;
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async init(initOptions: InitOptions = {}): Promise<void> {
|
||||
@@ -522,68 +510,6 @@ export class StateService<
|
||||
return account?.keys?.cryptoMasterKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Do not save the Master Key. Use the User Symmetric Key instead
|
||||
*/
|
||||
async setCryptoMasterKey(value: SymmetricCryptoKey, options?: StorageOptions): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
account.keys.cryptoMasterKey = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
|
||||
const nextStatus = value != null ? AuthenticationStatus.Unlocked : AuthenticationStatus.Locked;
|
||||
await this.accountService.setAccountStatus(options.userId as UserId, nextStatus);
|
||||
|
||||
if (options.userId == this.activeAccountSubject.getValue()) {
|
||||
const nextValue = value != null;
|
||||
|
||||
// Avoid emitting if we are already unlocked
|
||||
if (this.activeAccountUnlockedSubject.getValue() != nextValue) {
|
||||
this.activeAccountUnlockedSubject.next(nextValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* user key used to encrypt/decrypt data
|
||||
*/
|
||||
async getUserKey(options?: StorageOptions): Promise<UserKey> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
return account?.keys?.userKey as UserKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* user key used to encrypt/decrypt data
|
||||
*/
|
||||
async setUserKey(value: UserKey, options?: StorageOptions): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
account.keys.userKey = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
|
||||
const nextStatus = value != null ? AuthenticationStatus.Unlocked : AuthenticationStatus.Locked;
|
||||
await this.accountService.setAccountStatus(options.userId as UserId, nextStatus);
|
||||
|
||||
if (options?.userId == this.activeAccountSubject.getValue()) {
|
||||
const nextValue = value != null;
|
||||
|
||||
// Avoid emitting if we are already unlocked
|
||||
if (this.activeAccountUnlockedSubject.getValue() != nextValue) {
|
||||
this.activeAccountUnlockedSubject.next(nextValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* User's master key derived from MP, saved only if we decrypted with MP
|
||||
*/
|
||||
@@ -885,33 +811,6 @@ export class StateService<
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use UserKey instead
|
||||
*/
|
||||
async getDecryptedCryptoSymmetricKey(options?: StorageOptions): Promise<SymmetricCryptoKey> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
return account?.keys?.cryptoSymmetricKey?.decrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use UserKey instead
|
||||
*/
|
||||
async setDecryptedCryptoSymmetricKey(
|
||||
value: SymmetricCryptoKey,
|
||||
options?: StorageOptions,
|
||||
): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
account.keys.cryptoSymmetricKey.decrypted = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultInMemoryOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
@withPrototypeForArrayMembers(GeneratedPasswordHistory)
|
||||
async getDecryptedPasswordGenerationHistory(
|
||||
options?: StorageOptions,
|
||||
@@ -1565,20 +1464,6 @@ export class StateService<
|
||||
)?.keys.cryptoSymmetricKey.encrypted;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use UserKey instead
|
||||
*/
|
||||
async setEncryptedCryptoSymmetricKey(value: string, options?: StorageOptions): Promise<void> {
|
||||
const account = await this.getAccount(
|
||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
||||
);
|
||||
account.keys.cryptoSymmetricKey.encrypted = value;
|
||||
await this.saveAccount(
|
||||
account,
|
||||
this.reconcileOptions(options, await this.defaultOnDiskOptions()),
|
||||
);
|
||||
}
|
||||
|
||||
@withPrototypeForArrayMembers(GeneratedPasswordHistory)
|
||||
async getEncryptedPasswordGenerationHistory(
|
||||
options?: StorageOptions,
|
||||
|
||||
Reference in New Issue
Block a user