mirror of
https://github.com/bitwarden/browser
synced 2026-02-09 13:10:17 +00:00
updated to use sdk function without prociding the key
This commit is contained in:
@@ -738,7 +738,7 @@ export default class NotificationBackground {
|
||||
private async getDecryptedCipherById(cipherId: string, userId: UserId) {
|
||||
const cipher = await this.cipherService.get(cipherId, userId);
|
||||
if (cipher != null && cipher.type === CipherType.Login) {
|
||||
return await this.cipherService.decryptCipherWithSdkOrLegacy(cipher, userId);
|
||||
return await this.cipherService.decrypt(cipher, userId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ export class Fido2Component implements OnInit, OnDestroy {
|
||||
this.ciphers = await Promise.all(
|
||||
message.cipherIds.map(async (cipherId) => {
|
||||
const cipher = await this.cipherService.get(cipherId, activeUserId);
|
||||
return this.cipherService.decryptCipherWithSdkOrLegacy(cipher, activeUserId);
|
||||
return this.cipherService.decrypt(cipher, activeUserId);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -235,7 +235,7 @@ export class Fido2Component implements OnInit, OnDestroy {
|
||||
this.ciphers = await Promise.all(
|
||||
message.existingCipherIds.map(async (cipherId) => {
|
||||
const cipher = await this.cipherService.get(cipherId, activeUserId);
|
||||
return this.cipherService.decryptCipherWithSdkOrLegacy(cipher, activeUserId);
|
||||
return this.cipherService.decrypt(cipher, activeUserId);
|
||||
}),
|
||||
);
|
||||
|
||||
|
||||
@@ -18,7 +18,10 @@ export class BackgroundCommunicationBackend implements CommunicationBackend {
|
||||
return;
|
||||
}
|
||||
|
||||
void this.queue.enqueue({ ...message.message, source: { Web: { id: sender.tab.id } } });
|
||||
void this.queue.enqueue({
|
||||
...message.message,
|
||||
source: { Web: { id: sender.tab.id } },
|
||||
} as any);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ export class AssignCollections {
|
||||
route.queryParams.pipe(
|
||||
switchMap(async ({ cipherId }) => {
|
||||
const cipherDomain = await this.cipherService.get(cipherId, userId);
|
||||
return await this.cipherService.decryptCipherWithSdkOrLegacy(cipherDomain, userId);
|
||||
return await this.cipherService.decrypt(cipherDomain, userId);
|
||||
}),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -81,7 +81,7 @@ describe("OpenAttachmentsComponent", () => {
|
||||
useValue: {
|
||||
get: getCipher,
|
||||
getKeyForCipherKeyDecryption: () => Promise.resolve(null),
|
||||
decryptCipherWithSdkOrLegacy: jest.fn().mockResolvedValue(cipherView),
|
||||
decrypt: jest.fn().mockResolvedValue(cipherView),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -81,10 +81,7 @@ export class OpenAttachmentsComponent implements OnInit {
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const cipherDomain = await this.cipherService.get(this.cipherId, activeUserId);
|
||||
const cipher = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
cipherDomain,
|
||||
activeUserId,
|
||||
);
|
||||
const cipher = await this.cipherService.decrypt(cipherDomain, activeUserId);
|
||||
|
||||
if (!cipher.organizationId) {
|
||||
this.cipherIsAPartOfFreeOrg = false;
|
||||
|
||||
@@ -69,6 +69,6 @@ export class PasswordHistoryV2Component implements OnInit {
|
||||
const activeUserId = activeAccount.id as UserId;
|
||||
|
||||
const cipher = await this.cipherService.get(cipherId, activeUserId);
|
||||
this.cipher = await this.cipherService.decryptCipherWithSdkOrLegacy(cipher, activeUserId);
|
||||
this.cipher = await this.cipherService.decrypt(cipher, activeUserId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ describe("ViewV2Component", () => {
|
||||
getKeyForCipherKeyDecryption: jest.fn().mockResolvedValue({}),
|
||||
deleteWithServer: jest.fn().mockResolvedValue(undefined),
|
||||
softDeleteWithServer: jest.fn().mockResolvedValue(undefined),
|
||||
decryptCipherWithSdkOrLegacy: jest.fn().mockResolvedValue(mockCipher),
|
||||
decrypt: jest.fn().mockResolvedValue(mockCipher),
|
||||
};
|
||||
|
||||
beforeEach(async () => {
|
||||
|
||||
@@ -200,7 +200,7 @@ export class ViewV2Component {
|
||||
|
||||
async getCipherData(id: string, userId: UserId) {
|
||||
const cipher = await this.cipherService.get(id, userId);
|
||||
return await this.cipherService.decryptCipherWithSdkOrLegacy(cipher, userId);
|
||||
return await this.cipherService.decrypt(cipher, userId);
|
||||
}
|
||||
|
||||
async editCipher() {
|
||||
|
||||
@@ -59,14 +59,11 @@ export class ShareCommand {
|
||||
return Response.badRequest("This item already belongs to an organization.");
|
||||
}
|
||||
|
||||
const cipherView = await this.cipherService.decryptCipherWithSdkOrLegacy(cipher, activeUserId);
|
||||
const cipherView = await this.cipherService.decrypt(cipher, activeUserId);
|
||||
try {
|
||||
await this.cipherService.shareWithServer(cipherView, organizationId, req, activeUserId);
|
||||
const updatedCipher = await this.cipherService.get(cipher.id, activeUserId);
|
||||
const decCipher = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
updatedCipher,
|
||||
activeUserId,
|
||||
);
|
||||
const decCipher = await this.cipherService.decrypt(updatedCipher, activeUserId);
|
||||
const res = new CipherResponse(decCipher);
|
||||
return Response.success(res);
|
||||
} catch (e) {
|
||||
|
||||
@@ -90,7 +90,7 @@ export class EditCommand {
|
||||
return Response.notFound();
|
||||
}
|
||||
|
||||
let cipherView = await this.cipherService.decryptCipherWithSdkOrLegacy(cipher, activeUserId);
|
||||
let cipherView = await this.cipherService.decrypt(cipher, activeUserId);
|
||||
if (cipherView.isDeleted) {
|
||||
return Response.badRequest("You may not edit a deleted item. Use the restore command first.");
|
||||
}
|
||||
@@ -98,10 +98,7 @@ export class EditCommand {
|
||||
const encCipher = await this.cipherService.encrypt(cipherView, activeUserId);
|
||||
try {
|
||||
const updatedCipher = await this.cipherService.updateWithServer(encCipher);
|
||||
const decCipher = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
updatedCipher,
|
||||
activeUserId,
|
||||
);
|
||||
const decCipher = await this.cipherService.decrypt(updatedCipher, activeUserId);
|
||||
const res = new CipherResponse(decCipher);
|
||||
return Response.success(res);
|
||||
} catch (e) {
|
||||
@@ -131,10 +128,7 @@ export class EditCommand {
|
||||
cipher,
|
||||
activeUserId,
|
||||
);
|
||||
const decCipher = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
updatedCipher,
|
||||
activeUserId,
|
||||
);
|
||||
const decCipher = await this.cipherService.decrypt(updatedCipher, activeUserId);
|
||||
const res = new CipherResponse(decCipher);
|
||||
return Response.success(res);
|
||||
} catch (e) {
|
||||
|
||||
@@ -116,7 +116,7 @@ export class GetCommand extends DownloadCommand {
|
||||
if (Utils.isGuid(id)) {
|
||||
const cipher = await this.cipherService.get(id, activeUserId);
|
||||
if (cipher != null) {
|
||||
decCipher = await this.cipherService.decryptCipherWithSdkOrLegacy(cipher, activeUserId);
|
||||
decCipher = await this.cipherService.decrypt(cipher, activeUserId);
|
||||
}
|
||||
} else if (id.trim() !== "") {
|
||||
let ciphers = await this.cipherService.getAllDecrypted(activeUserId);
|
||||
|
||||
@@ -93,10 +93,7 @@ export class CreateCommand {
|
||||
const cipher = await this.cipherService.encrypt(CipherExport.toView(req), activeUserId);
|
||||
try {
|
||||
const newCipher = await this.cipherService.createWithServer(cipher);
|
||||
const decCipher = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
newCipher,
|
||||
activeUserId,
|
||||
);
|
||||
const decCipher = await this.cipherService.decrypt(newCipher, activeUserId);
|
||||
const res = new CipherResponse(decCipher);
|
||||
return Response.success(res);
|
||||
} catch (e) {
|
||||
@@ -163,10 +160,7 @@ export class CreateCommand {
|
||||
new Uint8Array(fileBuf).buffer,
|
||||
activeUserId,
|
||||
);
|
||||
const decCipher = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
updatedCipher,
|
||||
activeUserId,
|
||||
);
|
||||
const decCipher = await this.cipherService.decrypt(updatedCipher, activeUserId);
|
||||
return Response.success(new CipherResponse(decCipher));
|
||||
} catch (e) {
|
||||
return Response.error(e);
|
||||
|
||||
@@ -199,10 +199,7 @@ export class DesktopAutofillService implements OnDestroy {
|
||||
return;
|
||||
}
|
||||
|
||||
const decrypted = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
cipher,
|
||||
activeUserId,
|
||||
);
|
||||
const decrypted = await this.cipherService.decrypt(cipher, activeUserId);
|
||||
|
||||
const fido2Credential = decrypted.login.fido2Credentials?.[0];
|
||||
if (!fido2Credential) {
|
||||
|
||||
@@ -207,10 +207,7 @@ export class EncryptedMessageHandlerService {
|
||||
return { status: "failure" };
|
||||
}
|
||||
|
||||
const cipherView = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
cipher,
|
||||
activeUserId,
|
||||
);
|
||||
const cipherView = await this.cipherService.decrypt(cipher, activeUserId);
|
||||
cipherView.name = credentialUpdatePayload.name;
|
||||
cipherView.login.password = credentialUpdatePayload.password;
|
||||
cipherView.login.username = credentialUpdatePayload.userName;
|
||||
|
||||
@@ -19,7 +19,7 @@ export class WebCommunicationProvider implements CommunicationBackend {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.queue.enqueue({ ...message.message, source: "BrowserBackground" });
|
||||
await this.queue.enqueue({ ...message.message, source: "BrowserBackground" } as any);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -466,10 +466,7 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
|
||||
activeUserId,
|
||||
);
|
||||
|
||||
const updatedCipherView = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
updatedCipher,
|
||||
activeUserId,
|
||||
);
|
||||
const updatedCipherView = await this.cipherService.decrypt(updatedCipher, activeUserId);
|
||||
|
||||
this.cipherFormComponent.patchCipher((currentCipher) => {
|
||||
currentCipher.attachments = updatedCipherView.attachments;
|
||||
@@ -505,10 +502,7 @@ export class VaultItemDialogComponent implements OnInit, OnDestroy {
|
||||
return;
|
||||
}
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
return await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
config.originalCipher,
|
||||
activeUserId,
|
||||
);
|
||||
return await this.cipherService.decrypt(config.originalCipher, activeUserId);
|
||||
}
|
||||
|
||||
private updateTitle() {
|
||||
|
||||
@@ -50,10 +50,7 @@ export class CollectionsComponent implements OnInit {
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
this.cipherDomain = await this.loadCipher(activeUserId);
|
||||
this.collectionIds = this.loadCipherCollections();
|
||||
this.cipher = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
this.cipherDomain,
|
||||
activeUserId,
|
||||
);
|
||||
this.cipher = await this.cipherService.decrypt(this.cipherDomain, activeUserId);
|
||||
this.collections = await this.loadCollections();
|
||||
|
||||
this.collections.forEach((c) => ((c as any).checked = false));
|
||||
|
||||
@@ -76,7 +76,7 @@ export class ShareComponent implements OnInit, OnDestroy {
|
||||
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
const cipherDomain = await this.cipherService.get(this.cipherId, activeUserId);
|
||||
this.cipher = await this.cipherService.decryptCipherWithSdkOrLegacy(cipherDomain, activeUserId);
|
||||
this.cipher = await this.cipherService.decrypt(cipherDomain, activeUserId);
|
||||
}
|
||||
|
||||
filterCollections() {
|
||||
@@ -103,10 +103,7 @@ export class ShareComponent implements OnInit, OnDestroy {
|
||||
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
const cipherDomain = await this.cipherService.get(this.cipherId, activeUserId);
|
||||
const cipherView = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
cipherDomain,
|
||||
activeUserId,
|
||||
);
|
||||
const cipherView = await this.cipherService.decrypt(cipherDomain, activeUserId);
|
||||
const orgs = await firstValueFrom(this.organizations$);
|
||||
const orgName =
|
||||
orgs.find((o) => o.id === this.organizationId)?.name ?? this.i18nService.t("organization");
|
||||
|
||||
@@ -269,7 +269,7 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
||||
if (this.cipher == null) {
|
||||
if (this.editMode) {
|
||||
const cipher = await this.loadCipher(activeUserId);
|
||||
this.cipher = await this.cipherService.decryptCipherWithSdkOrLegacy(cipher, activeUserId);
|
||||
this.cipher = await this.cipherService.decrypt(cipher, activeUserId);
|
||||
|
||||
// Adjust Cipher Name if Cloning
|
||||
if (this.cloneMode) {
|
||||
|
||||
@@ -88,10 +88,7 @@ export class AttachmentsComponent implements OnInit {
|
||||
const activeUserId = await firstValueFrom(getUserId(this.accountService.activeAccount$));
|
||||
this.formPromise = this.saveCipherAttachment(files[0], activeUserId);
|
||||
this.cipherDomain = await this.formPromise;
|
||||
this.cipher = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
this.cipherDomain,
|
||||
activeUserId,
|
||||
);
|
||||
this.cipher = await this.cipherService.decrypt(this.cipherDomain, activeUserId);
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
title: null,
|
||||
@@ -131,7 +128,7 @@ export class AttachmentsComponent implements OnInit {
|
||||
const updatedCipher = await this.deletePromises[attachment.id];
|
||||
|
||||
const cipher = new Cipher(updatedCipher);
|
||||
this.cipher = await this.cipherService.decryptCipherWithSdkOrLegacy(cipher, activeUserId);
|
||||
this.cipher = await this.cipherService.decrypt(cipher, activeUserId);
|
||||
|
||||
this.toastService.showToast({
|
||||
variant: "success",
|
||||
@@ -227,10 +224,7 @@ export class AttachmentsComponent implements OnInit {
|
||||
protected async init() {
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
this.cipherDomain = await this.loadCipher(activeUserId);
|
||||
this.cipher = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
this.cipherDomain,
|
||||
activeUserId,
|
||||
);
|
||||
this.cipher = await this.cipherService.decrypt(this.cipherDomain, activeUserId);
|
||||
|
||||
const canAccessPremium = await firstValueFrom(
|
||||
this.billingAccountProfileStateService.hasPremiumFromAnySource$(activeUserId),
|
||||
@@ -292,10 +286,7 @@ export class AttachmentsComponent implements OnInit {
|
||||
activeUserId,
|
||||
admin,
|
||||
);
|
||||
this.cipher = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
this.cipherDomain,
|
||||
activeUserId,
|
||||
);
|
||||
this.cipher = await this.cipherService.decrypt(this.cipherDomain, activeUserId);
|
||||
|
||||
// 3. Delete old
|
||||
this.deletePromises[attachment.id] = this.deleteCipherAttachment(
|
||||
|
||||
@@ -42,7 +42,7 @@ export class PasswordHistoryComponent implements OnInit {
|
||||
protected async init() {
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
const cipher = await this.cipherService.get(this.cipherId, activeUserId);
|
||||
const decCipher = await this.cipherService.decryptCipherWithSdkOrLegacy(cipher, activeUserId);
|
||||
const decCipher = await this.cipherService.decrypt(cipher, activeUserId);
|
||||
this.history = decCipher.passwordHistory == null ? [] : decCipher.passwordHistory;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -152,7 +152,7 @@ describe("FidoAuthenticatorService", () => {
|
||||
id === excludedCipher.id ? ({ decrypt: () => excludedCipher } as any) : undefined,
|
||||
);
|
||||
cipherService.getAllDecrypted.mockResolvedValue([excludedCipher]);
|
||||
cipherService.decryptCipherWithSdkOrLegacy.mockResolvedValue(excludedCipher);
|
||||
cipherService.decrypt.mockResolvedValue(excludedCipher);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -221,7 +221,7 @@ describe("FidoAuthenticatorService", () => {
|
||||
id === existingCipher.id ? ({ decrypt: () => existingCipher } as any) : undefined,
|
||||
);
|
||||
cipherService.getAllDecrypted.mockResolvedValue([existingCipher]);
|
||||
cipherService.decryptCipherWithSdkOrLegacy.mockResolvedValue(existingCipher);
|
||||
cipherService.decrypt.mockResolvedValue(existingCipher);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -308,7 +308,7 @@ describe("FidoAuthenticatorService", () => {
|
||||
const encryptedCipher = { ...existingCipher, reprompt: CipherRepromptType.Password };
|
||||
cipherService.get.mockResolvedValue(encryptedCipher as unknown as Cipher);
|
||||
|
||||
cipherService.decryptCipherWithSdkOrLegacy.mockResolvedValue({
|
||||
cipherService.decrypt.mockResolvedValue({
|
||||
...existingCipher,
|
||||
reprompt: CipherRepromptType.Password,
|
||||
} as unknown as CipherView);
|
||||
@@ -354,7 +354,7 @@ describe("FidoAuthenticatorService", () => {
|
||||
cipherId === cipher.id ? ({ decrypt: () => cipher } as any) : undefined,
|
||||
);
|
||||
cipherService.getAllDecrypted.mockResolvedValue([await cipher]);
|
||||
cipherService.decryptCipherWithSdkOrLegacy.mockResolvedValue(cipher);
|
||||
cipherService.decrypt.mockResolvedValue(cipher);
|
||||
cipherService.encrypt.mockImplementation(async (cipher) => {
|
||||
cipher.login.fido2Credentials[0].credentialId = credentialId; // Replace id for testability
|
||||
return {} as any;
|
||||
|
||||
@@ -151,7 +151,7 @@ export class Fido2AuthenticatorService<ParentWindowReference>
|
||||
);
|
||||
const encrypted = await this.cipherService.get(cipherId, activeUserId);
|
||||
|
||||
cipher = await this.cipherService.decryptCipherWithSdkOrLegacy(encrypted, activeUserId);
|
||||
cipher = await this.cipherService.decrypt(encrypted, activeUserId);
|
||||
|
||||
if (
|
||||
!userVerified &&
|
||||
|
||||
@@ -221,5 +221,5 @@ export abstract class CipherService implements UserKeyRotationDataProvider<Ciphe
|
||||
* @param userId The user ID to use for decryption.
|
||||
* @returns A promise that resolves to the decrypted cipher view.
|
||||
*/
|
||||
abstract decryptCipherWithSdkOrLegacy(cipher: Cipher, userId: UserId): Promise<CipherView>;
|
||||
abstract decrypt(cipher: Cipher, userId: UserId): Promise<CipherView>;
|
||||
}
|
||||
|
||||
@@ -477,12 +477,12 @@ describe("Cipher Service", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("decryptCipherWithSdkOrLegacy", () => {
|
||||
describe("decrypt", () => {
|
||||
it("should call decrypt method of CipherEncryptionService when feature flag is true", async () => {
|
||||
configService.getFeatureFlag.mockResolvedValue(true);
|
||||
cipherEncryptionService.decrypt.mockResolvedValue(new CipherView(cipherObj));
|
||||
|
||||
const result = await cipherService.decryptCipherWithSdkOrLegacy(cipherObj, userId);
|
||||
const result = await cipherService.decrypt(cipherObj, userId);
|
||||
|
||||
expect(result).toEqual(new CipherView(cipherObj));
|
||||
expect(cipherEncryptionService.decrypt).toHaveBeenCalledWith(cipherObj, userId);
|
||||
@@ -495,7 +495,7 @@ describe("Cipher Service", () => {
|
||||
encryptService.decryptToBytes.mockResolvedValue(new Uint8Array(32));
|
||||
jest.spyOn(cipherObj, "decrypt").mockResolvedValue(new CipherView(cipherObj));
|
||||
|
||||
const result = await cipherService.decryptCipherWithSdkOrLegacy(cipherObj, userId);
|
||||
const result = await cipherService.decrypt(cipherObj, userId);
|
||||
|
||||
expect(result).toEqual(new CipherView(cipherObj));
|
||||
expect(cipherObj.decrypt).toHaveBeenCalledWith(mockUserKey);
|
||||
|
||||
@@ -428,57 +428,57 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
): Promise<[CipherView[], CipherView[]] | null> {
|
||||
if (await this.configService.getFeatureFlag(FeatureFlag.PM19941MigrateCipherDomainToSdk)) {
|
||||
return this.decryptCiphersWithSdk(ciphers, userId);
|
||||
} else {
|
||||
const keys = await firstValueFrom(this.keyService.cipherDecryptionKeys$(userId, true));
|
||||
if (keys == null || (keys.userKey == null && Object.keys(keys.orgKeys).length === 0)) {
|
||||
// return early if there are no keys to decrypt with
|
||||
return null;
|
||||
}
|
||||
// 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 decryptStartTime = new Date().getTime();
|
||||
const allCipherViews = (
|
||||
await Promise.all(
|
||||
Object.entries(grouped).map(async ([orgId, groupedCiphers]) => {
|
||||
if (await this.configService.getFeatureFlag(FeatureFlag.PM4154_BulkEncryptionService)) {
|
||||
return await this.bulkEncryptService.decryptItems(
|
||||
groupedCiphers,
|
||||
keys.orgKeys[orgId as OrganizationId] ?? keys.userKey,
|
||||
);
|
||||
} else {
|
||||
return await this.encryptService.decryptItems(
|
||||
groupedCiphers,
|
||||
keys.orgKeys[orgId as OrganizationId] ?? keys.userKey,
|
||||
);
|
||||
}
|
||||
}),
|
||||
)
|
||||
)
|
||||
.flat()
|
||||
.sort(this.getLocaleSortingFunction());
|
||||
this.logService.info(
|
||||
`[CipherService] Decrypting ${allCipherViews.length} ciphers took ${new Date().getTime() - decryptStartTime}ms`,
|
||||
);
|
||||
// Split ciphers into two arrays, one for successfully decrypted ciphers and one for ciphers that failed to decrypt
|
||||
return allCipherViews.reduce(
|
||||
(acc, c) => {
|
||||
if (c.decryptionFailure) {
|
||||
acc[1].push(c);
|
||||
} else {
|
||||
acc[0].push(c);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[[], []] as [CipherView[], CipherView[]],
|
||||
);
|
||||
}
|
||||
|
||||
const keys = await firstValueFrom(this.keyService.cipherDecryptionKeys$(userId, true));
|
||||
if (keys == null || (keys.userKey == null && Object.keys(keys.orgKeys).length === 0)) {
|
||||
// return early if there are no keys to decrypt with
|
||||
return null;
|
||||
}
|
||||
// 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 decryptStartTime = new Date().getTime();
|
||||
const allCipherViews = (
|
||||
await Promise.all(
|
||||
Object.entries(grouped).map(async ([orgId, groupedCiphers]) => {
|
||||
if (await this.configService.getFeatureFlag(FeatureFlag.PM4154_BulkEncryptionService)) {
|
||||
return await this.bulkEncryptService.decryptItems(
|
||||
groupedCiphers,
|
||||
keys.orgKeys[orgId as OrganizationId] ?? keys.userKey,
|
||||
);
|
||||
} else {
|
||||
return await this.encryptService.decryptItems(
|
||||
groupedCiphers,
|
||||
keys.orgKeys[orgId as OrganizationId] ?? keys.userKey,
|
||||
);
|
||||
}
|
||||
}),
|
||||
)
|
||||
)
|
||||
.flat()
|
||||
.sort(this.getLocaleSortingFunction());
|
||||
this.logService.info(
|
||||
`[CipherService] Decrypting ${allCipherViews.length} ciphers took ${new Date().getTime() - decryptStartTime}ms`,
|
||||
);
|
||||
// Split ciphers into two arrays, one for successfully decrypted ciphers and one for ciphers that failed to decrypt
|
||||
return allCipherViews.reduce(
|
||||
(acc, c) => {
|
||||
if (c.decryptionFailure) {
|
||||
acc[1].push(c);
|
||||
} else {
|
||||
acc[0].push(c);
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
[[], []] as [CipherView[], CipherView[]],
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -487,7 +487,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
* @param userId The user ID to use for decryption.
|
||||
* @returns A promise that resolves to the decrypted cipher view.
|
||||
*/
|
||||
async decryptCipherWithSdkOrLegacy(cipher: Cipher, userId: UserId): Promise<CipherView> {
|
||||
async decrypt(cipher: Cipher, userId: UserId): Promise<CipherView> {
|
||||
if (await this.configService.getFeatureFlag(FeatureFlag.PM19941MigrateCipherDomainToSdk)) {
|
||||
return await this.cipherEncryptionService.decrypt(cipher, userId);
|
||||
} else {
|
||||
@@ -907,7 +907,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
//then we rollback to using the user key as the main key of encryption of the item
|
||||
//in order to keep item and it's attachments with the same encryption level
|
||||
if (cipher.key != null && !cipherKeyEncryptionEnabled) {
|
||||
const model = await this.decryptCipherWithSdkOrLegacy(cipher, userId);
|
||||
const model = await this.decrypt(cipher, userId);
|
||||
cipher = await this.encrypt(model, userId);
|
||||
await this.updateWithServer(cipher);
|
||||
}
|
||||
@@ -1438,7 +1438,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
originalCipher: Cipher,
|
||||
userId: UserId,
|
||||
): Promise<void> {
|
||||
const existingCipher = await this.decryptCipherWithSdkOrLegacy(originalCipher, userId);
|
||||
const existingCipher = await this.decrypt(originalCipher, userId);
|
||||
model.passwordHistory = existingCipher.passwordHistory || [];
|
||||
if (model.type === CipherType.Login && existingCipher.type === CipherType.Login) {
|
||||
if (
|
||||
|
||||
@@ -212,7 +212,6 @@ describe("DefaultCipherEncryptionService", () => {
|
||||
);
|
||||
expect(mockSdkClient.vault().ciphers().decrypt_fido2_private_key).toHaveBeenCalledWith(
|
||||
sdkCipherView,
|
||||
fido2Credentials[0].keyValue,
|
||||
);
|
||||
expect(Fido2CredentialView.fromSdkFido2CredentialView).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
@@ -43,15 +43,14 @@ export class DefaultCipherEncryptionService implements CipherEncryptionService {
|
||||
.ciphers()
|
||||
.decrypt_fido2_credentials(sdkCipherView);
|
||||
|
||||
// TEMPORARY: Manually decrypt the keyValue for Fido2 credentials since don't currently use the SDK for Fido2 Authentication.
|
||||
const decryptedKeyValue = ref.value
|
||||
.vault()
|
||||
.ciphers()
|
||||
.decrypt_fido2_private_key(sdkCipherView);
|
||||
|
||||
clientCipherView.login.fido2Credentials = fido2CredentialViews
|
||||
.map((f) => {
|
||||
// TEMPORARY: Manually decrypt the keyValue for Fido2 credentials since don't currently use
|
||||
// the SDK for Fido2 Authentication.
|
||||
const decryptedKeyValue = ref.value
|
||||
.vault()
|
||||
.ciphers()
|
||||
.decrypt_fido2_private_key(sdkCipherView, f.keyValue);
|
||||
|
||||
const view = Fido2CredentialView.fromSdkFido2CredentialView(f)!;
|
||||
|
||||
return {
|
||||
|
||||
@@ -118,7 +118,7 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
||||
const activeUserId = await firstValueFrom(
|
||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
||||
);
|
||||
const view = await this.cipherService.decryptCipherWithSdkOrLegacy(cipher, activeUserId);
|
||||
const view = await this.cipherService.decrypt(cipher, activeUserId);
|
||||
this.cleanupCipher(view);
|
||||
this.result.ciphers.push(view);
|
||||
}
|
||||
|
||||
@@ -153,11 +153,9 @@ export class OrganizationVaultExportService
|
||||
.forEach(async (c) => {
|
||||
const cipher = new Cipher(new CipherData(c));
|
||||
exportPromises.push(
|
||||
this.cipherService
|
||||
.decryptCipherWithSdkOrLegacy(cipher, activeUserId)
|
||||
.then((decCipher) => {
|
||||
decCiphers.push(decCipher);
|
||||
}),
|
||||
this.cipherService.decrypt(cipher, activeUserId).then((decCipher) => {
|
||||
decCiphers.push(decCipher);
|
||||
}),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ describe("CipherAttachmentsComponent", () => {
|
||||
get: cipherServiceGet,
|
||||
saveAttachmentWithServer,
|
||||
getKeyForCipherKeyDecryption: () => Promise.resolve(null),
|
||||
decryptCipherWithSdkOrLegacy: jest.fn().mockResolvedValue(cipherView),
|
||||
decrypt: jest.fn().mockResolvedValue(cipherView),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -121,10 +121,7 @@ export class CipherAttachmentsComponent implements OnInit, AfterViewInit {
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
this.cipherDomain = await this.cipherService.get(this.cipherId, this.activeUserId);
|
||||
this.cipher = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
this.cipherDomain,
|
||||
this.activeUserId,
|
||||
);
|
||||
this.cipher = await this.cipherService.decrypt(this.cipherDomain, this.activeUserId);
|
||||
|
||||
// Update the initial state of the submit button
|
||||
if (this.submitBtn) {
|
||||
@@ -194,10 +191,7 @@ export class CipherAttachmentsComponent implements OnInit, AfterViewInit {
|
||||
);
|
||||
|
||||
// re-decrypt the cipher to update the attachments
|
||||
this.cipher = await this.cipherService.decryptCipherWithSdkOrLegacy(
|
||||
this.cipherDomain,
|
||||
this.activeUserId,
|
||||
);
|
||||
this.cipher = await this.cipherService.decrypt(this.cipherDomain, this.activeUserId);
|
||||
|
||||
// Reset reactive form and input element
|
||||
this.fileInput.nativeElement.value = "";
|
||||
|
||||
@@ -23,7 +23,7 @@ export class DefaultCipherFormService implements CipherFormService {
|
||||
|
||||
async decryptCipher(cipher: Cipher): Promise<CipherView> {
|
||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||
return await this.cipherService.decryptCipherWithSdkOrLegacy(cipher, activeUserId);
|
||||
return await this.cipherService.decrypt(cipher, activeUserId);
|
||||
}
|
||||
|
||||
async saveCipher(cipher: CipherView, config: CipherFormConfig): Promise<CipherView> {
|
||||
@@ -42,7 +42,7 @@ export class DefaultCipherFormService implements CipherFormService {
|
||||
// Creating a new cipher
|
||||
if (cipher.id == null) {
|
||||
savedCipher = await this.cipherService.createWithServer(encryptedCipher, config.admin);
|
||||
return await this.cipherService.decryptCipherWithSdkOrLegacy(savedCipher, activeUserId);
|
||||
return await this.cipherService.decrypt(savedCipher, activeUserId);
|
||||
}
|
||||
|
||||
if (config.originalCipher == null) {
|
||||
@@ -94,6 +94,6 @@ export class DefaultCipherFormService implements CipherFormService {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await this.cipherService.decryptCipherWithSdkOrLegacy(savedCipher, activeUserId);
|
||||
return await this.cipherService.decrypt(savedCipher, activeUserId);
|
||||
}
|
||||
}
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -24,7 +24,7 @@
|
||||
"@angular/platform-browser": "18.2.13",
|
||||
"@angular/platform-browser-dynamic": "18.2.13",
|
||||
"@angular/router": "18.2.13",
|
||||
"@bitwarden/sdk-internal": "0.2.0-main.135",
|
||||
"@bitwarden/sdk-internal": "0.2.0-main.138",
|
||||
"@electron/fuses": "1.8.0",
|
||||
"@emotion/css": "11.13.5",
|
||||
"@koa/multer": "3.0.2",
|
||||
@@ -4700,9 +4700,9 @@
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@bitwarden/sdk-internal": {
|
||||
"version": "0.2.0-main.135",
|
||||
"resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.135.tgz",
|
||||
"integrity": "sha512-A8NkuDtJSPRiuQ8y+Wi80IOQCf5ZHWflyi1mu53zzlyacWPoI9acGOHoEL0KgbsRuYi9s+3YFfM04g8PyMd3BA==",
|
||||
"version": "0.2.0-main.138",
|
||||
"resolved": "https://registry.npmjs.org/@bitwarden/sdk-internal/-/sdk-internal-0.2.0-main.138.tgz",
|
||||
"integrity": "sha512-cKAkO3/nyAagNHI/GQ59uaDFkFH316bENnN/kAQXYzi27s8op6hcc34MyJ9voZvnGe8xRDSn6b8M9DPNzr5W+w==",
|
||||
"license": "GPL-3.0"
|
||||
},
|
||||
"node_modules/@bitwarden/send-ui": {
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
"@angular/platform-browser": "18.2.13",
|
||||
"@angular/platform-browser-dynamic": "18.2.13",
|
||||
"@angular/router": "18.2.13",
|
||||
"@bitwarden/sdk-internal": "0.2.0-main.135",
|
||||
"@bitwarden/sdk-internal": "0.2.0-main.138",
|
||||
"@electron/fuses": "1.8.0",
|
||||
"@emotion/css": "11.13.5",
|
||||
"@koa/multer": "3.0.2",
|
||||
|
||||
Reference in New Issue
Block a user