diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts index 19779d73a1..c669ba167d 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.spec.ts @@ -10,7 +10,7 @@ import { BillingAccountProfileStateService } from "@bitwarden/common/billing/abs import { ProductTierType } from "@bitwarden/common/billing/enums"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; -import { CipherId, UserId } from "@bitwarden/common/types/guid"; +import { CipherId, OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { CipherType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -163,7 +163,7 @@ describe("OpenAttachmentsComponent", () => { it("sets `cipherIsAPartOfFreeOrg` to true when the cipher is a part of a free organization", async () => { cipherView.organizationId = "888-333-333"; org.productTierType = ProductTierType.Free; - org.id = cipherView.organizationId; + org.id = cipherView.organizationId as OrganizationId; await component.ngOnInit(); @@ -173,7 +173,7 @@ describe("OpenAttachmentsComponent", () => { it("sets `cipherIsAPartOfFreeOrg` to false when the organization is not free", async () => { cipherView.organizationId = "888-333-333"; org.productTierType = ProductTierType.Families; - org.id = cipherView.organizationId; + org.id = cipherView.organizationId as OrganizationId; await component.ngOnInit(); diff --git a/apps/cli/src/admin-console/models/request/organization-collection.request.ts b/apps/cli/src/admin-console/models/request/organization-collection.request.ts index b5f796afe2..14714e4758 100644 --- a/apps/cli/src/admin-console/models/request/organization-collection.request.ts +++ b/apps/cli/src/admin-console/models/request/organization-collection.request.ts @@ -1,13 +1,14 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore import { CollectionExport } from "@bitwarden/common/models/export/collection.export"; +import { OrganizationId } from "@bitwarden/common/types/guid"; import { SelectionReadOnly } from "../selection-read-only"; export class OrganizationCollectionRequest extends CollectionExport { static template(): OrganizationCollectionRequest { const req = new OrganizationCollectionRequest(); - req.organizationId = "00000000-0000-0000-0000-000000000000"; + req.organizationId = "00000000-0000-0000-0000-000000000000" as OrganizationId; req.name = "Collection name"; req.externalId = null; req.groups = [SelectionReadOnly.template(), SelectionReadOnly.template()]; diff --git a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts index 5dc217b7a5..706b202210 100644 --- a/apps/web/src/app/admin-console/organizations/collections/vault.component.ts +++ b/apps/web/src/app/admin-console/organizations/collections/vault.component.ts @@ -278,9 +278,16 @@ export class VaultComponent implements OnInit, OnDestroy { ); const filter$ = this.routedVaultFilterService.filter$; + + // FIXME: The RoutedVaultFilterModel uses `organizationId: Unassigned` to represent the individual vault, + // but that is never used in Admin Console. This function narrows the type so it doesn't pollute our code here, + // but really we should change to using our own vault filter model that only represents valid states in AC. + const isOrganizationId = (value: OrganizationId | Unassigned): value is OrganizationId => + value !== Unassigned; const organizationId$ = filter$.pipe( map((filter) => filter.organizationId), filter((filter) => filter !== undefined), + filter(isOrganizationId), distinctUntilChanged(), ); @@ -373,9 +380,12 @@ export class VaultComponent implements OnInit, OnDestroy { this.allCollectionsWithoutUnassigned$, ]).pipe( map(([organizationId, allCollections]) => { + // FIXME: We should not assert that the Unassigned type is a CollectionId. + // Instead we should consider representing the Unassigned collection as a different object, given that + // it is not actually a collection. const noneCollection = new CollectionAdminView(); noneCollection.name = this.i18nService.t("unassigned"); - noneCollection.id = Unassigned; + noneCollection.id = Unassigned as CollectionId; noneCollection.organizationId = organizationId; return allCollections.concat(noneCollection); }), diff --git a/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts b/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts index fabfb65fc6..a0964a90fc 100644 --- a/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts +++ b/apps/web/src/app/admin-console/organizations/shared/components/collection-dialog/collection-dialog.component.ts @@ -39,6 +39,7 @@ import { ConfigService } from "@bitwarden/common/platform/abstractions/config/co import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { getById } from "@bitwarden/common/platform/misc"; 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; diff --git a/apps/web/src/app/secrets-manager/models/requests/request-sm-access.request.ts b/apps/web/src/app/secrets-manager/models/requests/request-sm-access.request.ts index 5edd8bc046..7292e13a6a 100644 --- a/apps/web/src/app/secrets-manager/models/requests/request-sm-access.request.ts +++ b/apps/web/src/app/secrets-manager/models/requests/request-sm-access.request.ts @@ -1,8 +1,8 @@ // FIXME: Update this file to be type safe and remove this and next line // @ts-strict-ignore -import { Guid } from "@bitwarden/common/types/guid"; +import { OrganizationId } from "@bitwarden/common/types/guid"; export class RequestSMAccessRequest { - OrganizationId: Guid; + OrganizationId: OrganizationId; EmailContent: string; } diff --git a/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts b/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts index 443b3e03e5..0e32321a0b 100644 --- a/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts +++ b/apps/web/src/app/secrets-manager/secrets-manager-landing/request-sm-access.component.ts @@ -10,7 +10,6 @@ import { Organization } from "@bitwarden/common/admin-console/models/domain/orga import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { getUserId } from "@bitwarden/common/auth/services/account.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; -import { Guid } from "@bitwarden/common/types/guid"; import { NoItemsModule, SearchModule, ToastService } from "@bitwarden/components"; import { HeaderModule } from "../../layouts/header/header.module"; @@ -63,7 +62,7 @@ export class RequestSMAccessComponent implements OnInit { const formValue = this.requestAccessForm.value; const request = new RequestSMAccessRequest(); - request.OrganizationId = formValue.selectedOrganization.id as Guid; + request.OrganizationId = formValue.selectedOrganization.id; request.EmailContent = formValue.requestAccessEmailContents; await this.smLandingApiService.requestSMAccessFromAdmins(request); diff --git a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts index 78c4d21ded..c114cb6d7c 100644 --- a/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts +++ b/apps/web/src/app/vault/components/vault-items/vault-items.stories.ts @@ -29,6 +29,7 @@ import { } from "@bitwarden/common/platform/abstractions/environment.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums"; import { AttachmentView } from "@bitwarden/common/vault/models/view/attachment.view"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -262,7 +263,7 @@ export const OrganizationTrash: Story = { }; const unassignedCollection = new CollectionAdminView(); -unassignedCollection.id = Unassigned; +unassignedCollection.id = Unassigned as CollectionId; unassignedCollection.name = "Unassigned"; export const OrganizationTopLevelCollection: Story = { args: { @@ -327,7 +328,7 @@ function createCollectionView(i: number): CollectionAdminView { const organization = organizations[i % (organizations.length + 1)]; const group = groups[i % (groups.length + 1)]; const view = new CollectionAdminView(); - view.id = `collection-${i}`; + view.id = `collection-${i}` as CollectionId; view.name = `Collection ${i}`; view.organizationId = organization?.id; view.manage = true; @@ -357,7 +358,7 @@ function createGroupView(i: number): GroupView { function createOrganization(i: number): Organization { const organization = new Organization(); - organization.id = `organization-${i}`; + organization.id = `organization-${i}` as OrganizationId; organization.name = `Organization ${i}`; organization.type = OrganizationUserType.Owner; organization.permissions = new PermissionsApi(); diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts b/apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts index a42b522827..a5a99428b2 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/services/routed-vault-filter.service.ts @@ -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,12 @@ export class RoutedVaultFilterService implements OnDestroy { const type = isRoutedVaultFilterItemType(unsafeType) ? unsafeType : undefined; return { - collectionId: queryParams.get("collectionId") ?? undefined, + collectionId: (queryParams.get("collectionId") as CollectionId) ?? undefined, folderId: queryParams.get("folderId") ?? undefined, organizationId: - params.get("organizationId") ?? queryParams.get("organizationId") ?? undefined, + (params.get("organizationId") as OrganizationId) ?? + (queryParams.get("organizationId") as OrganizationId) ?? + undefined, organizationIdParamType: params.get("organizationId") != undefined ? ("path" as const) : ("query" as const), type, diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts index 266676e418..11e074db98 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/services/vault-filter.service.ts @@ -28,7 +28,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 { SingleUserState, StateProvider } from "@bitwarden/common/platform/state"; -import { UserId } from "@bitwarden/common/types/guid"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType } from "@bitwarden/common/vault/enums"; @@ -209,7 +209,7 @@ export class VaultFilterService implements VaultFilterServiceAbstraction { protected getOrganizationFilterMyVault(): TreeNode { const myVault = new Organization() as OrganizationFilter; - myVault.id = "MyVault"; + myVault.id = "MyVault" as OrganizationId; myVault.icon = "bwi-user"; myVault.enabled = true; myVault.hideOptions = true; 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 fe236a089e..02d536eb6a 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 @@ -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) { - let collectionId: string | undefined; + let collectionId: CollectionId | All | Unassigned | undefined; if (value != null && value.node.id === null) { collectionId = Unassigned; diff --git a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts index 866ba1d984..280ffd1573 100644 --- a/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts +++ b/apps/web/src/app/vault/individual-vault/vault-filter/shared/models/routed-vault-filter.model.ts @@ -1,4 +1,11 @@ +import { Unassigned } from "@bitwarden/admin-console/common"; +import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; + +/** + * A constant used to represent viewing "all" of a particular filter. + */ export const All = "all"; +export type All = typeof All; // TODO: Remove `All` when moving to vertical navigation. const itemTypes = [ @@ -19,9 +26,9 @@ export function isRoutedVaultFilterItemType(value: unknown): value is RoutedVaul } export interface RoutedVaultFilterModel { - collectionId?: string; + collectionId?: CollectionId | All | Unassigned; folderId?: string; - organizationId?: string; + organizationId?: OrganizationId | Unassigned; type?: RoutedVaultFilterItemType; organizationIdParamType?: "path" | "query"; diff --git a/libs/admin-console/src/common/collections/models/collection-admin.view.ts b/libs/admin-console/src/common/collections/models/collection-admin.view.ts index dd7a57013c..dcc8855155 100644 --- a/libs/admin-console/src/common/collections/models/collection-admin.view.ts +++ b/libs/admin-console/src/common/collections/models/collection-admin.view.ts @@ -4,7 +4,10 @@ import { CollectionAccessSelectionView } from "./collection-access-selection.vie import { CollectionAccessDetailsResponse } from "./collection.response"; import { CollectionView } from "./collection.view"; +// TODO: this is used to represent the pseudo "Unassigned" collection as well as +// the user's personal vault (as a pseudo organization). This should be separated out into different values. export const Unassigned = "unassigned"; +export type Unassigned = typeof Unassigned; export class CollectionAdminView extends CollectionView { groups: CollectionAccessSelectionView[] = []; diff --git a/libs/admin-console/src/common/collections/models/collection.spec.ts b/libs/admin-console/src/common/collections/models/collection.spec.ts index 925490d22b..fb38f1507f 100644 --- a/libs/admin-console/src/common/collections/models/collection.spec.ts +++ b/libs/admin-console/src/common/collections/models/collection.spec.ts @@ -54,7 +54,7 @@ describe("Collection", () => { it("Decrypt", async () => { const collection = new Collection(); - collection.id = "id"; + collection.id = "id" as CollectionId; collection.organizationId = "orgId" as OrganizationId; collection.name = mockEnc("encName"); collection.externalId = "extId"; diff --git a/libs/admin-console/src/common/collections/models/collection.ts b/libs/admin-console/src/common/collections/models/collection.ts index 7bbd018fa9..d1709d1751 100644 --- a/libs/admin-console/src/common/collections/models/collection.ts +++ b/libs/admin-console/src/common/collections/models/collection.ts @@ -1,5 +1,6 @@ import { EncString } from "@bitwarden/common/key-management/crypto/models/enc-string"; import Domain, { EncryptableKeys } 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"; @@ -13,8 +14,8 @@ export const CollectionTypes = { export type CollectionType = (typeof CollectionTypes)[keyof typeof CollectionTypes]; export class Collection extends Domain { - id: string | undefined; - organizationId: string | undefined; + id: CollectionId | undefined; + organizationId: OrganizationId | undefined; name: EncString | undefined; externalId: string | undefined; readOnly: boolean = false; diff --git a/libs/admin-console/src/common/collections/models/collection.view.ts b/libs/admin-console/src/common/collections/models/collection.view.ts index f75ff56510..3a60320856 100644 --- a/libs/admin-console/src/common/collections/models/collection.view.ts +++ b/libs/admin-console/src/common/collections/models/collection.view.ts @@ -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 = ""; externalId: string | undefined; // readOnly applies to the items within a collection diff --git a/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts b/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts index 57df2d0339..d0dfd44e41 100644 --- a/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts +++ b/libs/angular/src/vault/services/custom-nudges-services/empty-vault-nudge.service.ts @@ -5,7 +5,7 @@ import { combineLatest, Observable, of, switchMap } from "rxjs"; // eslint-disable-next-line no-restricted-imports import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { UserId } from "@bitwarden/common/types/guid"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { DefaultSingleNudgeService } from "../default-single-nudge.service"; @@ -42,7 +42,7 @@ export class EmptyVaultNudgeService extends DefaultSingleNudgeService { const orgIds = new Set(orgs.map((org) => org.id)); const canCreateCollections = orgs.some((org) => org.canCreateNewCollections); const hasManageCollections = collections.some( - (c) => c.manage && orgIds.has(c.organizationId!), + (c) => c.manage && orgIds.has(c.organizationId! as OrganizationId), ); // When the user has dismissed the nudge or spotlight, return the nudge status directly diff --git a/libs/angular/src/vault/services/custom-nudges-services/vault-settings-import-nudge.service.ts b/libs/angular/src/vault/services/custom-nudges-services/vault-settings-import-nudge.service.ts index 2529fc40b7..a8f29830f8 100644 --- a/libs/angular/src/vault/services/custom-nudges-services/vault-settings-import-nudge.service.ts +++ b/libs/angular/src/vault/services/custom-nudges-services/vault-settings-import-nudge.service.ts @@ -5,7 +5,7 @@ import { combineLatest, Observable, of, switchMap } from "rxjs"; // eslint-disable-next-line no-restricted-imports import { CollectionService } from "@bitwarden/admin-console/common"; import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; -import { UserId } from "@bitwarden/common/types/guid"; +import { OrganizationId, UserId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { DefaultSingleNudgeService } from "../default-single-nudge.service"; @@ -46,7 +46,7 @@ export class VaultSettingsImportNudgeService extends DefaultSingleNudgeService { const orgIds = new Set(orgs.map((org) => org.id)); const canCreateCollections = orgs.some((org) => org.canCreateNewCollections); const hasManageCollections = collections.some( - (c) => c.manage && orgIds.has(c.organizationId!), + (c) => c.manage && orgIds.has(c.organizationId! as OrganizationId), ); // When the user has dismissed the nudge or spotlight, return the nudge status directly diff --git a/libs/common/src/admin-console/models/domain/organization.ts b/libs/common/src/admin-console/models/domain/organization.ts index f8de529391..130b32e519 100644 --- a/libs/common/src/admin-console/models/domain/organization.ts +++ b/libs/common/src/admin-console/models/domain/organization.ts @@ -3,12 +3,13 @@ import { Jsonify } from "type-fest"; import { ProductTierType } from "../../../billing/enums"; +import { OrganizationId } from "../../../types/guid"; import { OrganizationUserStatusType, OrganizationUserType, ProviderType } from "../../enums"; import { PermissionsApi } from "../api/permissions.api"; import { OrganizationData } from "../data/organization.data"; export class Organization { - id: string; + id: OrganizationId; name: string; status: OrganizationUserStatusType; @@ -99,7 +100,7 @@ export class Organization { return; } - this.id = obj.id; + this.id = obj.id as OrganizationId; this.name = obj.name; this.status = obj.status; this.type = obj.type; diff --git a/libs/common/src/admin-console/models/domain/policy.ts b/libs/common/src/admin-console/models/domain/policy.ts index b45acb9920..8408f4832d 100644 --- a/libs/common/src/admin-console/models/domain/policy.ts +++ b/libs/common/src/admin-console/models/domain/policy.ts @@ -2,14 +2,14 @@ // @ts-strict-ignore import { ListResponse } from "../../../models/response/list.response"; import Domain from "../../../platform/models/domain/domain-base"; -import { PolicyId } from "../../../types/guid"; +import { OrganizationId, PolicyId } from "../../../types/guid"; import { PolicyType } from "../../enums"; import { PolicyData } from "../data/policy.data"; import { PolicyResponse } from "../response/policy.response"; export class Policy extends Domain { id: PolicyId; - organizationId: string; + organizationId: OrganizationId; type: PolicyType; data: any; @@ -26,7 +26,7 @@ export class Policy extends Domain { } this.id = obj.id; - this.organizationId = obj.organizationId; + this.organizationId = obj.organizationId as OrganizationId; this.type = obj.type; this.data = obj.data; this.enabled = obj.enabled; diff --git a/libs/common/src/models/export/collection-with-id.export.ts b/libs/common/src/models/export/collection-with-id.export.ts index a93f07b54e..c973472e0b 100644 --- a/libs/common/src/models/export/collection-with-id.export.ts +++ b/libs/common/src/models/export/collection-with-id.export.ts @@ -4,10 +4,12 @@ // 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; + id: CollectionId; static toView(req: CollectionWithIdExport, view = new CollectionView()) { view.id = req.id; diff --git a/libs/common/src/models/export/collection.export.ts b/libs/common/src/models/export/collection.export.ts index 88b68c08c5..b141346d03 100644 --- a/libs/common/src/models/export/collection.export.ts +++ b/libs/common/src/models/export/collection.export.ts @@ -5,13 +5,14 @@ import { Collection as CollectionDomain, CollectionView } from "@bitwarden/admin-console/common"; import { EncString } from "../../key-management/crypto/models/enc-string"; +import { emptyGuid, OrganizationId } from "../../types/guid"; import { safeGetString } from "./utils"; export class CollectionExport { static template(): CollectionExport { const req = new CollectionExport(); - req.organizationId = "00000000-0000-0000-0000-000000000000"; + req.organizationId = emptyGuid as OrganizationId; req.name = "Collection name"; req.externalId = null; return req; @@ -35,7 +36,7 @@ export class CollectionExport { return domain; } - organizationId: string; + organizationId: OrganizationId; name: string; externalId: string; diff --git a/libs/common/src/types/guid.ts b/libs/common/src/types/guid.ts index bd0980cd36..5a6aaf2ce5 100644 --- a/libs/common/src/types/guid.ts +++ b/libs/common/src/types/guid.ts @@ -20,3 +20,8 @@ export type OrganizationIntegrationConfigurationId = Opaque< string, "OrganizationIntegrationConfigurationId" >; + +/** + * A string representation of an empty guid. + */ +export const emptyGuid = "00000000-0000-0000-0000-000000000000"; diff --git a/libs/importer/src/importers/base-importer.ts b/libs/importer/src/importers/base-importer.ts index 1a97bc5a32..4c25a01f96 100644 --- a/libs/importer/src/importers/base-importer.ts +++ b/libs/importer/src/importers/base-importer.ts @@ -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 = []; diff --git a/libs/importer/src/importers/keeper/keeper-csv-importer.spec.ts b/libs/importer/src/importers/keeper/keeper-csv-importer.spec.ts index 026c501cf5..b326bc5d35 100644 --- a/libs/importer/src/importers/keeper/keeper-csv-importer.spec.ts +++ b/libs/importer/src/importers/keeper/keeper-csv-importer.spec.ts @@ -1,4 +1,5 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { OrganizationId } from "@bitwarden/common/types/guid"; import { testData as TestData, @@ -103,7 +104,7 @@ describe("Keeper CSV Importer", () => { }); it("should create collections, with subcollections and relationships", async () => { - importer.organizationId = Utils.newGuid(); + importer.organizationId = Utils.newGuid() as OrganizationId; const result = await importer.parse(TestData); expect(result != null).toBe(true); @@ -126,7 +127,7 @@ describe("Keeper CSV Importer", () => { }); it("should create collections tree, with child collections and relationships", async () => { - importer.organizationId = Utils.newGuid(); + importer.organizationId = Utils.newGuid() as OrganizationId; const result = await importer.parse(testDataMultiCollection); expect(result != null).toBe(true); diff --git a/libs/importer/src/importers/keeper/keeper-json-importer.spec.ts b/libs/importer/src/importers/keeper/keeper-json-importer.spec.ts index 22008f3b4c..1141897a04 100644 --- a/libs/importer/src/importers/keeper/keeper-json-importer.spec.ts +++ b/libs/importer/src/importers/keeper/keeper-json-importer.spec.ts @@ -1,4 +1,5 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { OrganizationId } from "@bitwarden/common/types/guid"; import { testData as TestData } from "../spec-data/keeper-json/testdata.json"; @@ -93,7 +94,7 @@ describe("Keeper Json Importer", () => { }); it("should create collections if part of an organization", async () => { - importer.organizationId = Utils.newGuid(); + importer.organizationId = Utils.newGuid() as OrganizationId; const result = await importer.parse(testDataJson); expect(result != null).toBe(true); diff --git a/libs/importer/src/importers/netwrix/netwrix-passwordsecure-csv-importer.spec.ts b/libs/importer/src/importers/netwrix/netwrix-passwordsecure-csv-importer.spec.ts index 8736b3df0c..b5479ce83c 100644 --- a/libs/importer/src/importers/netwrix/netwrix-passwordsecure-csv-importer.spec.ts +++ b/libs/importer/src/importers/netwrix/netwrix-passwordsecure-csv-importer.spec.ts @@ -1,4 +1,5 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { OrganizationId } from "@bitwarden/common/types/guid"; import { credentialsData, @@ -79,7 +80,7 @@ describe("Netwrix Password Secure CSV Importer", () => { }); it("should parse an item and create a collection when importing into an organization", async () => { - importer.organizationId = Utils.newGuid(); + importer.organizationId = Utils.newGuid() as OrganizationId; const result = await importer.parse(credentialsData); expect(result).not.toBeNull(); @@ -93,7 +94,7 @@ describe("Netwrix Password Secure CSV Importer", () => { }); it("should parse multiple collections", async () => { - importer.organizationId = Utils.newGuid(); + importer.organizationId = Utils.newGuid() as OrganizationId; const result = await importer.parse(credentialsDataWithFolders); expect(result).not.toBeNull(); diff --git a/libs/importer/src/importers/nordpass-csv-importer.spec.ts b/libs/importer/src/importers/nordpass-csv-importer.spec.ts index cadc7bca28..e633310e6e 100644 --- a/libs/importer/src/importers/nordpass-csv-importer.spec.ts +++ b/libs/importer/src/importers/nordpass-csv-importer.spec.ts @@ -1,4 +1,5 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { OrganizationId } from "@bitwarden/common/types/guid"; import { SecureNoteType, CipherType, FieldType } from "@bitwarden/common/vault/enums"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view"; @@ -224,7 +225,7 @@ describe("NordPass CSV Importer", () => { }); it("should parse an item and create a collection if organizationId is set", async () => { - importer.organizationId = Utils.newGuid(); + importer.organizationId = Utils.newGuid() as OrganizationId; const result = await importer.parse(secureNoteData); expect(result).not.toBeNull(); diff --git a/libs/importer/src/importers/onepassword/onepassword-1pux-importer.spec.ts b/libs/importer/src/importers/onepassword/onepassword-1pux-importer.spec.ts index d4976f7a19..1ca12a9ce6 100644 --- a/libs/importer/src/importers/onepassword/onepassword-1pux-importer.spec.ts +++ b/libs/importer/src/importers/onepassword/onepassword-1pux-importer.spec.ts @@ -1,4 +1,5 @@ import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { OrganizationId } from "@bitwarden/common/types/guid"; import { FieldType, SecureNoteType, CipherType } from "@bitwarden/common/vault/enums"; import { FieldView } from "@bitwarden/common/vault/models/view/field.view"; @@ -691,7 +692,7 @@ describe("1Password 1Pux Importer", () => { it("should create collections if part of an organization", async () => { const importer = new OnePassword1PuxImporter(); - importer.organizationId = Utils.newGuid(); + importer.organizationId = Utils.newGuid() as OrganizationId; const result = await importer.parse(SanitizedExportJson); expect(result != null).toBe(true); diff --git a/libs/importer/src/importers/passsordxp/passwordxp-csv-importer.spec.ts b/libs/importer/src/importers/passsordxp/passwordxp-csv-importer.spec.ts index 12cfbbe62b..67d90ef966 100644 --- a/libs/importer/src/importers/passsordxp/passwordxp-csv-importer.spec.ts +++ b/libs/importer/src/importers/passsordxp/passwordxp-csv-importer.spec.ts @@ -1,3 +1,4 @@ +import { OrganizationId } from "@bitwarden/common/types/guid"; import { CipherType } from "@bitwarden/common/vault/enums"; import { ImportResult } from "../../models/import-result"; @@ -146,7 +147,7 @@ describe("PasswordXPCsvImporter", () => { }); it("should convert folders to collections when importing into an organization", async () => { - importer.organizationId = "someOrg"; + importer.organizationId = "someOrg" as OrganizationId; const result: ImportResult = await importer.parse(withFolders); expect(result.success).toBe(true); expect(result.ciphers.length).toBe(5); @@ -172,7 +173,7 @@ describe("PasswordXPCsvImporter", () => { }); it("should convert multi-level folders to collections when importing into an organization", async () => { - importer.organizationId = "someOrg"; + importer.organizationId = "someOrg" as OrganizationId; const result: ImportResult = await importer.parse(withMultipleFolders); expect(result.success).toBe(true); expect(result.ciphers.length).toBe(5); diff --git a/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts b/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts index ea84603aef..dc438c0ede 100644 --- a/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts +++ b/libs/importer/src/importers/password-depot/password-depot-17-xml-importer.spec.ts @@ -1,6 +1,7 @@ // This import has been flagged as unallowed for this class. It may be involved in a circular dependency loop. // eslint-disable-next-line no-restricted-imports import { CollectionView } from "@bitwarden/admin-console/common"; +import { OrganizationId } from "@bitwarden/common/types/guid"; import { FieldType, SecureNoteType } from "@bitwarden/common/vault/enums"; import { FolderView } from "@bitwarden/common/vault/models/view/folder.view"; import { CipherType } from "@bitwarden/sdk-internal"; @@ -485,7 +486,7 @@ describe("Password Depot 17 Xml Importer", () => { it("should parse groups nodes into collections when importing into an organization", async () => { const importer = new PasswordDepot17XmlImporter(); - importer.organizationId = "someOrgId"; + importer.organizationId = "someOrgId" as OrganizationId; const collection = new CollectionView(); collection.name = "tempDB"; const actual = [collection]; diff --git a/libs/importer/src/importers/protonpass/protonpass-json-importer.spec.ts b/libs/importer/src/importers/protonpass/protonpass-json-importer.spec.ts index b8550bcb19..a97239fbd1 100644 --- a/libs/importer/src/importers/protonpass/protonpass-json-importer.spec.ts +++ b/libs/importer/src/importers/protonpass/protonpass-json-importer.spec.ts @@ -2,6 +2,7 @@ import { MockProxy } from "jest-mock-extended"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { OrganizationId } from "@bitwarden/common/types/guid"; import { FieldType, CipherType } from "@bitwarden/common/vault/enums"; import { testData } from "../spec-data/protonpass-json/protonpass.json"; @@ -90,7 +91,7 @@ describe("Protonpass Json Importer", () => { it("should create collections if part of an organization", async () => { const testDataJson = JSON.stringify(testData); - importer.organizationId = Utils.newGuid(); + importer.organizationId = Utils.newGuid() as OrganizationId; const result = await importer.parse(testDataJson); expect(result != null).toBe(true); diff --git a/libs/importer/src/importers/psono/psono-json-importer.spec.ts b/libs/importer/src/importers/psono/psono-json-importer.spec.ts index 3b4fcf67a3..29e5a8fb4a 100644 --- a/libs/importer/src/importers/psono/psono-json-importer.spec.ts +++ b/libs/importer/src/importers/psono/psono-json-importer.spec.ts @@ -1,3 +1,4 @@ +import { OrganizationId } from "@bitwarden/common/types/guid"; import { FieldType, CipherType } from "@bitwarden/common/vault/enums"; import { FieldView } from "@bitwarden/common/vault/models/view/field.view"; @@ -236,7 +237,7 @@ describe("PSONO JSON Importer", () => { it("should create collections if part of an organization", async () => { const importer = new PsonoJsonImporter(); - importer.organizationId = "someOrg"; + importer.organizationId = "someOrg" as OrganizationId; const result = await importer.parse(FoldersTestDataJson); expect(result != null).toBe(true); diff --git a/libs/importer/src/importers/zohovault-csv-importer.spec.ts b/libs/importer/src/importers/zohovault-csv-importer.spec.ts index e49b527cbb..d3904fb521 100644 --- a/libs/importer/src/importers/zohovault-csv-importer.spec.ts +++ b/libs/importer/src/importers/zohovault-csv-importer.spec.ts @@ -1,3 +1,4 @@ +import { OrganizationId } from "@bitwarden/common/types/guid"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { LoginUriView } from "@bitwarden/common/vault/models/view/login-uri.view"; import { LoginView } from "@bitwarden/common/vault/models/view/login.view"; @@ -73,7 +74,7 @@ describe("Zoho Vault CSV Importer", () => { it("should create collection and assign ciphers when importing into an organization", async () => { const importer = new ZohoVaultCsvImporter(); - importer.organizationId = "someOrgId"; + importer.organizationId = "someOrgId" as OrganizationId; const result = await importer.parse(samplezohovaultcsvdata); expect(result != null).toBe(true); expect(result.success).toBe(true); diff --git a/libs/importer/src/services/import.service.spec.ts b/libs/importer/src/services/import.service.spec.ts index 4245b770ce..ac560ed6f7 100644 --- a/libs/importer/src/services/import.service.spec.ts +++ b/libs/importer/src/services/import.service.spec.ts @@ -9,6 +9,7 @@ import { PinServiceAbstraction } from "@bitwarden/common/key-management/pin/pin. import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; import { MockSdkService } from "@bitwarden/common/platform/spec/mock-sdk.service"; +import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; @@ -67,7 +68,7 @@ describe("ImportService", () => { describe("getImporterInstance", () => { describe("Get bitPasswordProtected importer", () => { let importer: Importer; - const organizationId = Utils.newGuid(); + const organizationId = Utils.newGuid() as OrganizationId; const password = Utils.newGuid(); const promptForPassword_callback = async () => { return password; @@ -98,7 +99,7 @@ describe("ImportService", () => { }); describe("setImportTarget", () => { - const organizationId = Utils.newGuid(); + const organizationId = Utils.newGuid() as OrganizationId; let importResult: ImportResult; @@ -145,19 +146,19 @@ describe("ImportService", () => { }); const mockImportTargetCollection = new CollectionView(); - mockImportTargetCollection.id = "myImportTarget"; + mockImportTargetCollection.id = "myImportTarget" as CollectionId; mockImportTargetCollection.name = "myImportTarget"; mockImportTargetCollection.organizationId = organizationId; const mockCollection1 = new CollectionView(); - mockCollection1.id = "collection1"; + mockCollection1.id = "collection1" as CollectionId; mockCollection1.name = "collection1"; mockCollection1.organizationId = organizationId; const mockCollection2 = new CollectionView(); - mockCollection1.id = "collection2"; - mockCollection1.name = "collection2"; - mockCollection1.organizationId = organizationId; + mockCollection2.id = "collection2" as CollectionId; + mockCollection2.name = "collection2"; + mockCollection2.organizationId = organizationId; it("passing importTarget adds it to collections", async () => { await importService["setImportTarget"]( diff --git a/libs/importer/src/services/import.service.ts b/libs/importer/src/services/import.service.ts index 9acd4514b3..d3880f63bd 100644 --- a/libs/importer/src/services/import.service.ts +++ b/libs/importer/src/services/import.service.ts @@ -19,6 +19,7 @@ import { ErrorResponse } from "@bitwarden/common/models/response/error.response" import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { Utils } from "@bitwarden/common/platform/misc/utils"; +import { OrganizationId } from "@bitwarden/common/types/guid"; import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; import { FolderService } from "@bitwarden/common/vault/abstractions/folder/folder.service.abstraction"; import { CipherType, toCipherTypeName } from "@bitwarden/common/vault/enums"; @@ -130,7 +131,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 { @@ -204,7 +205,7 @@ export class ImportService implements ImportServiceAbstraction { getImporter( format: ImportType | "bitwardenpasswordprotected", promptForPassword_callback: () => Promise, - organizationId: string = null, + organizationId: OrganizationId = null, ): Importer { if (promptForPassword_callback == null) { return null; @@ -393,7 +394,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)), diff --git a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts index 3c513a2f06..1f2aaa8904 100644 --- a/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts +++ b/libs/vault/src/cipher-form/components/item-details/item-details-section.component.spec.ts @@ -14,6 +14,7 @@ import { Policy } from "@bitwarden/common/admin-console/models/domain/policy"; import { AccountService } from "@bitwarden/common/auth/abstractions/account.service"; import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { CollectionId, OrganizationId } from "@bitwarden/common/types/guid"; import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { SelectComponent } from "@bitwarden/components"; @@ -33,9 +34,9 @@ const createMockCollection = ( canEdit = true, ): CollectionView => { return { - id, + id: id as CollectionId, name, - organizationId, + organizationId: organizationId as OrganizationId, externalId: "", readOnly, hidePasswords: false,