1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 15:53:27 +00:00

[PM-12047] Remove usage of ActiveUserState from cipher.service (#12814)

* Cipher service web changes

* Updated browser client to pass user id to cipher service observable changes

* Cli changes

* desktop changes

* Fixed test

* Libs changes

* Fixed merge conflicts

* Fixed merge conflicts

* removed duplicate reference fixed conflict

* Fixed test

* Fixed test

* Fixed test

* Fixed desturcturing issue on failed to decrypt ciphers cipher service

* Updated abstraction to use method syntax

* Fixed conflicts

* Fixed test on add edit v2

Passed active userId to delete function

* Used getUserId utility function

* made vault changes

* made suggestion changes

* made suggestion changes

* made suggestion changes

* Replace getUserId function calls with pipe operator syntax for better consistency

* fixed merge conflicts

* revert mistake made of usinf account activity during merge conflict fix

* fixed conflicts

* fixed tests
This commit is contained in:
SmithThe4th
2025-02-12 08:53:31 -05:00
committed by GitHub
parent e45ef6b924
commit a2945203f4
98 changed files with 1174 additions and 725 deletions

View File

@@ -1,8 +1,9 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { firstValueFrom, map } 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 { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { Response } from "../../models/response";
@@ -48,7 +49,9 @@ export class ShareCommand {
organizationId = organizationId.toLowerCase();
}
const cipher = await this.cipherService.get(id);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const cipher = await this.cipherService.get(id, activeUserId);
if (cipher == null) {
return Response.notFound();
}
@@ -56,15 +59,12 @@ export class ShareCommand {
return Response.badRequest("This item already belongs to an organization.");
}
const activeUserId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
const cipherView = await cipher.decrypt(
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
);
try {
await this.cipherService.shareWithServer(cipherView, organizationId, req, activeUserId);
const updatedCipher = await this.cipherService.get(cipher.id);
const updatedCipher = await this.cipherService.get(cipher.id, activeUserId);
const decCipher = await updatedCipher.decrypt(
await this.cipherService.getKeyForCipherKeyDecryption(updatedCipher, activeUserId),
);

View File

@@ -1,11 +1,12 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { firstValueFrom, map } from "rxjs";
import { firstValueFrom } from "rxjs";
import { CollectionRequest } from "@bitwarden/admin-console/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { CipherExport } from "@bitwarden/common/models/export/cipher.export";
import { CollectionExport } from "@bitwarden/common/models/export/collection.export";
@@ -25,8 +26,6 @@ import { CipherResponse } from "../vault/models/cipher.response";
import { FolderResponse } from "../vault/models/folder.response";
export class EditCommand {
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
constructor(
private cipherService: CipherService,
private folderService: FolderService,
@@ -85,14 +84,12 @@ export class EditCommand {
}
private async editCipher(id: string, req: CipherExport) {
const cipher = await this.cipherService.get(id);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const cipher = await this.cipherService.get(id, activeUserId);
if (cipher == null) {
return Response.notFound();
}
const activeUserId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
let cipherView = await cipher.decrypt(
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
);
@@ -114,7 +111,9 @@ export class EditCommand {
}
private async editCipherCollections(id: string, req: string[]) {
const cipher = await this.cipherService.get(id);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const cipher = await this.cipherService.get(id, activeUserId);
if (cipher == null) {
return Response.notFound();
}
@@ -129,11 +128,14 @@ export class EditCommand {
cipher.collectionIds = req;
try {
const updatedCipher = await this.cipherService.saveCollectionsWithServer(cipher);
const updatedCipher = await this.cipherService.saveCollectionsWithServer(
cipher,
activeUserId,
);
const decCipher = await updatedCipher.decrypt(
await this.cipherService.getKeyForCipherKeyDecryption(
updatedCipher,
await firstValueFrom(this.activeUserId$),
await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId)),
),
);
const res = new CipherResponse(decCipher);
@@ -144,7 +146,7 @@ export class EditCommand {
}
private async editFolder(id: string, req: FolderExport) {
const activeUserId = await firstValueFrom(this.activeUserId$);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const folder = await this.folderService.getFromState(id, activeUserId);
if (folder == null) {
return Response.notFound();

View File

@@ -52,8 +52,6 @@ import { FolderResponse } from "../vault/models/folder.response";
import { DownloadCommand } from "./download.command";
export class GetCommand extends DownloadCommand {
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
constructor(
private cipherService: CipherService,
private folderService: FolderService,
@@ -114,16 +112,16 @@ export class GetCommand extends DownloadCommand {
private async getCipherView(id: string): Promise<CipherView | CipherView[]> {
let decCipher: CipherView = null;
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
if (Utils.isGuid(id)) {
const cipher = await this.cipherService.get(id);
const cipher = await this.cipherService.get(id, activeUserId);
if (cipher != null) {
const activeUserId = await firstValueFrom(this.activeUserId$);
decCipher = await cipher.decrypt(
await this.cipherService.getKeyForCipherKeyDecryption(cipher, activeUserId),
);
}
} else if (id.trim() !== "") {
let ciphers = await this.cipherService.getAllDecrypted();
let ciphers = await this.cipherService.getAllDecrypted(activeUserId);
ciphers = this.searchService.searchCiphersBasic(ciphers, id);
if (ciphers.length > 1) {
return ciphers;
@@ -265,8 +263,10 @@ export class GetCommand extends DownloadCommand {
const canAccessPremium = await firstValueFrom(
this.accountProfileService.hasPremiumFromAnySource$(account.id),
);
if (!canAccessPremium) {
const originalCipher = await this.cipherService.get(cipher.id);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const originalCipher = await this.cipherService.get(cipher.id, activeUserId);
if (
originalCipher == null ||
originalCipher.organizationId == null ||
@@ -352,7 +352,8 @@ export class GetCommand extends DownloadCommand {
this.accountProfileService.hasPremiumFromAnySource$(account.id),
);
if (!canAccessPremium) {
const originalCipher = await this.cipherService.get(cipher.id);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const originalCipher = await this.cipherService.get(cipher.id, activeUserId);
if (originalCipher == null || originalCipher.organizationId == null) {
return Response.error("Premium status is required to use this feature.");
}
@@ -384,7 +385,7 @@ export class GetCommand extends DownloadCommand {
private async getFolder(id: string) {
let decFolder: FolderView = null;
const activeUserId = await firstValueFrom(this.activeUserId$);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
if (Utils.isGuid(id)) {
const folder = await this.folderService.getFromState(id, activeUserId);
if (folder != null) {
@@ -561,7 +562,7 @@ export class GetCommand extends DownloadCommand {
private async getFingerprint(id: string) {
let fingerprint: string[] = null;
if (id === "me") {
const activeUserId = await firstValueFrom(this.activeUserId$);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const publicKey = await firstValueFrom(this.keyService.userPublicKey$(activeUserId));
fingerprint = await this.keyService.getFingerprint(activeUserId, publicKey);
} else if (Utils.isGuid(id)) {

View File

@@ -65,11 +65,14 @@ export class ListCommand {
private async listCiphers(options: Options) {
let ciphers: CipherView[];
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
options.trash = options.trash || false;
if (options.url != null && options.url.trim() !== "") {
ciphers = await this.cipherService.getAllDecryptedForUrl(options.url);
ciphers = await this.cipherService.getAllDecryptedForUrl(options.url, activeUserId);
} else {
ciphers = await this.cipherService.getAllDecrypted();
ciphers = await this.cipherService.getAllDecrypted(activeUserId);
}
if (
@@ -138,9 +141,8 @@ export class ListCommand {
}
private async listFolders(options: Options) {
const activeUserId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
let folders = await this.folderService.getAllDecryptedFromState(activeUserId);
if (options.search != null && options.search.trim() !== "") {

View File

@@ -1,9 +1,16 @@
import { firstValueFrom } from "rxjs";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
import { Response } from "../models/response";
export class RestoreCommand {
constructor(private cipherService: CipherService) {}
constructor(
private cipherService: CipherService,
private accountService: AccountService,
) {}
async run(object: string, id: string): Promise<Response> {
if (id != null) {
@@ -19,7 +26,9 @@ export class RestoreCommand {
}
private async restoreCipher(id: string) {
const cipher = await this.cipherService.get(id);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const cipher = await this.cipherService.get(id, activeUserId);
if (cipher == null) {
return Response.notFound();
}
@@ -28,7 +37,7 @@ export class RestoreCommand {
}
try {
await this.cipherService.restoreWithServer(id);
await this.cipherService.restoreWithServer(id, activeUserId);
return Response.success();
} catch (e) {
return Response.error(e);

View File

@@ -124,7 +124,10 @@ export class OssServeConfigurator {
this.serviceContainer.encryptService,
this.serviceContainer.organizationUserApiService,
);
this.restoreCommand = new RestoreCommand(this.serviceContainer.cipherService);
this.restoreCommand = new RestoreCommand(
this.serviceContainer.cipherService,
this.serviceContainer.accountService,
);
this.shareCommand = new ShareCommand(
this.serviceContainer.cipherService,
this.serviceContainer.accountService,

View File

@@ -347,7 +347,10 @@ export class VaultProgram extends BaseProgram {
}
await this.exitIfLocked();
const command = new RestoreCommand(this.serviceContainer.cipherService);
const command = new RestoreCommand(
this.serviceContainer.cipherService,
this.serviceContainer.accountService,
);
const response = await command.run(object, id);
this.processResponse(response);
});

View File

@@ -10,6 +10,7 @@ import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { SelectionReadOnlyRequest } from "@bitwarden/common/admin-console/models/request/selection-read-only.request";
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 { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service";
import { CipherExport } from "@bitwarden/common/models/export/cipher.export";
@@ -30,8 +31,6 @@ import { CipherResponse } from "./models/cipher.response";
import { FolderResponse } from "./models/folder.response";
export class CreateCommand {
private activeUserId$ = this.accountService.activeAccount$.pipe(map((a) => a?.id));
constructor(
private cipherService: CipherService,
private folderService: FolderService,
@@ -90,7 +89,7 @@ export class CreateCommand {
}
private async createCipher(req: CipherExport) {
const activeUserId = await firstValueFrom(this.activeUserId$);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const cipher = await this.cipherService.encrypt(CipherExport.toView(req), activeUserId);
try {
const newCipher = await this.cipherService.createWithServer(cipher);
@@ -132,14 +131,14 @@ export class CreateCommand {
return Response.badRequest("File name not provided.");
}
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const itemId = options.itemId.toLowerCase();
const cipher = await this.cipherService.get(itemId);
const cipher = await this.cipherService.get(itemId, activeUserId);
if (cipher == null) {
return Response.notFound();
}
const activeUserId = await firstValueFrom(this.activeUserId$);
const canAccessPremium = await firstValueFrom(
this.accountProfileService.hasPremiumFromAnySource$(activeUserId),
);
@@ -173,7 +172,7 @@ export class CreateCommand {
}
private async createFolder(req: FolderExport) {
const activeUserId = await firstValueFrom(this.activeUserId$);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const userKey = await this.keyService.getUserKeyWithLegacySupport(activeUserId);
const folder = await this.folderService.encrypt(FolderExport.toView(req), userKey);
try {

View File

@@ -1,7 +1,8 @@
import { firstValueFrom, map } from "rxjs";
import { firstValueFrom } from "rxjs";
import { ApiService } from "@bitwarden/common/abstractions/api.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 { Utils } from "@bitwarden/common/platform/misc/utils";
import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service";
@@ -44,7 +45,9 @@ export class DeleteCommand {
}
private async deleteCipher(id: string, options: Options) {
const cipher = await this.cipherService.get(id);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const cipher = await this.cipherService.get(id, activeUserId);
if (cipher == null) {
return Response.notFound();
}
@@ -59,9 +62,9 @@ export class DeleteCommand {
try {
if (options.permanent) {
await this.cipherService.deleteWithServer(id);
await this.cipherService.deleteWithServer(id, activeUserId);
} else {
await this.cipherService.softDeleteWithServer(id);
await this.cipherService.softDeleteWithServer(id, activeUserId);
}
return Response.success();
} catch (e) {
@@ -74,8 +77,10 @@ export class DeleteCommand {
return Response.badRequest("`itemid` option is required.");
}
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const itemId = options.itemId.toLowerCase();
const cipher = await this.cipherService.get(itemId);
const cipher = await this.cipherService.get(itemId, activeUserId);
if (cipher == null) {
return Response.notFound();
}
@@ -89,16 +94,19 @@ export class DeleteCommand {
return Response.error("Attachment `" + id + "` was not found.");
}
const account = await firstValueFrom(this.accountService.activeAccount$);
const canAccessPremium = await firstValueFrom(
this.accountProfileService.hasPremiumFromAnySource$(account.id),
this.accountProfileService.hasPremiumFromAnySource$(activeUserId),
);
if (cipher.organizationId == null && !canAccessPremium) {
return Response.error("Premium status is required to use this feature.");
}
try {
await this.cipherService.deleteAttachmentWithServer(cipher.id, attachments[0].id);
await this.cipherService.deleteAttachmentWithServer(
cipher.id,
attachments[0].id,
activeUserId,
);
return Response.success();
} catch (e) {
return Response.error(e);
@@ -106,9 +114,7 @@ export class DeleteCommand {
}
private async deleteFolder(id: string) {
const activeUserId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),
);
const activeUserId = await firstValueFrom(this.accountService.activeAccount$.pipe(getUserId));
const folder = await this.folderService.getFromState(id, activeUserId);
if (folder == null) {
return Response.notFound();