mirror of
https://github.com/bitwarden/browser
synced 2026-02-26 01:23:24 +00:00
Merge branch 'main' into km/replace-encstring-with-unsigned-shared-key
This commit is contained in:
@@ -9,6 +9,14 @@ import { CollectionData, Collection, CollectionView } from "../models";
|
||||
export abstract class CollectionService {
|
||||
abstract encryptedCollections$(userId: UserId): Observable<Collection[] | null>;
|
||||
abstract decryptedCollections$(userId: UserId): Observable<CollectionView[]>;
|
||||
|
||||
/**
|
||||
* Gets the default collection for a user in a given organization, if it exists.
|
||||
*/
|
||||
abstract defaultUserCollection$(
|
||||
userId: UserId,
|
||||
orgId: OrganizationId,
|
||||
): Observable<CollectionView | undefined>;
|
||||
abstract upsert(collection: CollectionData, userId: UserId): Promise<any>;
|
||||
abstract replace(collections: { [id: string]: CollectionData }, userId: UserId): Promise<any>;
|
||||
/**
|
||||
|
||||
@@ -15,9 +15,10 @@ import {
|
||||
} from "@bitwarden/common/spec";
|
||||
import { CollectionId, OrganizationId, UserId } from "@bitwarden/common/types/guid";
|
||||
import { OrgKey } from "@bitwarden/common/types/key";
|
||||
import { newGuid } from "@bitwarden/guid";
|
||||
import { KeyService } from "@bitwarden/key-management";
|
||||
|
||||
import { CollectionData, CollectionView } from "../models";
|
||||
import { CollectionData, CollectionTypes, CollectionView } from "../models";
|
||||
|
||||
import { DECRYPTED_COLLECTION_DATA_KEY, ENCRYPTED_COLLECTION_DATA_KEY } from "./collection.state";
|
||||
import { DefaultCollectionService } from "./default-collection.service";
|
||||
@@ -389,6 +390,83 @@ describe("DefaultCollectionService", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("defaultUserCollection$", () => {
|
||||
it("returns the default collection when one exists matching the org", async () => {
|
||||
const orgId = newGuid() as OrganizationId;
|
||||
const defaultCollection = collectionViewDataFactory(orgId);
|
||||
defaultCollection.type = CollectionTypes.DefaultUserCollection;
|
||||
|
||||
const regularCollection = collectionViewDataFactory(orgId);
|
||||
regularCollection.type = CollectionTypes.SharedCollection;
|
||||
|
||||
await setDecryptedState([defaultCollection, regularCollection]);
|
||||
|
||||
const result = await firstValueFrom(collectionService.defaultUserCollection$(userId, orgId));
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result?.id).toBe(defaultCollection.id);
|
||||
expect(result?.isDefaultCollection).toBe(true);
|
||||
});
|
||||
|
||||
it("returns undefined when no default collection exists", async () => {
|
||||
const orgId = newGuid() as OrganizationId;
|
||||
const collection1 = collectionViewDataFactory(orgId);
|
||||
collection1.type = CollectionTypes.SharedCollection;
|
||||
|
||||
const collection2 = collectionViewDataFactory(orgId);
|
||||
collection2.type = CollectionTypes.SharedCollection;
|
||||
|
||||
await setDecryptedState([collection1, collection2]);
|
||||
|
||||
const result = await firstValueFrom(collectionService.defaultUserCollection$(userId, orgId));
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns undefined when default collection exists but for different org", async () => {
|
||||
const orgA = newGuid() as OrganizationId;
|
||||
const orgB = newGuid() as OrganizationId;
|
||||
|
||||
const defaultCollectionForOrgA = collectionViewDataFactory(orgA);
|
||||
defaultCollectionForOrgA.type = CollectionTypes.DefaultUserCollection;
|
||||
|
||||
await setDecryptedState([defaultCollectionForOrgA]);
|
||||
|
||||
const result = await firstValueFrom(collectionService.defaultUserCollection$(userId, orgB));
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns undefined when collections array is empty", async () => {
|
||||
const orgId = newGuid() as OrganizationId;
|
||||
|
||||
await setDecryptedState([]);
|
||||
|
||||
const result = await firstValueFrom(collectionService.defaultUserCollection$(userId, orgId));
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns correct collection when multiple orgs have default collections", async () => {
|
||||
const orgA = newGuid() as OrganizationId;
|
||||
const orgB = newGuid() as OrganizationId;
|
||||
|
||||
const defaultCollectionForOrgA = collectionViewDataFactory(orgA);
|
||||
defaultCollectionForOrgA.type = CollectionTypes.DefaultUserCollection;
|
||||
|
||||
const defaultCollectionForOrgB = collectionViewDataFactory(orgB);
|
||||
defaultCollectionForOrgB.type = CollectionTypes.DefaultUserCollection;
|
||||
|
||||
await setDecryptedState([defaultCollectionForOrgA, defaultCollectionForOrgB]);
|
||||
|
||||
const result = await firstValueFrom(collectionService.defaultUserCollection$(userId, orgB));
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result?.id).toBe(defaultCollectionForOrgB.id);
|
||||
expect(result?.organizationId).toBe(orgB);
|
||||
});
|
||||
});
|
||||
|
||||
const setEncryptedState = (collectionData: CollectionData[] | null) =>
|
||||
stateProvider.setUserState(
|
||||
ENCRYPTED_COLLECTION_DATA_KEY,
|
||||
|
||||
@@ -87,6 +87,17 @@ export class DefaultCollectionService implements CollectionService {
|
||||
return result$;
|
||||
}
|
||||
|
||||
defaultUserCollection$(
|
||||
userId: UserId,
|
||||
orgId: OrganizationId,
|
||||
): Observable<CollectionView | undefined> {
|
||||
return this.decryptedCollections$(userId).pipe(
|
||||
map((collections) => {
|
||||
return collections.find((c) => c.isDefaultCollection && c.organizationId === orgId);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
private initializeDecryptedState(userId: UserId): Observable<CollectionView[]> {
|
||||
return combineLatest([
|
||||
this.encryptedCollections$(userId),
|
||||
|
||||
Reference in New Issue
Block a user