mirror of
https://github.com/bitwarden/browser
synced 2025-12-06 00:13:28 +00:00
[PM-24105] Remove usage of getUserKey on keyService (#16626)
• prefer undefined over null • obtain required UserId once per method, before branching • guards moved to beginning of methods * lift UserId retrieval to occur once during import * remove redundant userId retrieval
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { mock } from "jest-mock-extended";
|
||||
import { of } from "rxjs";
|
||||
|
||||
import { emptyGuid, UserId } from "@bitwarden/common/types/guid";
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
@@ -97,6 +99,7 @@ describe("Send", () => {
|
||||
const text = mock<SendText>();
|
||||
text.decrypt.mockResolvedValue("textView" as any);
|
||||
const userKey = new SymmetricCryptoKey(new Uint8Array(32)) as UserKey;
|
||||
const userId = emptyGuid as UserId;
|
||||
|
||||
const send = new Send();
|
||||
send.id = "id";
|
||||
@@ -120,11 +123,11 @@ describe("Send", () => {
|
||||
.calledWith(send.key, userKey)
|
||||
.mockResolvedValue(makeStaticByteArray(32));
|
||||
keyService.makeSendKey.mockResolvedValue("cryptoKey" as any);
|
||||
keyService.getUserKey.mockResolvedValue(userKey);
|
||||
keyService.userKey$.calledWith(userId).mockReturnValue(of(userKey));
|
||||
|
||||
(window as any).bitwardenContainerService = new ContainerService(keyService, encryptService);
|
||||
|
||||
const view = await send.decrypt();
|
||||
const view = await send.decrypt(userId);
|
||||
|
||||
expect(text.decrypt).toHaveBeenNthCalledWith(1, "cryptoKey");
|
||||
expect(send.name.decrypt).toHaveBeenNthCalledWith(
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
// FIXME: Update this file to be type safe and remove this and next line
|
||||
// @ts-strict-ignore
|
||||
import { firstValueFrom } from "rxjs";
|
||||
import { Jsonify } from "type-fest";
|
||||
|
||||
import { UserId } from "@bitwarden/common/types/guid";
|
||||
|
||||
import { EncString } from "../../../../key-management/crypto/models/enc-string";
|
||||
import { Utils } from "../../../../platform/misc/utils";
|
||||
import Domain from "../../../../platform/models/domain/domain-base";
|
||||
@@ -73,22 +76,18 @@ export class Send extends Domain {
|
||||
}
|
||||
}
|
||||
|
||||
async decrypt(): Promise<SendView> {
|
||||
const model = new SendView(this);
|
||||
async decrypt(userId: UserId): Promise<SendView> {
|
||||
if (!userId) {
|
||||
throw new Error("User ID must not be null or undefined");
|
||||
}
|
||||
|
||||
const model = new SendView(this);
|
||||
const keyService = Utils.getContainerService().getKeyService();
|
||||
const encryptService = Utils.getContainerService().getEncryptService();
|
||||
|
||||
try {
|
||||
const sendKeyEncryptionKey = await keyService.getUserKey();
|
||||
// model.key is a seed used to derive a key, not a SymmetricCryptoKey
|
||||
model.key = await encryptService.decryptBytes(this.key, sendKeyEncryptionKey);
|
||||
model.cryptoKey = await keyService.makeSendKey(model.key);
|
||||
// FIXME: Remove when updating file. Eslint update
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (e) {
|
||||
// TODO: error?
|
||||
}
|
||||
const sendKeyEncryptionKey = await firstValueFrom(keyService.userKey$(userId));
|
||||
// model.key is a seed used to derive a key, not a SymmetricCryptoKey
|
||||
model.key = await encryptService.decryptBytes(this.key, sendKeyEncryptionKey);
|
||||
model.cryptoKey = await keyService.makeSendKey(model.key);
|
||||
|
||||
await this.decryptObj<Send, SendView>(this, model, ["name", "notes"], null, model.cryptoKey);
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ describe("SendService", () => {
|
||||
decryptedState.nextState([testSendViewData("1", "Test Send")]);
|
||||
|
||||
sendService = new SendService(
|
||||
accountService,
|
||||
keyService,
|
||||
i18nService,
|
||||
keyGenerationService,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// @ts-strict-ignore
|
||||
import { Observable, concatMap, distinctUntilChanged, firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
// This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop.
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { PBKDF2KdfConfig, KeyService } from "@bitwarden/key-management";
|
||||
@@ -35,12 +36,16 @@ export class SendService implements InternalSendServiceAbstraction {
|
||||
map(([, record]) => Object.values(record || {}).map((data) => new Send(data))),
|
||||
);
|
||||
sendViews$ = this.stateProvider.encryptedState$.pipe(
|
||||
concatMap(([, record]) =>
|
||||
this.decryptSends(Object.values(record || {}).map((data) => new Send(data))),
|
||||
concatMap(([userId, record]) =>
|
||||
this.decryptSends(
|
||||
Object.values(record || {}).map((data) => new Send(data)),
|
||||
userId,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
constructor(
|
||||
private accountService: AccountService,
|
||||
private keyService: KeyService,
|
||||
private i18nService: I18nService,
|
||||
private keyGenerationService: KeyGenerationService,
|
||||
@@ -89,8 +94,9 @@ export class SendService implements InternalSendServiceAbstraction {
|
||||
);
|
||||
send.password = passwordKey.keyB64;
|
||||
}
|
||||
const userId = (await firstValueFrom(this.accountService.activeAccount$)).id;
|
||||
if (userKey == null) {
|
||||
userKey = await this.keyService.getUserKey();
|
||||
userKey = await firstValueFrom(this.keyService.userKey$(userId));
|
||||
}
|
||||
// Key is not a SymmetricCryptoKey, but key material used to derive the cryptoKey
|
||||
send.key = await this.encryptService.encryptBytes(model.key, userKey);
|
||||
@@ -111,11 +117,12 @@ export class SendService implements InternalSendServiceAbstraction {
|
||||
model.file.fileName,
|
||||
file,
|
||||
model.cryptoKey,
|
||||
userId,
|
||||
);
|
||||
send.file.fileName = name;
|
||||
fileData = data;
|
||||
} else {
|
||||
fileData = await this.parseFile(send, file, model.cryptoKey);
|
||||
fileData = await this.parseFile(send, file, model.cryptoKey, userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -208,6 +215,9 @@ export class SendService implements InternalSendServiceAbstraction {
|
||||
}
|
||||
|
||||
async getAllDecryptedFromState(userId: UserId): Promise<SendView[]> {
|
||||
if (!userId) {
|
||||
throw new Error("User ID must not be null or undefined");
|
||||
}
|
||||
let decSends = await this.stateProvider.getDecryptedSends();
|
||||
if (decSends != null) {
|
||||
return decSends;
|
||||
@@ -222,7 +232,7 @@ export class SendService implements InternalSendServiceAbstraction {
|
||||
const promises: Promise<any>[] = [];
|
||||
const sends = await this.getAll();
|
||||
sends.forEach((send) => {
|
||||
promises.push(send.decrypt().then((f) => decSends.push(f)));
|
||||
promises.push(send.decrypt(userId).then((f) => decSends.push(f)));
|
||||
});
|
||||
|
||||
await Promise.all(promises);
|
||||
@@ -311,7 +321,12 @@ export class SendService implements InternalSendServiceAbstraction {
|
||||
return requests;
|
||||
}
|
||||
|
||||
private parseFile(send: Send, file: File, key: SymmetricCryptoKey): Promise<EncArrayBuffer> {
|
||||
private parseFile(
|
||||
send: Send,
|
||||
file: File,
|
||||
key: SymmetricCryptoKey,
|
||||
userId: UserId,
|
||||
): Promise<EncArrayBuffer> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsArrayBuffer(file);
|
||||
@@ -321,6 +336,7 @@ export class SendService implements InternalSendServiceAbstraction {
|
||||
file.name,
|
||||
evt.target.result as ArrayBuffer,
|
||||
key,
|
||||
userId,
|
||||
);
|
||||
send.file.fileName = name;
|
||||
resolve(data);
|
||||
@@ -338,17 +354,18 @@ export class SendService implements InternalSendServiceAbstraction {
|
||||
fileName: string,
|
||||
data: ArrayBuffer,
|
||||
key: SymmetricCryptoKey,
|
||||
userId: UserId,
|
||||
): Promise<[EncString, EncArrayBuffer]> {
|
||||
if (key == null) {
|
||||
key = await this.keyService.getUserKey();
|
||||
key = await firstValueFrom(this.keyService.userKey$(userId));
|
||||
}
|
||||
const encFileName = await this.encryptService.encryptString(fileName, key);
|
||||
const encFileData = await this.encryptService.encryptFileData(new Uint8Array(data), key);
|
||||
return [encFileName, encFileData];
|
||||
}
|
||||
|
||||
private async decryptSends(sends: Send[]) {
|
||||
const decryptSendPromises = sends.map((s) => s.decrypt());
|
||||
private async decryptSends(sends: Send[], userId: UserId) {
|
||||
const decryptSendPromises = sends.map((s) => s.decrypt(userId));
|
||||
const decryptedSends = await Promise.all(decryptSendPromises);
|
||||
|
||||
decryptedSends.sort(Utils.getSortFunction(this.i18nService, "name"));
|
||||
|
||||
Reference in New Issue
Block a user