mirror of
https://github.com/bitwarden/browser
synced 2026-02-26 17:43:22 +00:00
updated changes to match SDK changes
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||
import { Cipher } from "@bitwarden/common/vault/models/domain/cipher";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { CipherListView } from "@bitwarden/sdk-internal";
|
||||
|
||||
/**
|
||||
* Result of decrypting all ciphers, containing both successes and failures.
|
||||
@@ -126,16 +128,17 @@ export abstract class CipherSdkService {
|
||||
abstract getAllDecrypted(userId: UserId): Promise<DecryptAllCiphersResult>;
|
||||
|
||||
/**
|
||||
* Fetches and decrypts all ciphers for an organization from the API using the SDK.
|
||||
* Fetches all ciphers for an organization from the API using the SDK.
|
||||
* Returns encrypted ciphers for on-demand decryption and lightweight list views for display.
|
||||
*
|
||||
* @param organizationId The organization ID to fetch ciphers for
|
||||
* @param userId The user ID to use for SDK client
|
||||
* @param includeMemberItems Whether to include member items
|
||||
* @returns A promise that resolves to the decrypted cipher views
|
||||
* @returns A promise that resolves to the encrypted ciphers and decrypted list views
|
||||
*/
|
||||
abstract getAllFromApiForOrganization(
|
||||
organizationId: string,
|
||||
userId: UserId,
|
||||
includeMemberItems: boolean,
|
||||
): Promise<CipherView[]>;
|
||||
): Promise<[Cipher[], CipherListView[]]>;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { UserId, CipherId, OrganizationId } from "@bitwarden/common/types/guid";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
|
||||
import { CipherType } from "../enums/cipher-type";
|
||||
import { Cipher } from "../models/domain/cipher";
|
||||
|
||||
import { DefaultCipherSdkService } from "./cipher-sdk.service";
|
||||
|
||||
@@ -34,7 +35,7 @@ describe("DefaultCipherSdkService", () => {
|
||||
soft_delete_many: jest.fn().mockResolvedValue(undefined),
|
||||
restore: jest.fn().mockResolvedValue(undefined),
|
||||
restore_many: jest.fn().mockResolvedValue(undefined),
|
||||
list_org_ciphers: jest.fn().mockResolvedValue({ successes: [], failures: [] }),
|
||||
list_org_ciphers: jest.fn().mockResolvedValue({ ciphers: [], listViews: [] }),
|
||||
};
|
||||
mockCiphersSdk = {
|
||||
create: jest.fn(),
|
||||
@@ -613,12 +614,40 @@ describe("DefaultCipherSdkService", () => {
|
||||
});
|
||||
|
||||
describe("getAllFromApiForOrganization()", () => {
|
||||
const mockSdkCipher: any = {
|
||||
id: cipherId,
|
||||
name: "2.encryptedName|iv|data",
|
||||
type: CipherType.Login,
|
||||
organizationId: orgId,
|
||||
folderId: null,
|
||||
favorite: false,
|
||||
edit: true,
|
||||
viewPassword: true,
|
||||
organizationUseTotp: false,
|
||||
revisionDate: new Date().toISOString(),
|
||||
creationDate: new Date().toISOString(),
|
||||
collectionIds: [],
|
||||
deletedDate: null,
|
||||
reprompt: 0,
|
||||
key: null,
|
||||
localData: null,
|
||||
attachments: null,
|
||||
fields: null,
|
||||
passwordHistory: null,
|
||||
notes: null,
|
||||
login: null,
|
||||
secureNote: null,
|
||||
card: null,
|
||||
identity: null,
|
||||
sshKey: null,
|
||||
permissions: null,
|
||||
};
|
||||
|
||||
it("should list organization ciphers using SDK admin API", async () => {
|
||||
const mockSdkCipherView = new CipherView().toSdkCipherView();
|
||||
mockSdkCipherView.name = "Org Cipher";
|
||||
const mockListView: any = { id: cipherId, name: "Org Cipher" };
|
||||
mockAdminSdk.list_org_ciphers.mockResolvedValue({
|
||||
successes: [mockSdkCipherView],
|
||||
failures: [],
|
||||
ciphers: [mockSdkCipher],
|
||||
listViews: [mockListView],
|
||||
});
|
||||
|
||||
const result = await cipherSdkService.getAllFromApiForOrganization(orgId, userId, false);
|
||||
@@ -627,14 +656,16 @@ describe("DefaultCipherSdkService", () => {
|
||||
expect(mockVaultSdk.ciphers).toHaveBeenCalled();
|
||||
expect(mockCiphersSdk.admin).toHaveBeenCalled();
|
||||
expect(mockAdminSdk.list_org_ciphers).toHaveBeenCalledWith(orgId, false);
|
||||
expect(result).toHaveLength(1);
|
||||
expect(result[0]).toBeInstanceOf(CipherView);
|
||||
const [ciphers, listViews] = result;
|
||||
expect(ciphers).toHaveLength(1);
|
||||
expect(ciphers[0]).toBeInstanceOf(Cipher);
|
||||
expect(listViews).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("should pass includeMemberItems parameter to SDK", async () => {
|
||||
mockAdminSdk.list_org_ciphers.mockResolvedValue({
|
||||
successes: [],
|
||||
failures: [],
|
||||
ciphers: [],
|
||||
listViews: [],
|
||||
});
|
||||
|
||||
await cipherSdkService.getAllFromApiForOrganization(orgId, userId, true);
|
||||
|
||||
@@ -5,7 +5,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
|
||||
import { SdkService, asUuid } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
|
||||
import { OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
|
||||
import { CipherView as SdkCipherView } from "@bitwarden/sdk-internal";
|
||||
import { CipherListView, CipherView as SdkCipherView } from "@bitwarden/sdk-internal";
|
||||
|
||||
import { CipherSdkService, DecryptAllCiphersResult } from "../abstractions/cipher-sdk.service";
|
||||
import { Cipher } from "../models/domain/cipher";
|
||||
@@ -299,7 +299,7 @@ export class DefaultCipherSdkService implements CipherSdkService {
|
||||
organizationId: string,
|
||||
userId: UserId,
|
||||
includeMemberItems: boolean,
|
||||
): Promise<CipherView[]> {
|
||||
): Promise<[Cipher[], CipherListView[]]> {
|
||||
return await firstValueFrom(
|
||||
this.sdkService.userClient$(userId).pipe(
|
||||
switchMap(async (sdk) => {
|
||||
@@ -308,15 +308,17 @@ export class DefaultCipherSdkService implements CipherSdkService {
|
||||
}
|
||||
using ref = sdk.take();
|
||||
|
||||
const decryptResult = await ref.value
|
||||
const result = await ref.value
|
||||
.vault()
|
||||
.ciphers()
|
||||
.admin()
|
||||
.list_org_ciphers(asUuid(organizationId), includeMemberItems);
|
||||
|
||||
return decryptResult.successes
|
||||
.map((sdkCipherView: any) => CipherView.fromSdkCipherView(sdkCipherView))
|
||||
.filter((v): v is CipherView => v !== undefined);
|
||||
const ciphers = result.ciphers
|
||||
.map((c) => Cipher.fromSdkCipher(c))
|
||||
.filter((c): c is Cipher => c !== undefined);
|
||||
|
||||
return [ciphers, result.listViews] as [Cipher[], CipherListView[]];
|
||||
}),
|
||||
catchError((error: unknown) => {
|
||||
this.logService.error(`Failed to list organization ciphers: ${error}`);
|
||||
|
||||
@@ -1280,6 +1280,9 @@ describe("Cipher Service", () => {
|
||||
it("should use SDK to list organization ciphers when feature flag is enabled", async () => {
|
||||
sdkCrudFeatureFlag$.next(true);
|
||||
|
||||
const mockCipher1 = new Cipher(cipherData);
|
||||
const mockCipher2 = new Cipher(cipherData);
|
||||
|
||||
const mockCipherView1 = new CipherView();
|
||||
mockCipherView1.name = "Test Cipher 1";
|
||||
const mockCipherView2 = new CipherView();
|
||||
@@ -1287,7 +1290,12 @@ describe("Cipher Service", () => {
|
||||
|
||||
const sdkServiceSpy = jest
|
||||
.spyOn(cipherSdkService, "getAllFromApiForOrganization")
|
||||
.mockResolvedValue([mockCipherView1, mockCipherView2]);
|
||||
.mockResolvedValue([[mockCipher1, mockCipher2], []]);
|
||||
|
||||
cipherEncryptionService.decryptManyLegacy.mockResolvedValue([
|
||||
[mockCipherView1, mockCipherView2],
|
||||
[],
|
||||
]);
|
||||
|
||||
const apiSpy = jest.spyOn(apiService, "getCiphersOrganization");
|
||||
|
||||
@@ -1295,6 +1303,10 @@ describe("Cipher Service", () => {
|
||||
|
||||
expect(sdkServiceSpy).toHaveBeenCalledWith(testOrgId, mockUserId, true);
|
||||
expect(apiSpy).not.toHaveBeenCalled();
|
||||
expect(cipherEncryptionService.decryptManyLegacy).toHaveBeenCalledWith(
|
||||
[mockCipher1, mockCipher2],
|
||||
mockUserId,
|
||||
);
|
||||
expect(result).toHaveLength(2);
|
||||
expect(result[0]).toBeInstanceOf(CipherView);
|
||||
expect(result[1]).toBeInstanceOf(CipherView);
|
||||
@@ -1305,7 +1317,9 @@ describe("Cipher Service", () => {
|
||||
|
||||
const sdkServiceSpy = jest
|
||||
.spyOn(cipherSdkService, "getAllFromApiForOrganization")
|
||||
.mockResolvedValue([]);
|
||||
.mockResolvedValue([[], []]);
|
||||
|
||||
cipherEncryptionService.decryptManyLegacy.mockResolvedValue([[], []]);
|
||||
|
||||
const apiSpy = jest.spyOn(apiService, "getCiphersOrganization");
|
||||
|
||||
@@ -1323,9 +1337,7 @@ describe("Cipher Service", () => {
|
||||
});
|
||||
|
||||
it("should use SDK to list and decrypt ciphers when feature flag is enabled", async () => {
|
||||
configService.getFeatureFlag
|
||||
.calledWith(FeatureFlag.PM27632_SdkCipherCrudOperations)
|
||||
.mockResolvedValue(true);
|
||||
sdkCrudFeatureFlag$.next(true);
|
||||
|
||||
const mockCipherView1 = new CipherView();
|
||||
mockCipherView1.name = "Test Cipher 1";
|
||||
|
||||
@@ -788,12 +788,14 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
|
||||
try {
|
||||
const cipherViews = await this.cipherSdkService.getAllFromApiForOrganization(
|
||||
const [ciphers] = await this.cipherSdkService.getAllFromApiForOrganization(
|
||||
organizationId,
|
||||
userId,
|
||||
includeMemberItems,
|
||||
);
|
||||
|
||||
const [cipherViews] = await this.cipherEncryptionService.decryptManyLegacy(ciphers, userId);
|
||||
|
||||
// Sort by locale (matching existing behavior)
|
||||
cipherViews.sort(this.getLocaleSortingFunction());
|
||||
|
||||
@@ -975,7 +977,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
|
||||
const encrypted = await this.encrypt(cipherView, userId);
|
||||
const result = await this.createWithServer_legacy(encrypted, orgAdmin);
|
||||
const result = await this.createWithServerLegacy(encrypted, orgAdmin);
|
||||
return await this.decrypt(result, userId);
|
||||
}
|
||||
|
||||
@@ -993,7 +995,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
return resultCipherView;
|
||||
}
|
||||
|
||||
private async createWithServer_legacy(
|
||||
private async createWithServerLegacy(
|
||||
{ cipher, encryptedFor }: EncryptionContext,
|
||||
orgAdmin?: boolean,
|
||||
): Promise<Cipher> {
|
||||
@@ -1032,7 +1034,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
}
|
||||
|
||||
const encrypted = await this.encrypt(cipherView, userId);
|
||||
const updatedCipher = await this.updateWithServer_legacy(encrypted, orgAdmin);
|
||||
const updatedCipher = await this.updateWithServerLegacy(encrypted, orgAdmin);
|
||||
const updatedCipherView = await this.decrypt(updatedCipher, userId);
|
||||
return updatedCipherView;
|
||||
}
|
||||
@@ -1053,7 +1055,7 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
return resultCipherView;
|
||||
}
|
||||
|
||||
async updateWithServer_legacy(
|
||||
async updateWithServerLegacy(
|
||||
{ cipher, encryptedFor }: EncryptionContext,
|
||||
orgAdmin?: boolean,
|
||||
): Promise<Cipher> {
|
||||
|
||||
Reference in New Issue
Block a user