mirror of
https://github.com/bitwarden/browser
synced 2025-12-12 06:13:38 +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:
@@ -990,6 +990,7 @@ export default class MainBackground {
|
|||||||
|
|
||||||
this.sendStateProvider = new SendStateProvider(this.stateProvider);
|
this.sendStateProvider = new SendStateProvider(this.stateProvider);
|
||||||
this.sendService = new SendService(
|
this.sendService = new SendService(
|
||||||
|
this.accountService,
|
||||||
this.keyService,
|
this.keyService,
|
||||||
this.i18nService,
|
this.i18nService,
|
||||||
this.keyGenerationService,
|
this.keyGenerationService,
|
||||||
|
|||||||
@@ -211,6 +211,7 @@ export class OssServeConfigurator {
|
|||||||
this.serviceContainer.sendService,
|
this.serviceContainer.sendService,
|
||||||
this.serviceContainer.sendApiService,
|
this.serviceContainer.sendApiService,
|
||||||
this.serviceContainer.environmentService,
|
this.serviceContainer.environmentService,
|
||||||
|
this.serviceContainer.accountService,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -552,6 +552,7 @@ export class ServiceContainer {
|
|||||||
this.sendStateProvider = new SendStateProvider(this.stateProvider);
|
this.sendStateProvider = new SendStateProvider(this.stateProvider);
|
||||||
|
|
||||||
this.sendService = new SendService(
|
this.sendService = new SendService(
|
||||||
|
this.accountService,
|
||||||
this.keyService,
|
this.keyService,
|
||||||
this.i18nService,
|
this.i18nService,
|
||||||
this.keyGenerationService,
|
this.keyGenerationService,
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import * as path from "path";
|
|||||||
import { firstValueFrom, switchMap } from "rxjs";
|
import { firstValueFrom, switchMap } from "rxjs";
|
||||||
|
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||||
@@ -142,7 +143,8 @@ export class SendCreateCommand {
|
|||||||
|
|
||||||
await this.sendApiService.save([encSend, fileData]);
|
await this.sendApiService.save([encSend, fileData]);
|
||||||
const newSend = await this.sendService.getFromState(encSend.id);
|
const newSend = await this.sendService.getFromState(encSend.id);
|
||||||
const decSend = await newSend.decrypt();
|
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
|
const decSend = await newSend.decrypt(activeUserId);
|
||||||
const env = await firstValueFrom(this.environmentService.environment$);
|
const env = await firstValueFrom(this.environmentService.environment$);
|
||||||
const res = new SendResponse(decSend, env.getWebVaultUrl());
|
const res = new SendResponse(decSend, env.getWebVaultUrl());
|
||||||
return Response.success(res);
|
return Response.success(res);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||||
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
import { SendType } from "@bitwarden/common/tools/send/enums/send-type";
|
||||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||||
@@ -83,7 +84,8 @@ export class SendEditCommand {
|
|||||||
return Response.error("Premium status is required to use this feature.");
|
return Response.error("Premium status is required to use this feature.");
|
||||||
}
|
}
|
||||||
|
|
||||||
let sendView = await send.decrypt();
|
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
|
let sendView = await send.decrypt(activeUserId);
|
||||||
sendView = SendResponse.toView(req, sendView);
|
sendView = SendResponse.toView(req, sendView);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import { Utils } from "@bitwarden/common/platform/misc/utils";
|
|||||||
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
|
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
|
||||||
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
import { SendService } from "@bitwarden/common/tools/send/services/send.service.abstraction";
|
||||||
import { SearchService } from "@bitwarden/common/vault/abstractions/search.service";
|
import { SearchService } from "@bitwarden/common/vault/abstractions/search.service";
|
||||||
|
import { isGuid } from "@bitwarden/guid";
|
||||||
|
|
||||||
import { DownloadCommand } from "../../../commands/download.command";
|
import { DownloadCommand } from "../../../commands/download.command";
|
||||||
import { Response } from "../../../models/response";
|
import { Response } from "../../../models/response";
|
||||||
@@ -74,13 +75,13 @@ export class SendGetCommand extends DownloadCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getSendView(id: string): Promise<SendView | SendView[]> {
|
private async getSendView(id: string): Promise<SendView | SendView[]> {
|
||||||
if (Utils.isGuid(id)) {
|
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
|
if (isGuid(id)) {
|
||||||
const send = await this.sendService.getFromState(id);
|
const send = await this.sendService.getFromState(id);
|
||||||
if (send != null) {
|
if (send != null) {
|
||||||
return await send.decrypt();
|
return await send.decrypt(activeUserId);
|
||||||
}
|
}
|
||||||
} else if (id.trim() !== "") {
|
} else if (id.trim() !== "") {
|
||||||
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
|
||||||
let sends = await this.sendService.getAllDecryptedFromState(activeUserId);
|
let sends = await this.sendService.getAllDecryptedFromState(activeUserId);
|
||||||
sends = this.searchService.searchSends(sends, id);
|
sends = this.searchService.searchSends(sends, id);
|
||||||
if (sends.length > 1) {
|
if (sends.length > 1) {
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { firstValueFrom } from "rxjs";
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
import { SendService } from "@bitwarden/common/tools/send/services//send.service.abstraction";
|
import { SendService } from "@bitwarden/common/tools/send/services//send.service.abstraction";
|
||||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||||
@@ -14,6 +16,7 @@ export class SendRemovePasswordCommand {
|
|||||||
private sendService: SendService,
|
private sendService: SendService,
|
||||||
private sendApiService: SendApiService,
|
private sendApiService: SendApiService,
|
||||||
private environmentService: EnvironmentService,
|
private environmentService: EnvironmentService,
|
||||||
|
private accountService: AccountService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async run(id: string) {
|
async run(id: string) {
|
||||||
@@ -21,7 +24,8 @@ export class SendRemovePasswordCommand {
|
|||||||
await this.sendApiService.removePassword(id);
|
await this.sendApiService.removePassword(id);
|
||||||
|
|
||||||
const updatedSend = await firstValueFrom(this.sendService.get$(id));
|
const updatedSend = await firstValueFrom(this.sendService.get$(id));
|
||||||
const decSend = await updatedSend.decrypt();
|
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
|
const decSend = await updatedSend.decrypt(activeUserId);
|
||||||
const env = await firstValueFrom(this.environmentService.environment$);
|
const env = await firstValueFrom(this.environmentService.environment$);
|
||||||
const webVaultUrl = env.getWebVaultUrl();
|
const webVaultUrl = env.getWebVaultUrl();
|
||||||
const res = new SendResponse(decSend, webVaultUrl);
|
const res = new SendResponse(decSend, webVaultUrl);
|
||||||
|
|||||||
@@ -297,6 +297,7 @@ export class SendProgram extends BaseProgram {
|
|||||||
this.serviceContainer.sendService,
|
this.serviceContainer.sendService,
|
||||||
this.serviceContainer.sendApiService,
|
this.serviceContainer.sendApiService,
|
||||||
this.serviceContainer.environmentService,
|
this.serviceContainer.environmentService,
|
||||||
|
this.serviceContainer.accountService,
|
||||||
);
|
);
|
||||||
const response = await cmd.run(id);
|
const response = await cmd.run(id);
|
||||||
this.processResponse(response);
|
this.processResponse(response);
|
||||||
|
|||||||
@@ -3,11 +3,13 @@
|
|||||||
import { CommonModule, DatePipe } from "@angular/common";
|
import { CommonModule, DatePipe } from "@angular/common";
|
||||||
import { Component } from "@angular/core";
|
import { Component } from "@angular/core";
|
||||||
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
|
import { FormBuilder, ReactiveFormsModule } from "@angular/forms";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||||
import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/tools/send/add-edit.component";
|
import { AddEditComponent as BaseAddEditComponent } from "@bitwarden/angular/tools/send/add-edit.component";
|
||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abstractions/account/billing-account-profile-state.service";
|
||||||
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
@@ -63,7 +65,8 @@ export class AddEditComponent extends BaseAddEditComponent {
|
|||||||
|
|
||||||
async refresh() {
|
async refresh() {
|
||||||
const send = await this.loadSend();
|
const send = await this.loadSend();
|
||||||
this.send = await send.decrypt();
|
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
|
this.send = await send.decrypt(userId);
|
||||||
this.updateFormValues();
|
this.updateFormValues();
|
||||||
this.hasPassword = this.send.password != null && this.send.password.trim() !== "";
|
this.hasPassword = this.send.password != null && this.send.password.trim() !== "";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -791,6 +791,7 @@ const safeProviders: SafeProvider[] = [
|
|||||||
provide: InternalSendService,
|
provide: InternalSendService,
|
||||||
useClass: SendService,
|
useClass: SendService,
|
||||||
deps: [
|
deps: [
|
||||||
|
AccountServiceAbstraction,
|
||||||
KeyService,
|
KeyService,
|
||||||
I18nServiceAbstraction,
|
I18nServiceAbstraction,
|
||||||
KeyGenerationService,
|
KeyGenerationService,
|
||||||
|
|||||||
@@ -260,12 +260,19 @@ export class AddEditComponent implements OnInit, OnDestroy {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (this.editMode) {
|
if (this.editMode) {
|
||||||
this.sendService
|
this.accountService.activeAccount$
|
||||||
.get$(this.sendId)
|
|
||||||
.pipe(
|
.pipe(
|
||||||
//Promise.reject will complete the BehaviourSubject, if desktop starts relying only on BehaviourSubject, this should be changed.
|
getUserId,
|
||||||
concatMap((s) =>
|
switchMap((userId) =>
|
||||||
s instanceof Send ? s.decrypt() : Promise.reject(new Error("Failed to load send.")),
|
this.sendService
|
||||||
|
.get$(this.sendId)
|
||||||
|
.pipe(
|
||||||
|
concatMap((s) =>
|
||||||
|
s instanceof Send
|
||||||
|
? s.decrypt(userId)
|
||||||
|
: Promise.reject(new Error("Failed to load send.")),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
takeUntil(this.destroy$),
|
takeUntil(this.destroy$),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { mock } from "jest-mock-extended";
|
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.
|
// 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
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { KeyService } from "@bitwarden/key-management";
|
import { KeyService } from "@bitwarden/key-management";
|
||||||
@@ -97,6 +99,7 @@ describe("Send", () => {
|
|||||||
const text = mock<SendText>();
|
const text = mock<SendText>();
|
||||||
text.decrypt.mockResolvedValue("textView" as any);
|
text.decrypt.mockResolvedValue("textView" as any);
|
||||||
const userKey = new SymmetricCryptoKey(new Uint8Array(32)) as UserKey;
|
const userKey = new SymmetricCryptoKey(new Uint8Array(32)) as UserKey;
|
||||||
|
const userId = emptyGuid as UserId;
|
||||||
|
|
||||||
const send = new Send();
|
const send = new Send();
|
||||||
send.id = "id";
|
send.id = "id";
|
||||||
@@ -120,11 +123,11 @@ describe("Send", () => {
|
|||||||
.calledWith(send.key, userKey)
|
.calledWith(send.key, userKey)
|
||||||
.mockResolvedValue(makeStaticByteArray(32));
|
.mockResolvedValue(makeStaticByteArray(32));
|
||||||
keyService.makeSendKey.mockResolvedValue("cryptoKey" as any);
|
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);
|
(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(text.decrypt).toHaveBeenNthCalledWith(1, "cryptoKey");
|
||||||
expect(send.name.decrypt).toHaveBeenNthCalledWith(
|
expect(send.name.decrypt).toHaveBeenNthCalledWith(
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
import { Jsonify } from "type-fest";
|
import { Jsonify } from "type-fest";
|
||||||
|
|
||||||
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
|
|
||||||
import { EncString } from "../../../../key-management/crypto/models/enc-string";
|
import { EncString } from "../../../../key-management/crypto/models/enc-string";
|
||||||
import { Utils } from "../../../../platform/misc/utils";
|
import { Utils } from "../../../../platform/misc/utils";
|
||||||
import Domain from "../../../../platform/models/domain/domain-base";
|
import Domain from "../../../../platform/models/domain/domain-base";
|
||||||
@@ -73,22 +76,18 @@ export class Send extends Domain {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async decrypt(): Promise<SendView> {
|
async decrypt(userId: UserId): Promise<SendView> {
|
||||||
const model = new SendView(this);
|
if (!userId) {
|
||||||
|
throw new Error("User ID must not be null or undefined");
|
||||||
|
}
|
||||||
|
|
||||||
|
const model = new SendView(this);
|
||||||
const keyService = Utils.getContainerService().getKeyService();
|
const keyService = Utils.getContainerService().getKeyService();
|
||||||
const encryptService = Utils.getContainerService().getEncryptService();
|
const encryptService = Utils.getContainerService().getEncryptService();
|
||||||
|
const sendKeyEncryptionKey = await firstValueFrom(keyService.userKey$(userId));
|
||||||
try {
|
// model.key is a seed used to derive a key, not a SymmetricCryptoKey
|
||||||
const sendKeyEncryptionKey = await keyService.getUserKey();
|
model.key = await encryptService.decryptBytes(this.key, sendKeyEncryptionKey);
|
||||||
// model.key is a seed used to derive a key, not a SymmetricCryptoKey
|
model.cryptoKey = await keyService.makeSendKey(model.key);
|
||||||
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?
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.decryptObj<Send, SendView>(this, model, ["name", "notes"], null, model.cryptoKey);
|
await this.decryptObj<Send, SendView>(this, model, ["name", "notes"], null, model.cryptoKey);
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,7 @@ describe("SendService", () => {
|
|||||||
decryptedState.nextState([testSendViewData("1", "Test Send")]);
|
decryptedState.nextState([testSendViewData("1", "Test Send")]);
|
||||||
|
|
||||||
sendService = new SendService(
|
sendService = new SendService(
|
||||||
|
accountService,
|
||||||
keyService,
|
keyService,
|
||||||
i18nService,
|
i18nService,
|
||||||
keyGenerationService,
|
keyGenerationService,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { Observable, concatMap, distinctUntilChanged, firstValueFrom, map } from "rxjs";
|
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.
|
// 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
|
// eslint-disable-next-line no-restricted-imports
|
||||||
import { PBKDF2KdfConfig, KeyService } from "@bitwarden/key-management";
|
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))),
|
map(([, record]) => Object.values(record || {}).map((data) => new Send(data))),
|
||||||
);
|
);
|
||||||
sendViews$ = this.stateProvider.encryptedState$.pipe(
|
sendViews$ = this.stateProvider.encryptedState$.pipe(
|
||||||
concatMap(([, record]) =>
|
concatMap(([userId, record]) =>
|
||||||
this.decryptSends(Object.values(record || {}).map((data) => new Send(data))),
|
this.decryptSends(
|
||||||
|
Object.values(record || {}).map((data) => new Send(data)),
|
||||||
|
userId,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
private accountService: AccountService,
|
||||||
private keyService: KeyService,
|
private keyService: KeyService,
|
||||||
private i18nService: I18nService,
|
private i18nService: I18nService,
|
||||||
private keyGenerationService: KeyGenerationService,
|
private keyGenerationService: KeyGenerationService,
|
||||||
@@ -89,8 +94,9 @@ export class SendService implements InternalSendServiceAbstraction {
|
|||||||
);
|
);
|
||||||
send.password = passwordKey.keyB64;
|
send.password = passwordKey.keyB64;
|
||||||
}
|
}
|
||||||
|
const userId = (await firstValueFrom(this.accountService.activeAccount$)).id;
|
||||||
if (userKey == null) {
|
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
|
// Key is not a SymmetricCryptoKey, but key material used to derive the cryptoKey
|
||||||
send.key = await this.encryptService.encryptBytes(model.key, userKey);
|
send.key = await this.encryptService.encryptBytes(model.key, userKey);
|
||||||
@@ -111,11 +117,12 @@ export class SendService implements InternalSendServiceAbstraction {
|
|||||||
model.file.fileName,
|
model.file.fileName,
|
||||||
file,
|
file,
|
||||||
model.cryptoKey,
|
model.cryptoKey,
|
||||||
|
userId,
|
||||||
);
|
);
|
||||||
send.file.fileName = name;
|
send.file.fileName = name;
|
||||||
fileData = data;
|
fileData = data;
|
||||||
} else {
|
} 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[]> {
|
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();
|
let decSends = await this.stateProvider.getDecryptedSends();
|
||||||
if (decSends != null) {
|
if (decSends != null) {
|
||||||
return decSends;
|
return decSends;
|
||||||
@@ -222,7 +232,7 @@ export class SendService implements InternalSendServiceAbstraction {
|
|||||||
const promises: Promise<any>[] = [];
|
const promises: Promise<any>[] = [];
|
||||||
const sends = await this.getAll();
|
const sends = await this.getAll();
|
||||||
sends.forEach((send) => {
|
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);
|
await Promise.all(promises);
|
||||||
@@ -311,7 +321,12 @@ export class SendService implements InternalSendServiceAbstraction {
|
|||||||
return requests;
|
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) => {
|
return new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsArrayBuffer(file);
|
reader.readAsArrayBuffer(file);
|
||||||
@@ -321,6 +336,7 @@ export class SendService implements InternalSendServiceAbstraction {
|
|||||||
file.name,
|
file.name,
|
||||||
evt.target.result as ArrayBuffer,
|
evt.target.result as ArrayBuffer,
|
||||||
key,
|
key,
|
||||||
|
userId,
|
||||||
);
|
);
|
||||||
send.file.fileName = name;
|
send.file.fileName = name;
|
||||||
resolve(data);
|
resolve(data);
|
||||||
@@ -338,17 +354,18 @@ export class SendService implements InternalSendServiceAbstraction {
|
|||||||
fileName: string,
|
fileName: string,
|
||||||
data: ArrayBuffer,
|
data: ArrayBuffer,
|
||||||
key: SymmetricCryptoKey,
|
key: SymmetricCryptoKey,
|
||||||
|
userId: UserId,
|
||||||
): Promise<[EncString, EncArrayBuffer]> {
|
): Promise<[EncString, EncArrayBuffer]> {
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
key = await this.keyService.getUserKey();
|
key = await firstValueFrom(this.keyService.userKey$(userId));
|
||||||
}
|
}
|
||||||
const encFileName = await this.encryptService.encryptString(fileName, key);
|
const encFileName = await this.encryptService.encryptString(fileName, key);
|
||||||
const encFileData = await this.encryptService.encryptFileData(new Uint8Array(data), key);
|
const encFileData = await this.encryptService.encryptFileData(new Uint8Array(data), key);
|
||||||
return [encFileName, encFileData];
|
return [encFileName, encFileData];
|
||||||
}
|
}
|
||||||
|
|
||||||
private async decryptSends(sends: Send[]) {
|
private async decryptSends(sends: Send[], userId: UserId) {
|
||||||
const decryptSendPromises = sends.map((s) => s.decrypt());
|
const decryptSendPromises = sends.map((s) => s.decrypt(userId));
|
||||||
const decryptedSends = await Promise.all(decryptSendPromises);
|
const decryptedSends = await Promise.all(decryptSendPromises);
|
||||||
|
|
||||||
decryptedSends.sort(Utils.getSortFunction(this.i18nService, "name"));
|
decryptedSends.sort(Utils.getSortFunction(this.i18nService, "name"));
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
|||||||
|
|
||||||
for (const c of results.items) {
|
for (const c of results.items) {
|
||||||
const cipher = CipherWithIdExport.toDomain(c);
|
const cipher = CipherWithIdExport.toDomain(c);
|
||||||
// reset ids incase they were set for some reason
|
// reset ids in case they were set for some reason
|
||||||
cipher.id = null;
|
cipher.id = null;
|
||||||
cipher.organizationId = this.organizationId;
|
cipher.organizationId = this.organizationId;
|
||||||
cipher.collectionIds = null;
|
cipher.collectionIds = null;
|
||||||
@@ -131,7 +131,7 @@ export class BitwardenJsonImporter extends BaseImporter implements Importer {
|
|||||||
|
|
||||||
results.items.forEach((c) => {
|
results.items.forEach((c) => {
|
||||||
const cipher = CipherWithIdExport.toView(c);
|
const cipher = CipherWithIdExport.toView(c);
|
||||||
// reset ids incase they were set for some reason
|
// reset ids in case they were set for some reason
|
||||||
cipher.id = null;
|
cipher.id = null;
|
||||||
cipher.organizationId = null;
|
cipher.organizationId = null;
|
||||||
cipher.collectionIds = null;
|
cipher.collectionIds = null;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import { Utils } from "@bitwarden/common/platform/misc/utils";
|
|||||||
import { emptyGuid, OrganizationId } from "@bitwarden/common/types/guid";
|
import { emptyGuid, OrganizationId } from "@bitwarden/common/types/guid";
|
||||||
import { OrgKey, UserKey } from "@bitwarden/common/types/key";
|
import { OrgKey, UserKey } from "@bitwarden/common/types/key";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
import { newGuid } from "@bitwarden/guid";
|
|
||||||
import { KdfType, KeyService } from "@bitwarden/key-management";
|
import { KdfType, KeyService } from "@bitwarden/key-management";
|
||||||
import { UserId } from "@bitwarden/user-core";
|
import { UserId } from "@bitwarden/user-core";
|
||||||
|
|
||||||
@@ -41,7 +40,7 @@ describe("BitwardenPasswordProtectedImporter", () => {
|
|||||||
accountService = mock<AccountService>();
|
accountService = mock<AccountService>();
|
||||||
|
|
||||||
accountService.activeAccount$ = of({
|
accountService.activeAccount$ = of({
|
||||||
id: newGuid() as UserId,
|
id: emptyGuid as UserId,
|
||||||
email: "test@example.com",
|
email: "test@example.com",
|
||||||
emailVerified: true,
|
emailVerified: true,
|
||||||
name: "Test User",
|
name: "Test User",
|
||||||
@@ -52,8 +51,8 @@ describe("BitwardenPasswordProtectedImporter", () => {
|
|||||||
The key values below are never read, empty objects are cast as types for compilation type checking only.
|
The key values below are never read, empty objects are cast as types for compilation type checking only.
|
||||||
Tests specific to key contents are in key-service.spec.ts
|
Tests specific to key contents are in key-service.spec.ts
|
||||||
*/
|
*/
|
||||||
const mockOrgKey = {} as unknown as OrgKey;
|
const mockOrgKey = {} as OrgKey;
|
||||||
const mockUserKey = {} as unknown as UserKey;
|
const mockUserKey = {} as UserKey;
|
||||||
|
|
||||||
keyService.orgKeys$.mockImplementation(() =>
|
keyService.orgKeys$.mockImplementation(() =>
|
||||||
of({ [mockOrgId]: mockOrgKey } as Record<OrganizationId, OrgKey>),
|
of({ [mockOrgId]: mockOrgKey } as Record<OrganizationId, OrgKey>),
|
||||||
@@ -99,7 +98,7 @@ describe("BitwardenPasswordProtectedImporter", () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
accountService.activeAccount$ = of({
|
accountService.activeAccount$ = of({
|
||||||
id: newGuid() as UserId,
|
id: emptyGuid as UserId,
|
||||||
email: "test@example.com",
|
email: "test@example.com",
|
||||||
emailVerified: true,
|
emailVerified: true,
|
||||||
name: "Test User",
|
name: "Test User",
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
CollectionView,
|
CollectionView,
|
||||||
} from "@bitwarden/admin-console/common";
|
} from "@bitwarden/admin-console/common";
|
||||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { DeviceType } from "@bitwarden/common/enums";
|
import { DeviceType } from "@bitwarden/common/enums";
|
||||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||||
import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
|
import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
|
||||||
@@ -21,7 +22,7 @@ import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.servic
|
|||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { SemanticLogger } from "@bitwarden/common/tools/log";
|
import { SemanticLogger } from "@bitwarden/common/tools/log";
|
||||||
import { SystemServiceProvider } from "@bitwarden/common/tools/providers";
|
import { SystemServiceProvider } from "@bitwarden/common/tools/providers";
|
||||||
import { OrganizationId } from "@bitwarden/common/types/guid";
|
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||||
import { CipherType, toCipherTypeName } from "@bitwarden/common/vault/enums";
|
import { CipherType, toCipherTypeName } from "@bitwarden/common/vault/enums";
|
||||||
@@ -238,10 +239,11 @@ export class ImportService implements ImportServiceAbstraction {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await this.setImportTarget(importResult, organizationId, selectedImportTarget);
|
await this.setImportTarget(importResult, organizationId, selectedImportTarget);
|
||||||
|
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
if (organizationId != null) {
|
if (organizationId != null) {
|
||||||
await this.handleOrganizationalImport(importResult, organizationId);
|
await this.handleOrganizationalImport(importResult, organizationId, userId);
|
||||||
} else {
|
} else {
|
||||||
await this.handleIndividualImport(importResult);
|
await this.handleIndividualImport(importResult, userId);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorResponse = new ErrorResponse(error, 400);
|
const errorResponse = new ErrorResponse(error, 400);
|
||||||
@@ -419,16 +421,14 @@ export class ImportService implements ImportServiceAbstraction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async handleIndividualImport(importResult: ImportResult) {
|
private async handleIndividualImport(importResult: ImportResult, userId: UserId) {
|
||||||
const request = new ImportCiphersRequest();
|
const request = new ImportCiphersRequest();
|
||||||
const activeUserId = await firstValueFrom(
|
|
||||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
|
||||||
);
|
|
||||||
for (let i = 0; i < importResult.ciphers.length; i++) {
|
for (let i = 0; i < importResult.ciphers.length; i++) {
|
||||||
const c = await this.cipherService.encrypt(importResult.ciphers[i], activeUserId);
|
const c = await this.cipherService.encrypt(importResult.ciphers[i], userId);
|
||||||
request.ciphers.push(new CipherRequest(c));
|
request.ciphers.push(new CipherRequest(c));
|
||||||
}
|
}
|
||||||
const userKey = await this.keyService.getUserKey(activeUserId);
|
const userKey = await firstValueFrom(this.keyService.userKey$(userId));
|
||||||
|
|
||||||
if (importResult.folders != null) {
|
if (importResult.folders != null) {
|
||||||
for (let i = 0; i < importResult.folders.length; i++) {
|
for (let i = 0; i < importResult.folders.length; i++) {
|
||||||
const f = await this.folderService.encrypt(importResult.folders[i], userKey);
|
const f = await this.folderService.encrypt(importResult.folders[i], userKey);
|
||||||
@@ -446,20 +446,18 @@ export class ImportService implements ImportServiceAbstraction {
|
|||||||
private async handleOrganizationalImport(
|
private async handleOrganizationalImport(
|
||||||
importResult: ImportResult,
|
importResult: ImportResult,
|
||||||
organizationId: OrganizationId,
|
organizationId: OrganizationId,
|
||||||
|
userId: UserId,
|
||||||
) {
|
) {
|
||||||
const request = new ImportOrganizationCiphersRequest();
|
const request = new ImportOrganizationCiphersRequest();
|
||||||
const activeUserId = await firstValueFrom(
|
|
||||||
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
|
|
||||||
);
|
|
||||||
for (let i = 0; i < importResult.ciphers.length; i++) {
|
for (let i = 0; i < importResult.ciphers.length; i++) {
|
||||||
importResult.ciphers[i].organizationId = organizationId;
|
importResult.ciphers[i].organizationId = organizationId;
|
||||||
const c = await this.cipherService.encrypt(importResult.ciphers[i], activeUserId);
|
const c = await this.cipherService.encrypt(importResult.ciphers[i], userId);
|
||||||
request.ciphers.push(new CipherRequest(c));
|
request.ciphers.push(new CipherRequest(c));
|
||||||
}
|
}
|
||||||
if (importResult.collections != null) {
|
if (importResult.collections != null) {
|
||||||
for (let i = 0; i < importResult.collections.length; i++) {
|
for (let i = 0; i < importResult.collections.length; i++) {
|
||||||
importResult.collections[i].organizationId = organizationId;
|
importResult.collections[i].organizationId = organizationId;
|
||||||
const c = await this.collectionService.encrypt(importResult.collections[i], activeUserId);
|
const c = await this.collectionService.encrypt(importResult.collections[i], userId);
|
||||||
request.collections.push(new CollectionWithIdRequest(c));
|
request.collections.push(new CollectionWithIdRequest(c));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
|
import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin.service.abstraction";
|
||||||
import { CipherWithIdExport } from "@bitwarden/common/models/export/cipher-with-ids.export";
|
import { CipherWithIdExport } from "@bitwarden/common/models/export/cipher-with-ids.export";
|
||||||
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
import { Utils } from "@bitwarden/common/platform/misc/utils";
|
||||||
import { CipherId, UserId } from "@bitwarden/common/types/guid";
|
import { CipherId, emptyGuid, UserId } from "@bitwarden/common/types/guid";
|
||||||
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
|
||||||
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction";
|
||||||
import { CipherType } from "@bitwarden/common/vault/enums";
|
import { CipherType } from "@bitwarden/common/vault/enums";
|
||||||
@@ -179,7 +179,7 @@ describe("VaultExportService", () => {
|
|||||||
let restrictedItemTypesService: Partial<RestrictedItemTypesService>;
|
let restrictedItemTypesService: Partial<RestrictedItemTypesService>;
|
||||||
let fetchMock: jest.Mock;
|
let fetchMock: jest.Mock;
|
||||||
|
|
||||||
const userId = "" as UserId;
|
const userId = emptyGuid as UserId;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
cryptoFunctionService = mock<CryptoFunctionService>();
|
cryptoFunctionService = mock<CryptoFunctionService>();
|
||||||
|
|||||||
@@ -201,6 +201,10 @@ export class IndividualVaultExportService
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async getEncryptedExport(activeUserId: UserId): Promise<ExportedVaultAsString> {
|
private async getEncryptedExport(activeUserId: UserId): Promise<ExportedVaultAsString> {
|
||||||
|
if (!activeUserId) {
|
||||||
|
throw new Error("User ID must not be null or undefined");
|
||||||
|
}
|
||||||
|
|
||||||
let folders: Folder[] = [];
|
let folders: Folder[] = [];
|
||||||
let ciphers: Cipher[] = [];
|
let ciphers: Cipher[] = [];
|
||||||
const promises = [];
|
const promises = [];
|
||||||
@@ -225,7 +229,7 @@ export class IndividualVaultExportService
|
|||||||
|
|
||||||
await Promise.all(promises);
|
await Promise.all(promises);
|
||||||
|
|
||||||
const userKey = await this.keyService.getUserKey(activeUserId);
|
const userKey = await firstValueFrom(this.keyService.userKey$(activeUserId));
|
||||||
const encKeyValidation = await this.encryptService.encryptString(Utils.newGuid(), userKey);
|
const encKeyValidation = await this.encryptService.encryptString(Utils.newGuid(), userKey);
|
||||||
|
|
||||||
const jsonDoc: BitwardenEncryptedIndividualJsonExport = {
|
const jsonDoc: BitwardenEncryptedIndividualJsonExport = {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
|
||||||
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
|
||||||
import { UserId } from "@bitwarden/common/types/guid";
|
import { UserId } from "@bitwarden/common/types/guid";
|
||||||
@@ -15,7 +17,11 @@ export class LegacyPasswordHistoryDecryptor {
|
|||||||
|
|
||||||
/** Decrypts a password history. */
|
/** Decrypts a password history. */
|
||||||
async decrypt(history: GeneratedPasswordHistory[]): Promise<GeneratedPasswordHistory[]> {
|
async decrypt(history: GeneratedPasswordHistory[]): Promise<GeneratedPasswordHistory[]> {
|
||||||
const key = await this.keyService.getUserKey(this.userId);
|
const key = await firstValueFrom(this.keyService.userKey$(this.userId));
|
||||||
|
|
||||||
|
if (key == undefined) {
|
||||||
|
throw new Error("No user key found for decryption");
|
||||||
|
}
|
||||||
|
|
||||||
const promises = (history ?? []).map(async (item) => {
|
const promises = (history ?? []).map(async (item) => {
|
||||||
const encrypted = new EncString(item.password);
|
const encrypted = new EncString(item.password);
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
// FIXME: Update this file to be type safe and remove this and next line
|
// FIXME: Update this file to be type safe and remove this and next line
|
||||||
// @ts-strict-ignore
|
// @ts-strict-ignore
|
||||||
import { inject, Injectable } from "@angular/core";
|
import { inject, Injectable } from "@angular/core";
|
||||||
|
import { firstValueFrom } from "rxjs";
|
||||||
|
|
||||||
|
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||||
|
import { getUserId } from "@bitwarden/common/auth/services/account.service";
|
||||||
import { Send } from "@bitwarden/common/tools/send/models/domain/send";
|
import { Send } from "@bitwarden/common/tools/send/models/domain/send";
|
||||||
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
|
import { SendView } from "@bitwarden/common/tools/send/models/view/send.view";
|
||||||
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
import { SendApiService } from "@bitwarden/common/tools/send/services/send-api.service.abstraction";
|
||||||
@@ -12,11 +15,13 @@ import { SendFormService } from "../abstractions/send-form.service";
|
|||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class DefaultSendFormService implements SendFormService {
|
export class DefaultSendFormService implements SendFormService {
|
||||||
|
private accountService = inject(AccountService);
|
||||||
private sendApiService: SendApiService = inject(SendApiService);
|
private sendApiService: SendApiService = inject(SendApiService);
|
||||||
private sendService = inject(SendService);
|
private sendService = inject(SendService);
|
||||||
|
|
||||||
async decryptSend(send: Send): Promise<SendView> {
|
async decryptSend(send: Send): Promise<SendView> {
|
||||||
return await send.decrypt();
|
const userId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
|
||||||
|
return await send.decrypt(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async saveSend(send: SendView, file: File | ArrayBuffer, config: SendFormConfig) {
|
async saveSend(send: SendView, file: File | ArrayBuffer, config: SendFormConfig) {
|
||||||
|
|||||||
Reference in New Issue
Block a user