diff --git a/server/routers/client/getClient.ts b/server/routers/client/getClient.ts index a05fdef42..c97612b07 100644 --- a/server/routers/client/getClient.ts +++ b/server/routers/client/getClient.ts @@ -1,8 +1,8 @@ import { Request, Response, NextFunction } from "express"; import { z } from "zod"; -import { db, olms, users } from "@server/db"; +import { db, idp, idpOidcConfig, olms, users } from "@server/db"; import { clients, currentFingerprint } from "@server/db"; -import { eq, and } from "drizzle-orm"; +import { and, eq } from "drizzle-orm"; import response from "@server/lib/response"; import HttpCode from "@server/types/HttpCode"; import createHttpError from "http-errors"; @@ -236,6 +236,9 @@ export type GetClientResponse = NonNullable< lastSeen: number | null; } | null; posture: PostureData | null; + userType: string | null; + idpName: string | null; + idpVariant: string | null; }; registry.registerPath({ @@ -337,6 +340,30 @@ export async function getClient( : maskPostureDataWithPlaceholder(rawPosture) : null; + let userType: string | null = null; + let idpName: string | null = null; + let idpVariant: string | null = null; + + if (client.clients.userId) { + const [idpRow] = await db + .select({ + userType: users.type, + idpName: idp.name, + idpVariant: idpOidcConfig.variant + }) + .from(users) + .leftJoin(idp, eq(users.idpId, idp.idpId)) + .leftJoin(idpOidcConfig, eq(idpOidcConfig.idpId, idp.idpId)) + .where(eq(users.userId, client.clients.userId)) + .limit(1); + + if (idpRow) { + userType = idpRow.userType; + idpName = idpRow.idpName; + idpVariant = idpRow.idpVariant; + } + } + const data: GetClientResponse = { ...client.clients, name: clientName, @@ -347,7 +374,10 @@ export async function getClient( userName: client.user?.name ?? null, userUsername: client.user?.username ?? null, fingerprint: fingerprintData, - posture: postureData + posture: postureData, + userType, + idpName, + idpVariant }; return response(res, { diff --git a/server/routers/client/listUserDevices.ts b/server/routers/client/listUserDevices.ts index d793faf09..567eb0d6b 100644 --- a/server/routers/client/listUserDevices.ts +++ b/server/routers/client/listUserDevices.ts @@ -3,6 +3,8 @@ import { clients, currentFingerprint, db, + idp, + idpOidcConfig, olms, orgs, roleClients, @@ -165,6 +167,9 @@ function queryUserDevicesBase() { userId: clients.userId, username: users.username, userEmail: users.email, + userType: users.type, + idpName: idp.name, + idpVariant: idpOidcConfig.variant, niceId: clients.niceId, agent: olms.agent, approvalState: clients.approvalState, @@ -184,6 +189,8 @@ function queryUserDevicesBase() { .leftJoin(orgs, eq(clients.orgId, orgs.orgId)) .leftJoin(olms, eq(clients.clientId, olms.clientId)) .leftJoin(users, eq(clients.userId, users.userId)) + .leftJoin(idp, eq(users.idpId, idp.idpId)) + .leftJoin(idpOidcConfig, eq(idpOidcConfig.idpId, idp.idpId)) .leftJoin(currentFingerprint, eq(olms.olmId, currentFingerprint.olmId)); } diff --git a/src/app/[orgId]/settings/clients/user/page.tsx b/src/app/[orgId]/settings/clients/user/page.tsx index 23fba583a..880019177 100644 --- a/src/app/[orgId]/settings/clients/user/page.tsx +++ b/src/app/[orgId]/settings/clients/user/page.tsx @@ -96,6 +96,9 @@ export default async function ClientsPage(props: ClientsPageProps) { userId: client.userId, username: client.username, userEmail: client.userEmail, + userType: client.userType ?? null, + idpName: client.idpName ?? null, + idpVariant: client.idpVariant ?? null, niceId: client.niceId, agent: client.agent, archived: Boolean(client.archived), diff --git a/src/components/ClientInfoCard.tsx b/src/components/ClientInfoCard.tsx index 7f55a46cd..4815c85fb 100644 --- a/src/components/ClientInfoCard.tsx +++ b/src/components/ClientInfoCard.tsx @@ -8,6 +8,7 @@ import { InfoSections, InfoSectionTitle } from "@app/components/InfoSection"; +import IdpTypeBadge from "@app/components/IdpTypeBadge"; import { getUserDisplayName } from "@app/lib/getUserDisplayName"; import { useTranslations } from "next-intl"; @@ -36,7 +37,24 @@ export default function SiteInfoCard({}: ClientInfoCardProps) { {userDisplayName ? t("user") : t("identifier")} - {userDisplayName || client.niceId} +
+ {userDisplayName || client.niceId} + {userDisplayName && + (client.userType ?? "internal") !== + "internal" && ( + + )} +
diff --git a/src/components/UserDevicesTable.tsx b/src/components/UserDevicesTable.tsx index 88e495406..0a130cc16 100644 --- a/src/components/UserDevicesTable.tsx +++ b/src/components/UserDevicesTable.tsx @@ -35,6 +35,7 @@ import { useMemo, useState, useTransition } from "react"; import { useDebouncedCallback } from "use-debounce"; import ClientDownloadBanner from "./ClientDownloadBanner"; import { ColumnFilterButton } from "./ColumnFilterButton"; +import IdpTypeBadge from "./IdpTypeBadge"; import { Badge } from "./ui/badge"; import { ControlledDataTable } from "./ui/controlled-data-table"; @@ -52,6 +53,9 @@ export type ClientRow = { userId: string | null; username: string | null; userEmail: string | null; + userType: string | null; + idpName: string | null; + idpVariant: string | null; niceId: string; agent: string | null; approvalState: "approved" | "pending" | "denied" | null; @@ -370,17 +374,30 @@ export default function UserDevicesTable({ cell: ({ row }) => { const r = row.original; return r.userId ? ( - - - +
+ + + + {(r.userType ?? "internal") !== "internal" && ( + + )} +
) : ( "-" );