mirror of
https://github.com/fosrl/pangolin.git
synced 2026-06-01 13:36:29 +00:00
show user idp in devices
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
import { Request, Response, NextFunction } from "express";
|
import { Request, Response, NextFunction } from "express";
|
||||||
import { z } from "zod";
|
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 { clients, currentFingerprint } from "@server/db";
|
||||||
import { eq, and } from "drizzle-orm";
|
import { and, eq } from "drizzle-orm";
|
||||||
import response from "@server/lib/response";
|
import response from "@server/lib/response";
|
||||||
import HttpCode from "@server/types/HttpCode";
|
import HttpCode from "@server/types/HttpCode";
|
||||||
import createHttpError from "http-errors";
|
import createHttpError from "http-errors";
|
||||||
@@ -236,6 +236,9 @@ export type GetClientResponse = NonNullable<
|
|||||||
lastSeen: number | null;
|
lastSeen: number | null;
|
||||||
} | null;
|
} | null;
|
||||||
posture: PostureData | null;
|
posture: PostureData | null;
|
||||||
|
userType: string | null;
|
||||||
|
idpName: string | null;
|
||||||
|
idpVariant: string | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
registry.registerPath({
|
registry.registerPath({
|
||||||
@@ -337,6 +340,30 @@ export async function getClient(
|
|||||||
: maskPostureDataWithPlaceholder(rawPosture)
|
: maskPostureDataWithPlaceholder(rawPosture)
|
||||||
: null;
|
: 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 = {
|
const data: GetClientResponse = {
|
||||||
...client.clients,
|
...client.clients,
|
||||||
name: clientName,
|
name: clientName,
|
||||||
@@ -347,7 +374,10 @@ export async function getClient(
|
|||||||
userName: client.user?.name ?? null,
|
userName: client.user?.name ?? null,
|
||||||
userUsername: client.user?.username ?? null,
|
userUsername: client.user?.username ?? null,
|
||||||
fingerprint: fingerprintData,
|
fingerprint: fingerprintData,
|
||||||
posture: postureData
|
posture: postureData,
|
||||||
|
userType,
|
||||||
|
idpName,
|
||||||
|
idpVariant
|
||||||
};
|
};
|
||||||
|
|
||||||
return response<GetClientResponse>(res, {
|
return response<GetClientResponse>(res, {
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ import {
|
|||||||
clients,
|
clients,
|
||||||
currentFingerprint,
|
currentFingerprint,
|
||||||
db,
|
db,
|
||||||
|
idp,
|
||||||
|
idpOidcConfig,
|
||||||
olms,
|
olms,
|
||||||
orgs,
|
orgs,
|
||||||
roleClients,
|
roleClients,
|
||||||
@@ -165,6 +167,9 @@ function queryUserDevicesBase() {
|
|||||||
userId: clients.userId,
|
userId: clients.userId,
|
||||||
username: users.username,
|
username: users.username,
|
||||||
userEmail: users.email,
|
userEmail: users.email,
|
||||||
|
userType: users.type,
|
||||||
|
idpName: idp.name,
|
||||||
|
idpVariant: idpOidcConfig.variant,
|
||||||
niceId: clients.niceId,
|
niceId: clients.niceId,
|
||||||
agent: olms.agent,
|
agent: olms.agent,
|
||||||
approvalState: clients.approvalState,
|
approvalState: clients.approvalState,
|
||||||
@@ -184,6 +189,8 @@ function queryUserDevicesBase() {
|
|||||||
.leftJoin(orgs, eq(clients.orgId, orgs.orgId))
|
.leftJoin(orgs, eq(clients.orgId, orgs.orgId))
|
||||||
.leftJoin(olms, eq(clients.clientId, olms.clientId))
|
.leftJoin(olms, eq(clients.clientId, olms.clientId))
|
||||||
.leftJoin(users, eq(clients.userId, users.userId))
|
.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));
|
.leftJoin(currentFingerprint, eq(olms.olmId, currentFingerprint.olmId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -96,6 +96,9 @@ export default async function ClientsPage(props: ClientsPageProps) {
|
|||||||
userId: client.userId,
|
userId: client.userId,
|
||||||
username: client.username,
|
username: client.username,
|
||||||
userEmail: client.userEmail,
|
userEmail: client.userEmail,
|
||||||
|
userType: client.userType ?? null,
|
||||||
|
idpName: client.idpName ?? null,
|
||||||
|
idpVariant: client.idpVariant ?? null,
|
||||||
niceId: client.niceId,
|
niceId: client.niceId,
|
||||||
agent: client.agent,
|
agent: client.agent,
|
||||||
archived: Boolean(client.archived),
|
archived: Boolean(client.archived),
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
InfoSections,
|
InfoSections,
|
||||||
InfoSectionTitle
|
InfoSectionTitle
|
||||||
} from "@app/components/InfoSection";
|
} from "@app/components/InfoSection";
|
||||||
|
import IdpTypeBadge from "@app/components/IdpTypeBadge";
|
||||||
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
|
import { getUserDisplayName } from "@app/lib/getUserDisplayName";
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
|
|
||||||
@@ -36,7 +37,24 @@ export default function SiteInfoCard({}: ClientInfoCardProps) {
|
|||||||
{userDisplayName ? t("user") : t("identifier")}
|
{userDisplayName ? t("user") : t("identifier")}
|
||||||
</InfoSectionTitle>
|
</InfoSectionTitle>
|
||||||
<InfoSectionContent>
|
<InfoSectionContent>
|
||||||
{userDisplayName || client.niceId}
|
<div className="flex flex-wrap items-center gap-2">
|
||||||
|
<span>{userDisplayName || client.niceId}</span>
|
||||||
|
{userDisplayName &&
|
||||||
|
(client.userType ?? "internal") !==
|
||||||
|
"internal" && (
|
||||||
|
<IdpTypeBadge
|
||||||
|
type={client.userType ?? "oidc"}
|
||||||
|
name={
|
||||||
|
client.idpName?.trim()
|
||||||
|
? client.idpName
|
||||||
|
: t("idpNameInternal")
|
||||||
|
}
|
||||||
|
variant={
|
||||||
|
client.idpVariant ?? undefined
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</InfoSectionContent>
|
</InfoSectionContent>
|
||||||
</InfoSection>
|
</InfoSection>
|
||||||
<InfoSection>
|
<InfoSection>
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import { useMemo, useState, useTransition } from "react";
|
|||||||
import { useDebouncedCallback } from "use-debounce";
|
import { useDebouncedCallback } from "use-debounce";
|
||||||
import ClientDownloadBanner from "./ClientDownloadBanner";
|
import ClientDownloadBanner from "./ClientDownloadBanner";
|
||||||
import { ColumnFilterButton } from "./ColumnFilterButton";
|
import { ColumnFilterButton } from "./ColumnFilterButton";
|
||||||
|
import IdpTypeBadge from "./IdpTypeBadge";
|
||||||
import { Badge } from "./ui/badge";
|
import { Badge } from "./ui/badge";
|
||||||
import { ControlledDataTable } from "./ui/controlled-data-table";
|
import { ControlledDataTable } from "./ui/controlled-data-table";
|
||||||
|
|
||||||
@@ -52,6 +53,9 @@ export type ClientRow = {
|
|||||||
userId: string | null;
|
userId: string | null;
|
||||||
username: string | null;
|
username: string | null;
|
||||||
userEmail: string | null;
|
userEmail: string | null;
|
||||||
|
userType: string | null;
|
||||||
|
idpName: string | null;
|
||||||
|
idpVariant: string | null;
|
||||||
niceId: string;
|
niceId: string;
|
||||||
agent: string | null;
|
agent: string | null;
|
||||||
approvalState: "approved" | "pending" | "denied" | null;
|
approvalState: "approved" | "pending" | "denied" | null;
|
||||||
@@ -370,17 +374,30 @@ export default function UserDevicesTable({
|
|||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const r = row.original;
|
const r = row.original;
|
||||||
return r.userId ? (
|
return r.userId ? (
|
||||||
<Link
|
<div className="flex items-center gap-2">
|
||||||
href={`/${r.orgId}/settings/access/users/${r.userId}`}
|
<Link
|
||||||
>
|
href={`/${r.orgId}/settings/access/users/${r.userId}`}
|
||||||
<Button variant="outline" size="sm">
|
>
|
||||||
{getUserDisplayName({
|
<Button variant="outline" size="sm">
|
||||||
email: r.userEmail,
|
{getUserDisplayName({
|
||||||
username: r.username
|
email: r.userEmail,
|
||||||
}) || r.userId}
|
username: r.username
|
||||||
<ArrowUpRight className="ml-2 h-3 w-3" />
|
}) || r.userId}
|
||||||
</Button>
|
<ArrowUpRight className="ml-2 h-3 w-3" />
|
||||||
</Link>
|
</Button>
|
||||||
|
</Link>
|
||||||
|
{(r.userType ?? "internal") !== "internal" && (
|
||||||
|
<IdpTypeBadge
|
||||||
|
type={r.userType ?? "oidc"}
|
||||||
|
name={
|
||||||
|
r.idpName?.trim()
|
||||||
|
? r.idpName
|
||||||
|
: t("idpNameInternal")
|
||||||
|
}
|
||||||
|
variant={r.idpVariant ?? undefined}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
"-"
|
"-"
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user