1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-13 23:13:36 +00:00

Update Collection to use strongly typed guids

This commit is contained in:
Thomas Rittson
2025-07-19 09:44:44 +10:00
parent 0ff3f1c26c
commit 13f84b08ed
11 changed files with 49 additions and 25 deletions

View File

@@ -39,6 +39,7 @@ import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
import {
DIALOG_DATA,
DialogConfig,
@@ -87,8 +88,8 @@ enum ButtonType {
}
export interface CollectionDialogParams {
collectionId?: string;
organizationId: string;
collectionId?: CollectionId;
organizationId: OrganizationId;
initialTab?: CollectionDialogTabType;
parentCollectionId?: string;
showOrgSelector?: boolean;
@@ -136,7 +137,7 @@ export class CollectionDialogComponent implements OnInit, OnDestroy {
externalId: { value: "", disabled: true },
parent: undefined as string | undefined,
access: [[] as AccessItemValue[]],
selectedOrg: "",
selectedOrg: "" as OrganizationId,
});
protected PermissionMode = PermissionMode;
protected showDeleteButton = false;

View File

@@ -2,6 +2,8 @@ import { Injectable, OnDestroy } from "@angular/core";
import { ActivatedRoute, NavigationExtras } from "@angular/router";
import { combineLatest, map, Observable, Subject, takeUntil } from "rxjs";
import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
import {
isRoutedVaultFilterItemType,
RoutedVaultFilterModel,
@@ -31,10 +33,11 @@ export class RoutedVaultFilterService implements OnDestroy {
const type = isRoutedVaultFilterItemType(unsafeType) ? unsafeType : undefined;
return {
collectionId: queryParams.get("collectionId") ?? undefined,
collectionId: (queryParams.get("collectionId") ?? undefined) as CollectionId,
folderId: queryParams.get("folderId") ?? undefined,
organizationId:
params.get("organizationId") ?? queryParams.get("organizationId") ?? undefined,
organizationId: (params.get("organizationId") ??
queryParams.get("organizationId") ??
undefined) as OrganizationId,
organizationIdParamType:
params.get("organizationId") != undefined ? ("path" as const) : ("query" as const),
type,

View File

@@ -1,4 +1,5 @@
import { Unassigned } from "@bitwarden/admin-console/common";
import { CollectionId } from "@bitwarden/common/types/guid";
import { CipherType } from "@bitwarden/common/vault/enums";
import { TreeNode } from "@bitwarden/common/vault/models/domain/tree-node";
@@ -65,7 +66,7 @@ export class RoutedVaultFilterBridge implements VaultFilter {
let type: RoutedVaultFilterItemType | undefined;
if (value?.node.id === "AllItems" && this.routedFilter.organizationIdParamType === "path") {
type = "all";
type = All;
} else if (
value?.node.id === "AllItems" &&
this.routedFilter.organizationIdParamType === "query"
@@ -98,7 +99,7 @@ export class RoutedVaultFilterBridge implements VaultFilter {
return this.legacyFilter.selectedCollectionNode;
}
set selectedCollectionNode(value: TreeNode<CollectionFilter>) {
let collectionId: string | undefined;
let collectionId: CollectionId | undefined;
if (value != null && value.node.id === null) {
collectionId = Unassigned;

View File

@@ -1,4 +1,9 @@
export const All = "all";
import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
/**
* Represents viewing all collections for an organization
*/
export const All = "all" as CollectionId;
// TODO: Remove `All` when moving to vertical navigation.
const itemTypes = [
@@ -19,9 +24,9 @@ export function isRoutedVaultFilterItemType(value: unknown): value is RoutedVaul
}
export interface RoutedVaultFilterModel {
collectionId?: string;
collectionId?: CollectionId;
folderId?: string;
organizationId?: string;
organizationId?: OrganizationId;
type?: RoutedVaultFilterItemType;
organizationIdParamType?: "path" | "query";

View File

@@ -1,10 +1,14 @@
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
import { CollectionAccessSelectionView } from "./collection-access-selection.view";
import { CollectionAccessDetailsResponse } from "./collection.response";
import { CollectionView } from "./collection.view";
export const Unassigned = "unassigned";
// TODO: this is used to represent the pseudo "Unassigned" collection
// AND ALSO the user's personal vault - this should be separated out to different consts.
// The type union allows this to be glossed over for now.
export const Unassigned = "unassigned" as CollectionId & OrganizationId;
export class CollectionAdminView extends CollectionView {
groups: CollectionAccessSelectionView[] = [];

View File

@@ -2,6 +2,7 @@
// @ts-strict-ignore
import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string";
import Domain from "@bitwarden/common/platform/models/domain/domain-base";
import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
import { OrgKey } from "@bitwarden/common/types/key";
import { CollectionData } from "./collection.data";
@@ -15,8 +16,8 @@ export const CollectionTypes = {
export type CollectionType = (typeof CollectionTypes)[keyof typeof CollectionTypes];
export class Collection extends Domain {
id: string;
organizationId: string;
id: CollectionId;
organizationId: OrganizationId;
name: EncString;
externalId: string;
readOnly: boolean;

View File

@@ -2,6 +2,7 @@ import { Jsonify } from "type-fest";
import { Organization } from "@bitwarden/common/admin-console/models/domain/organization";
import { View } from "@bitwarden/common/models/view/view";
import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
import { ITreeNodeObject } from "@bitwarden/common/vault/models/domain/tree-node";
import { Collection, CollectionType, CollectionTypes } from "./collection";
@@ -10,8 +11,8 @@ import { CollectionAccessDetailsResponse } from "./collection.response";
export const NestingDelimiter = "/";
export class CollectionView implements View, ITreeNodeObject {
id: string | undefined;
organizationId: string | undefined;
id: CollectionId | undefined;
organizationId: OrganizationId | undefined;
name: string | undefined;
externalId: string | undefined;
// readOnly applies to the items within a collection

View File

@@ -4,18 +4,20 @@
// eslint-disable-next-line no-restricted-imports
import { Collection as CollectionDomain, CollectionView } from "@bitwarden/admin-console/common";
import { CollectionId } from "../../types/guid";
import { CollectionExport } from "./collection.export";
export class CollectionWithIdExport extends CollectionExport {
id: string;
static toView(req: CollectionWithIdExport, view = new CollectionView()) {
view.id = req.id;
view.id = req.id as CollectionId;
return super.toView(req, view);
}
static toDomain(req: CollectionWithIdExport, domain = new CollectionDomain()) {
domain.id = req.id;
domain.id = req.id as CollectionId;
return super.toDomain(req, domain);
}

View File

@@ -5,6 +5,7 @@
import { Collection as CollectionDomain, CollectionView } from "@bitwarden/admin-console/common";
import { EncString } from "../../key-management/crypto/models/enc-string";
import { OrganizationId } from "../../types/guid";
import { safeGetString } from "./utils";
@@ -21,7 +22,7 @@ export class CollectionExport {
view.name = req.name;
view.externalId = req.externalId;
if (view.organizationId == null) {
view.organizationId = req.organizationId;
view.organizationId = req.organizationId as OrganizationId;
}
return view;
}
@@ -30,7 +31,7 @@ export class CollectionExport {
domain.name = req.name != null ? new EncString(req.name) : null;
domain.externalId = req.externalId;
if (domain.organizationId == null) {
domain.organizationId = req.organizationId;
domain.organizationId = req.organizationId as OrganizationId;
}
return domain;
}

View File

@@ -9,6 +9,7 @@ import { normalizeExpiryYearFormat } from "@bitwarden/common/autofill/utils";
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid";
import { FieldType, SecureNoteType, CipherType } from "@bitwarden/common/vault/enums";
import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view";
import { FieldView } from "@bitwarden/common/vault/models/view/field.view";
@@ -20,7 +21,7 @@ import { SecureNoteView } from "@bitwarden/common/vault/models/view/secure-note.
import { ImportResult } from "../models/import-result";
export abstract class BaseImporter {
organizationId: string = null;
organizationId: OrganizationId = null;
// FIXME: This should be replaced by injecting the log service.
protected logService: LogService = new ConsoleLogService(false);
@@ -279,7 +280,7 @@ export abstract class BaseImporter {
result.collections = result.folders.map((f) => {
const collection = new CollectionView();
collection.name = f.name;
collection.id = f.id ?? undefined; // folder id may be null, which is not suitable for collections.
collection.id = (f.id as CollectionId) ?? undefined; // folder id may be null, which is not suitable for collections.
return collection;
});
result.folderRelationships = [];

View File

@@ -102,6 +102,7 @@ import {
import { ImportResult } from "../models/import-result";
import { ImportApiServiceAbstraction } from "../services/import-api.service.abstraction";
import { ImportServiceAbstraction } from "../services/import.service.abstraction";
import { OrganizationId } from "@bitwarden/common/types/guid";
export class ImportService implements ImportServiceAbstraction {
featuredImportOptions = featuredImportOptions as readonly ImportOption[];
@@ -128,7 +129,7 @@ export class ImportService implements ImportServiceAbstraction {
async import(
importer: Importer,
fileContents: string,
organizationId: string = null,
organizationId: OrganizationId = null,
selectedImportTarget: FolderView | CollectionView = null,
canAccessImportExport: boolean,
): Promise<ImportResult> {
@@ -191,7 +192,7 @@ export class ImportService implements ImportServiceAbstraction {
getImporter(
format: ImportType | "bitwardenpasswordprotected",
promptForPassword_callback: () => Promise<string>,
organizationId: string = null,
organizationId: OrganizationId = null,
): Importer {
if (promptForPassword_callback == null) {
return null;
@@ -380,7 +381,10 @@ export class ImportService implements ImportServiceAbstraction {
return await this.importApiService.postImportCiphers(request);
}
private async handleOrganizationalImport(importResult: ImportResult, organizationId: string) {
private async handleOrganizationalImport(
importResult: ImportResult,
organizationId: OrganizationId,
) {
const request = new ImportOrganizationCiphersRequest();
const activeUserId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)),