1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

add decryption logic (#17106)

This commit is contained in:
Brandon Treston
2025-10-29 15:04:37 -04:00
committed by GitHub
parent d85b9986d0
commit 75846e8fb1
2 changed files with 98 additions and 10 deletions

View File

@@ -1,13 +1,17 @@
import { TestBed } from "@angular/core/testing";
import { of } from "rxjs";
import {
CollectionService,
OrganizationUserApiService,
OrganizationUserUserDetailsResponse,
} from "@bitwarden/admin-console/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { ListResponse } from "@bitwarden/common/models/response/list.response";
import { OrganizationId } from "@bitwarden/common/types/guid";
import { KeyService } from "@bitwarden/key-management";
import { GroupApiService } from "../../../core";
@@ -18,6 +22,9 @@ describe("OrganizationMembersService", () => {
let organizationUserApiService: jest.Mocked<OrganizationUserApiService>;
let groupService: jest.Mocked<GroupApiService>;
let apiService: jest.Mocked<ApiService>;
let keyService: jest.Mocked<KeyService>;
let accountService: jest.Mocked<AccountService>;
let collectionService: jest.Mocked<CollectionService>;
const mockOrganizationId = "org-123" as OrganizationId;
@@ -51,6 +58,7 @@ describe("OrganizationMembersService", () => {
const createMockCollection = (id: string, name: string) => ({
id,
name,
organizationId: mockOrganizationId,
});
beforeEach(() => {
@@ -66,12 +74,27 @@ describe("OrganizationMembersService", () => {
getCollections: jest.fn(),
} as any;
keyService = {
orgKeys$: jest.fn(),
} as any;
accountService = {
activeAccount$: of({ id: "user-123" } as any),
} as any;
collectionService = {
decryptMany$: jest.fn(),
} as any;
TestBed.configureTestingModule({
providers: [
OrganizationMembersService,
{ provide: OrganizationUserApiService, useValue: organizationUserApiService },
{ provide: GroupApiService, useValue: groupService },
{ provide: ApiService, useValue: apiService },
{ provide: KeyService, useValue: keyService },
{ provide: AccountService, useValue: accountService },
{ provide: CollectionService, useValue: collectionService },
],
});
@@ -88,11 +111,15 @@ describe("OrganizationMembersService", () => {
data: [mockUser],
} as any;
const mockCollections = [createMockCollection("col-1", "Collection 1")];
const mockOrgKey = { [mockOrganizationId]: {} as any };
const mockDecryptedCollections = [{ id: "col-1", name: "Collection 1" }];
organizationUserApiService.getAllUsers.mockResolvedValue(mockUsersResponse);
apiService.getCollections.mockResolvedValue({
data: mockCollections,
} as any);
keyService.orgKeys$.mockReturnValue(of(mockOrgKey));
collectionService.decryptMany$.mockReturnValue(of(mockDecryptedCollections as any));
const result = await service.loadUsers(organization);
@@ -171,11 +198,19 @@ describe("OrganizationMembersService", () => {
createMockCollection("col-2", "Alpha Collection"),
createMockCollection("col-3", "Beta Collection"),
];
const mockOrgKey = { [mockOrganizationId]: {} as any };
const mockDecryptedCollections = [
{ id: "col-1", name: "Zebra Collection" },
{ id: "col-2", name: "Alpha Collection" },
{ id: "col-3", name: "Beta Collection" },
];
organizationUserApiService.getAllUsers.mockResolvedValue(mockUsersResponse);
apiService.getCollections.mockResolvedValue({
data: mockCollections,
} as any);
keyService.orgKeys$.mockReturnValue(of(mockOrgKey));
collectionService.decryptMany$.mockReturnValue(of(mockDecryptedCollections as any));
const result = await service.loadUsers(organization);
@@ -223,11 +258,19 @@ describe("OrganizationMembersService", () => {
// col-2 is missing - should be filtered out
createMockCollection("col-3", "Collection 3"),
];
const mockOrgKey = { [mockOrganizationId]: {} as any };
const mockDecryptedCollections = [
{ id: "col-1", name: "Collection 1" },
// col-2 is missing - should be filtered out
{ id: "col-3", name: "Collection 3" },
];
organizationUserApiService.getAllUsers.mockResolvedValue(mockUsersResponse);
apiService.getCollections.mockResolvedValue({
data: mockCollections,
} as any);
keyService.orgKeys$.mockReturnValue(of(mockOrgKey));
collectionService.decryptMany$.mockReturnValue(of(mockDecryptedCollections as any));
const result = await service.loadUsers(organization);
@@ -269,11 +312,14 @@ describe("OrganizationMembersService", () => {
const mockUsersResponse: ListResponse<OrganizationUserUserDetailsResponse> = {
data: null as any,
} as any;
const mockOrgKey = { [mockOrganizationId]: {} as any };
organizationUserApiService.getAllUsers.mockResolvedValue(mockUsersResponse);
apiService.getCollections.mockResolvedValue({
data: [],
} as any);
keyService.orgKeys$.mockReturnValue(of(mockOrgKey));
collectionService.decryptMany$.mockReturnValue(of([]));
const result = await service.loadUsers(organization);
@@ -285,11 +331,14 @@ describe("OrganizationMembersService", () => {
const mockUsersResponse: ListResponse<OrganizationUserUserDetailsResponse> = {
data: undefined as any,
} as any;
const mockOrgKey = { [mockOrganizationId]: {} as any };
organizationUserApiService.getAllUsers.mockResolvedValue(mockUsersResponse);
apiService.getCollections.mockResolvedValue({
data: [],
} as any);
keyService.orgKeys$.mockReturnValue(of(mockOrgKey));
collectionService.decryptMany$.mockReturnValue(of([]));
const result = await service.loadUsers(organization);
@@ -322,11 +371,14 @@ describe("OrganizationMembersService", () => {
const mockUsersResponse: ListResponse<OrganizationUserUserDetailsResponse> = {
data: [mockUser],
} as any;
const mockOrgKey = { [mockOrganizationId]: {} as any };
organizationUserApiService.getAllUsers.mockResolvedValue(mockUsersResponse);
apiService.getCollections.mockResolvedValue({
data: [],
} as any);
keyService.orgKeys$.mockReturnValue(of(mockOrgKey));
collectionService.decryptMany$.mockReturnValue(of([]));
const result = await service.loadUsers(organization);

View File

@@ -1,8 +1,18 @@
import { Injectable } from "@angular/core";
import { combineLatest, firstValueFrom, from, map, switchMap } from "rxjs";
import { OrganizationUserApiService } from "@bitwarden/admin-console/common";
import {
Collection,
CollectionData,
CollectionDetailsResponse,
CollectionService,
OrganizationUserApiService,
} from "@bitwarden/admin-console/common";
import { ApiService } from "@bitwarden/common/abstractions/api.service";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { getUserId } from "@bitwarden/common/auth/services/account.service";
import { KeyService } from "@bitwarden/key-management";
import { GroupApiService } from "../../../core";
import { OrganizationUserView } from "../../../core/views/organization-user.view";
@@ -13,6 +23,9 @@ export class OrganizationMembersService {
private organizationUserApiService: OrganizationUserApiService,
private groupService: GroupApiService,
private apiService: ApiService,
private keyService: KeyService,
private accountService: AccountService,
private collectionService: CollectionService,
) {}
async loadUsers(organization: Organization): Promise<OrganizationUserView[]> {
@@ -62,15 +75,38 @@ export class OrganizationMembersService {
}
private async getCollectionNameMap(organization: Organization): Promise<Map<string, string>> {
const response = this.apiService
.getCollections(organization.id)
.then((res) =>
res.data.map((r: { id: string; name: string }) => ({ id: r.id, name: r.name })),
);
const collections$ = from(this.apiService.getCollections(organization.id)).pipe(
map((response) => {
return response.data.map((r) =>
Collection.fromCollectionData(new CollectionData(r as CollectionDetailsResponse)),
);
}),
);
const collections = await response;
const collectionMap = new Map<string, string>();
collections.forEach((c: { id: string; name: string }) => collectionMap.set(c.id, c.name));
return collectionMap;
const orgKey$ = this.accountService.activeAccount$.pipe(
getUserId,
switchMap((userId) => this.keyService.orgKeys$(userId)),
map((orgKeys) => {
if (orgKeys == null) {
throw new Error("Organization keys not found for provided User.");
}
return orgKeys;
}),
);
return await firstValueFrom(
combineLatest([orgKey$, collections$]).pipe(
switchMap(([orgKey, collections]) =>
this.collectionService.decryptMany$(collections, orgKey),
),
map((decryptedCollections) => {
const collectionMap: Map<string, string> = new Map<string, string>();
decryptedCollections.forEach((c) => {
collectionMap.set(c.id, c.name);
});
return collectionMap;
}),
),
);
}
}