1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-15 07:43:35 +00:00

[PM-23920] Admin Console - adopt strongly typed guids (#15814)

Update organization, collection and policy to use strongly typed IDs
This commit is contained in:
Thomas Rittson
2025-08-06 15:27:52 +10:00
committed by GitHub
parent 598348fcc1
commit 61cd0c4f51
36 changed files with 122 additions and 67 deletions

View File

@@ -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);
}),

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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();

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,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,

View File

@@ -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<OrganizationFilter> {
const myVault = new Organization() as OrganizationFilter;
myVault.id = "MyVault";
myVault.id = "MyVault" as OrganizationId;
myVault.icon = "bwi-user";
myVault.enabled = true;
myVault.hideOptions = true;

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 | All | Unassigned | undefined;
if (value != null && value.node.id === null) {
collectionId = Unassigned;

View File

@@ -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";