From c22451ed14c49d6cf6ef9127b08adc44b6b62964 Mon Sep 17 00:00:00 2001 From: Thomas Rittson Date: Sat, 19 Jul 2025 09:58:31 +1000 Subject: [PATCH] Update ciphers to use CollectionId and OrganizationId --- .../models/routed-vault-filter-bridge.model.ts | 2 +- .../shared/models/vault-filter.model.ts | 3 ++- libs/common/src/models/export/cipher.export.ts | 9 ++++++--- libs/common/src/vault/models/domain/cipher.ts | 7 ++++--- libs/common/src/vault/models/view/cipher.view.ts | 9 +++++---- libs/common/src/vault/services/cipher.service.ts | 12 ++++++------ .../src/multi-select/models/select-item-view.ts | 10 ++++++++++ .../components/assign-collections.component.ts | 16 ++++++++-------- 8 files changed, 42 insertions(+), 26 deletions(-) diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts index 93a7f81d08d..12d3a6e46b5 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter-bridge.model.ts @@ -145,7 +145,7 @@ export class RoutedVaultFilterBridge implements VaultFilter { get folderId(): string { return this.legacyFilter.folderId; } - get collectionId(): string { + get collectionId(): CollectionId { return this.legacyFilter.collectionId; } resetFilter(): void { diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts index 7f001f3aab2..027a76351ef 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/vault-filter.model.ts @@ -1,5 +1,6 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore +import { CollectionId } from "@bitwarden/common/types/guid"; import { CipherType, isCipherType } from "@bitwarden/common/vault/enums"; import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -94,7 +95,7 @@ export class VaultFilter { return this.selectedFolderNode?.node.id; } - get collectionId(): string { + get collectionId(): CollectionId { return this.selectedCollectionNode?.node.id; } diff --git a/libs/common/src/models/export/cipher.export.ts b/libs/common/src/models/export/cipher.export.ts index ad6d8b7a609..c6ad522cda8 100644 --- a/libs/common/src/models/export/cipher.export.ts +++ b/libs/common/src/models/export/cipher.export.ts @@ -1,6 +1,7 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { EncString } from "../../key-management/crypto/models/enc-string"; +import { CollectionId, OrganizationId } from "../../types/guid"; import { CipherRepromptType } from "../../vault/enums/cipher-reprompt-type"; import { CipherType } from "../../vault/enums/cipher-type"; import { Cipher as CipherDomain } from "../../vault/models/domain/cipher"; @@ -43,10 +44,12 @@ export class CipherExport { view.type = req.type; view.folderId = req.folderId; if (view.organizationId == null) { - view.organizationId = req.organizationId; + view.organizationId = req.organizationId as OrganizationId; } if (view.collectionIds || req.collectionIds) { - const set = new Set((view.collectionIds ?? []).concat(req.collectionIds ?? [])); + const set = new Set( + (view.collectionIds ?? []).concat((req.collectionIds as CollectionId[]) ?? []), + ); view.collectionIds = Array.from(set.values()); } view.name = req.name; @@ -90,7 +93,7 @@ export class CipherExport { domain.type = req.type; domain.folderId = req.folderId; if (domain.organizationId == null) { - domain.organizationId = req.organizationId; + domain.organizationId = req.organizationId as OrganizationId; } domain.name = req.name != null ? new EncString(req.name) : null; domain.notes = req.notes != null ? new EncString(req.notes) : null; diff --git a/libs/common/src/vault/models/domain/cipher.ts b/libs/common/src/vault/models/domain/cipher.ts index 2f64fb82726..80cc39bc9da 100644 --- a/libs/common/src/vault/models/domain/cipher.ts +++ b/libs/common/src/vault/models/domain/cipher.ts @@ -10,6 +10,7 @@ import { Utils } from "../../../platform/misc/utils"; import Domain from "../../../platform/models/domain/domain-base"; import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key"; import { InitializerKey } from "../../../platform/services/cryptography/initializer-key"; +import { CollectionId, OrganizationId } from "../../../types/guid"; import { CipherRepromptType } from "../../enums/cipher-reprompt-type"; import { CipherType } from "../../enums/cipher-type"; import { CipherPermissionsApi } from "../api/cipher-permissions.api"; @@ -33,7 +34,7 @@ export class Cipher extends Domain implements Decryptable { readonly initializerKey = InitializerKey.Cipher; id: string; - organizationId: string; + organizationId: OrganizationId; folderId: string; name: EncString; notes: EncString; @@ -53,7 +54,7 @@ export class Cipher extends Domain implements Decryptable { attachments: Attachment[]; fields: Field[]; passwordHistory: Password[]; - collectionIds: string[]; + collectionIds: CollectionId[]; creationDate: Date; deletedDate: Date; reprompt: CipherRepromptType; @@ -90,7 +91,7 @@ export class Cipher extends Domain implements Decryptable { } this.permissions = obj.permissions; this.revisionDate = obj.revisionDate != null ? new Date(obj.revisionDate) : null; - this.collectionIds = obj.collectionIds; + this.collectionIds = obj.collectionIds as CollectionId[]; this.localData = localData; this.creationDate = obj.creationDate != null ? new Date(obj.creationDate) : null; this.deletedDate = obj.deletedDate != null ? new Date(obj.deletedDate) : null; diff --git a/libs/common/src/vault/models/view/cipher.view.ts b/libs/common/src/vault/models/view/cipher.view.ts index 353fffa8eef..d16c6137757 100644 --- a/libs/common/src/vault/models/view/cipher.view.ts +++ b/libs/common/src/vault/models/view/cipher.view.ts @@ -6,6 +6,7 @@ import { View } from "../../../models/view/view"; import { InitializerMetadata } from "../../../platform/interfaces/initializer-metadata.interface"; import { InitializerKey } from "../../../platform/services/cryptography/initializer-key"; import { DeepJsonify } from "../../../types/deep-jsonify"; +import { CollectionId, OrganizationId } from "../../../types/guid"; import { CipherType, LinkedIdType } from "../../enums"; import { CipherRepromptType } from "../../enums/cipher-reprompt-type"; import { CipherPermissionsApi } from "../api/cipher-permissions.api"; @@ -25,7 +26,7 @@ export class CipherView implements View, InitializerMetadata { readonly initializerKey = InitializerKey.CipherView; id: string = null; - organizationId: string | undefined = null; + organizationId: OrganizationId | undefined = null; folderId: string = null; name: string = null; notes: string = null; @@ -44,7 +45,7 @@ export class CipherView implements View, InitializerMetadata { attachments: AttachmentView[] = null; fields: FieldView[] = null; passwordHistory: PasswordHistoryView[] = null; - collectionIds: string[] = null; + collectionIds: CollectionId[] = null; revisionDate: Date = null; creationDate: Date = null; deletedDate: Date = null; @@ -237,7 +238,7 @@ export class CipherView implements View, InitializerMetadata { const cipherView = new CipherView(); cipherView.id = obj.id ?? null; - cipherView.organizationId = obj.organizationId ?? null; + cipherView.organizationId = (obj.organizationId as OrganizationId) ?? null; cipherView.folderId = obj.folderId ?? null; cipherView.name = obj.name; cipherView.notes = obj.notes ?? null; @@ -262,7 +263,7 @@ export class CipherView implements View, InitializerMetadata { cipherView.fields = obj.fields?.map((f) => FieldView.fromSdkFieldView(f)) ?? null; cipherView.passwordHistory = obj.passwordHistory?.map((ph) => PasswordHistoryView.fromSdkPasswordHistoryView(ph)) ?? null; - cipherView.collectionIds = obj.collectionIds ?? null; + cipherView.collectionIds = (obj.collectionIds as CollectionId[]) ?? null; cipherView.revisionDate = obj.revisionDate == null ? null : new Date(obj.revisionDate); cipherView.creationDate = obj.creationDate == null ? null : new Date(obj.creationDate); cipherView.deletedDate = obj.deletedDate == null ? null : new Date(obj.deletedDate); diff --git a/libs/common/src/vault/services/cipher.service.ts b/libs/common/src/vault/services/cipher.service.ts index 8bef5289a95..81b327b51ad 100644 --- a/libs/common/src/vault/services/cipher.service.ts +++ b/libs/common/src/vault/services/cipher.service.ts @@ -546,7 +546,7 @@ export class CipherService implements CipherServiceAbstraction { } async getAllDecryptedForGrouping( - groupingId: string, + groupingId: string | CollectionId, // may be a FolderId or a CollectionId userId: UserId, folder = true, ): Promise { @@ -561,7 +561,7 @@ export class CipherService implements CipherServiceAbstraction { } else if ( !folder && cipher.collectionIds != null && - cipher.collectionIds.indexOf(groupingId) > -1 + cipher.collectionIds.indexOf(groupingId as CollectionId) > -1 ) { return true; } @@ -851,8 +851,8 @@ export class CipherService implements CipherServiceAbstraction { async shareWithServer( cipher: CipherView, - organizationId: string, - collectionIds: string[], + organizationId: OrganizationId, + collectionIds: CollectionId[], userId: UserId, ): Promise { const attachmentPromises: Promise[] = []; @@ -879,8 +879,8 @@ export class CipherService implements CipherServiceAbstraction { async shareManyWithServer( ciphers: CipherView[], - organizationId: string, - collectionIds: string[], + organizationId: OrganizationId, + collectionIds: CollectionId[], userId: UserId, ) { const promises: Promise[] = []; diff --git a/libs/components/src/multi-select/models/select-item-view.ts b/libs/components/src/multi-select/models/select-item-view.ts index f66680c4406..4e2581e69ab 100644 --- a/libs/components/src/multi-select/models/select-item-view.ts +++ b/libs/components/src/multi-select/models/select-item-view.ts @@ -1,3 +1,5 @@ +import { CollectionId } from "@bitwarden/common/types/guid"; + export type SelectItemView = { id: string; // Unique ID used for comparisons listName: string; // Default bindValue -> this is what will be displayed in list items @@ -5,3 +7,11 @@ export type SelectItemView = { icon?: string; // Icon to display within the list parentGrouping?: string; // Used to group items by parent }; + +export type SelectCollectionView = { + id: CollectionId; // Unique ID used for comparisons + listName: string; // Default bindValue -> this is what will be displayed in list items + labelName: string; // This is what will be displayed in the selection option badge + icon?: string; // Icon to display within the list + parentGrouping?: string; // Used to group items by parent +}; diff --git a/libs/vault/src/components/assign-collections.component.ts b/libs/vault/src/components/assign-collections.component.ts index 124dc783034..52da043ed88 100644 --- a/libs/vault/src/components/assign-collections.component.ts +++ b/libs/vault/src/components/assign-collections.component.ts @@ -49,7 +49,7 @@ import { DialogModule, FormFieldModule, MultiSelectModule, - SelectItemView, + SelectCollectionView, SelectModule, ToastService, } from "@bitwarden/components"; @@ -125,7 +125,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI formGroup = this.formBuilder.group({ selectedOrg: [null], - collections: [[], [Validators.required]], + collections: [[], [Validators.required]], }); /** @@ -138,7 +138,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI protected editableItemCount: number; protected readonlyItemCount: number; protected personalItemsCount: number; - protected availableCollections: SelectItemView[] = []; + protected availableCollections: SelectCollectionView[] = []; protected orgName: string; protected showOrgSelector: boolean = false; @@ -240,8 +240,8 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI this.destroy$.complete(); } - selectCollections(items: SelectItemView[]) { - const currentCollections = this.formGroup.controls.collections.value as SelectItemView[]; + selectCollections(items: SelectCollectionView[]) { + const currentCollections = this.formGroup.controls.collections.value as SelectCollectionView[]; const updatedCollections = [...currentCollections, ...items].sort(this.sortItems); this.formGroup.patchValue({ collections: updatedCollections }); } @@ -288,7 +288,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI this.onCollectionAssign.emit(CollectionAssignmentResult.Saved); }; - private sortItems = (a: SelectItemView, b: SelectItemView) => + private sortItems = (a: SelectCollectionView, b: SelectCollectionView) => this.i18nService.collator.compare(a.labelName, b.labelName); private async handleOrganizationCiphers(organizationId: OrganizationId) { @@ -415,7 +415,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI this.formGroup.controls.collections.setValue([], { emitEvent: false }); }), switchMap((orgId) => { - return this.getCollectionsForOrganization(orgId as OrganizationId); + return this.getCollectionsForOrganization(orgId); }), takeUntil(this.destroy$), ) @@ -482,7 +482,7 @@ export class AssignCollectionsComponent implements OnInit, OnDestroy, AfterViewI this.selectedOrgId, userId, cipherIds, - this.formGroup.controls.collections.value.map((i) => i.id as CollectionId), + this.formGroup.controls.collections.value.map((i) => i.id), false, ); }