mirror of
https://github.com/bitwarden/browser
synced 2025-12-16 00:03:56 +00:00
[EC-272] Web workers using EncryptionService (#3532)
* Add item decryption to encryptService * Create multithreadEncryptService subclass to handle web workers * Create encryption web worker * Refactor cipherService to use new interface * Update dependencies
This commit is contained in:
@@ -3,6 +3,7 @@ import { firstValueFrom } from "rxjs";
|
||||
import { ApiService } from "../abstractions/api.service";
|
||||
import { CipherService as CipherServiceAbstraction } from "../abstractions/cipher.service";
|
||||
import { CryptoService } from "../abstractions/crypto.service";
|
||||
import { EncryptService } from "../abstractions/encrypt.service";
|
||||
import { FileUploadService } from "../abstractions/fileUpload.service";
|
||||
import { I18nService } from "../abstractions/i18n.service";
|
||||
import { LogService } from "../abstractions/log.service";
|
||||
@@ -65,7 +66,8 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
private i18nService: I18nService,
|
||||
private searchService: () => SearchService,
|
||||
private logService: LogService,
|
||||
private stateService: StateService
|
||||
private stateService: StateService,
|
||||
private encryptService: EncryptService
|
||||
) {}
|
||||
|
||||
async getDecryptedCipherCache(): Promise<CipherView[]> {
|
||||
@@ -329,35 +331,50 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
|
||||
@sequentialize(() => "getAllDecrypted")
|
||||
async getAllDecrypted(): Promise<CipherView[]> {
|
||||
const userId = await this.stateService.getUserId();
|
||||
if ((await this.getDecryptedCipherCache()) != null) {
|
||||
if (
|
||||
this.searchService != null &&
|
||||
(this.searchService().indexedEntityId ?? userId) !== userId
|
||||
) {
|
||||
await this.searchService().indexCiphers(userId, await this.getDecryptedCipherCache());
|
||||
}
|
||||
await this.reindexCiphers();
|
||||
return await this.getDecryptedCipherCache();
|
||||
}
|
||||
|
||||
const decCiphers: CipherView[] = [];
|
||||
const hasKey = await this.cryptoService.hasKey();
|
||||
if (!hasKey) {
|
||||
throw new Error("No key.");
|
||||
}
|
||||
|
||||
const promises: Promise<number>[] = [];
|
||||
const ciphers = await this.getAll();
|
||||
ciphers.forEach(async (cipher) => {
|
||||
promises.push(cipher.decrypt().then((c) => decCiphers.push(c)));
|
||||
});
|
||||
const orgKeys = await this.cryptoService.getOrgKeys();
|
||||
const userKey = await this.cryptoService.getKeyForUserEncryption();
|
||||
|
||||
// Group ciphers by orgId or under 'null' for the user's ciphers
|
||||
const grouped = ciphers.reduce((agg, c) => {
|
||||
agg[c.organizationId] ??= [];
|
||||
agg[c.organizationId].push(c);
|
||||
return agg;
|
||||
}, {} as Record<string, Cipher[]>);
|
||||
|
||||
const decCiphers = (
|
||||
await Promise.all(
|
||||
Object.entries(grouped).map(([orgId, groupedCiphers]) =>
|
||||
this.encryptService.decryptItems(groupedCiphers, orgKeys.get(orgId) ?? userKey)
|
||||
)
|
||||
)
|
||||
)
|
||||
.flat()
|
||||
.sort(this.getLocaleSortingFunction());
|
||||
|
||||
await Promise.all(promises);
|
||||
decCiphers.sort(this.getLocaleSortingFunction());
|
||||
await this.setDecryptedCipherCache(decCiphers);
|
||||
return decCiphers;
|
||||
}
|
||||
|
||||
private async reindexCiphers() {
|
||||
const userId = await this.stateService.getUserId();
|
||||
const reindexRequired =
|
||||
this.searchService != null && (this.searchService().indexedEntityId ?? userId) !== userId;
|
||||
if (reindexRequired) {
|
||||
await this.searchService().indexCiphers(userId, await this.getDecryptedCipherCache());
|
||||
}
|
||||
}
|
||||
|
||||
async getAllDecryptedForGrouping(groupingId: string, folder = true): Promise<CipherView[]> {
|
||||
const ciphers = await this.getAllDecrypted();
|
||||
|
||||
@@ -488,21 +505,17 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
|
||||
async getAllFromApiForOrganization(organizationId: string): Promise<CipherView[]> {
|
||||
const ciphers = await this.apiService.getCiphersOrganization(organizationId);
|
||||
if (ciphers != null && ciphers.data != null && ciphers.data.length) {
|
||||
const decCiphers: CipherView[] = [];
|
||||
const promises: any[] = [];
|
||||
ciphers.data.forEach((r) => {
|
||||
const data = new CipherData(r);
|
||||
const cipher = new Cipher(data);
|
||||
promises.push(cipher.decrypt().then((c) => decCiphers.push(c)));
|
||||
});
|
||||
await Promise.all(promises);
|
||||
decCiphers.sort(this.getLocaleSortingFunction());
|
||||
return decCiphers;
|
||||
} else {
|
||||
const response = await this.apiService.getCiphersOrganization(organizationId);
|
||||
if (response?.data == null || response.data.length < 1) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const ciphers = response.data.map((cr) => new Cipher(new CipherData(cr)));
|
||||
const key = await this.cryptoService.getOrgKey(organizationId);
|
||||
const decCiphers = await this.encryptService.decryptItems(ciphers, key);
|
||||
|
||||
decCiphers.sort(this.getLocaleSortingFunction());
|
||||
return decCiphers;
|
||||
}
|
||||
|
||||
async getLastUsedForUrl(url: string, autofillOnPageLoad = false): Promise<CipherView> {
|
||||
|
||||
Reference in New Issue
Block a user