mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-21 11:28:43 +00:00
feat(pam): PAM Platform V1
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
import { Helmet } from "react-helmet";
|
||||
import { useParams } from "@tanstack/react-router";
|
||||
|
||||
import { ProjectPermissionCan } from "@app/components/permissions";
|
||||
import { PageHeader } from "@app/components/v2";
|
||||
import { ROUTE_PATHS } from "@app/const/routes";
|
||||
import { ProjectPermissionSub } from "@app/context";
|
||||
import { ProjectPermissionPamSessionActions } from "@app/context/ProjectPermissionContext/types";
|
||||
import { useGetPamSessionById } from "@app/hooks/api/pam";
|
||||
|
||||
import { PamSessionDetailsSection } from "./components/PamSessionDetailsSection";
|
||||
import { PamSessionLogsSection } from "./components/PamSessionLogsSection";
|
||||
|
||||
const Page = () => {
|
||||
const sessionId = useParams({
|
||||
from: ROUTE_PATHS.Pam.PamSessionByIDPage.id,
|
||||
select: (el) => el.sessionId
|
||||
});
|
||||
const { data: session } = useGetPamSessionById(sessionId);
|
||||
|
||||
return (
|
||||
<div className="container mx-auto flex flex-col justify-between bg-bunker-800 text-white">
|
||||
{session && (
|
||||
<div className="mx-auto mb-6 w-full max-w-7xl">
|
||||
<PageHeader
|
||||
title={`${session.accountName} Session`}
|
||||
description={`View details for this ${session.accountName} session.`}
|
||||
/>
|
||||
<div className="flex">
|
||||
<div className="mr-4 w-96">
|
||||
<PamSessionDetailsSection session={session} />
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<PamSessionLogsSection session={session} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const PamSessionByIDPage = () => {
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>PAM Session</title>
|
||||
</Helmet>
|
||||
<ProjectPermissionCan
|
||||
I={ProjectPermissionPamSessionActions.Read}
|
||||
a={ProjectPermissionSub.PamSessions}
|
||||
passThrough={false}
|
||||
renderGuardBanner
|
||||
>
|
||||
<Page />
|
||||
</ProjectPermissionCan>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,125 @@
|
||||
import { faBoxOpen, faCheck, faCopy, faUser } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { Badge, IconButton, Tooltip } from "@app/components/v2";
|
||||
import { useTimedReset } from "@app/hooks";
|
||||
import { PAM_RESOURCE_TYPE_MAP, TPamSession } from "@app/hooks/api/pam";
|
||||
|
||||
import { PamSessionStatusBadge } from "../../PamSessionsPage/components/PamSessionStatusBadge";
|
||||
|
||||
type Props = {
|
||||
session: TPamSession;
|
||||
};
|
||||
|
||||
const DetailItem = ({ label, children }: { label: string; children: React.ReactNode }) => (
|
||||
<div className="mb-4">
|
||||
<p className="font-semibold">{label}</p>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
|
||||
export const PamSessionDetailsSection = ({
|
||||
session: {
|
||||
id,
|
||||
accountName,
|
||||
resourceType,
|
||||
resourceName,
|
||||
status,
|
||||
actorName,
|
||||
actorEmail,
|
||||
createdAt,
|
||||
endedAt,
|
||||
actorIp,
|
||||
actorUserAgent,
|
||||
startedAt,
|
||||
expiresAt
|
||||
}
|
||||
}: Props) => {
|
||||
const [copyTextId, isCopyingId, setCopyTextId] = useTimedReset<string>({
|
||||
initialState: "Copy ID to clipboard"
|
||||
});
|
||||
|
||||
const details = PAM_RESOURCE_TYPE_MAP[resourceType];
|
||||
|
||||
return (
|
||||
<div className="max-w-[350px] rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="flex items-center border-b border-mineshaft-400 pb-4">
|
||||
<h3 className="text-lg font-semibold text-mineshaft-100">Session Details</h3>
|
||||
</div>
|
||||
<div className="pt-4 text-sm text-mineshaft-300">
|
||||
<DetailItem label="Session ID">
|
||||
<div className="group flex align-top">
|
||||
<p className="truncate">{id}</p>
|
||||
<div className="opacity-0 transition-opacity duration-300 group-hover:opacity-100">
|
||||
<Tooltip content={copyTextId}>
|
||||
<IconButton
|
||||
ariaLabel="copy icon"
|
||||
variant="plain"
|
||||
className="group relative ml-2"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(id);
|
||||
setCopyTextId("Copied");
|
||||
}}
|
||||
>
|
||||
<FontAwesomeIcon icon={isCopyingId ? faCheck : faCopy} />
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label="Account">
|
||||
<div className="flex items-center gap-2">
|
||||
<img
|
||||
alt={`${details.name} logo`}
|
||||
src={`/images/integrations/${details.image}`}
|
||||
className="size-4"
|
||||
/>
|
||||
<p>{accountName}</p>
|
||||
<Badge className="flex h-5 w-min items-center gap-1.5 whitespace-nowrap bg-yellow/20 text-yellow">
|
||||
<FontAwesomeIcon icon={faBoxOpen} />
|
||||
{resourceName}
|
||||
</Badge>
|
||||
</div>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label="Actor">
|
||||
<div className="flex items-center gap-2">
|
||||
<FontAwesomeIcon icon={faUser} className="-translate-y-px" />
|
||||
<p>
|
||||
<strong>{actorName}</strong> ({actorEmail})
|
||||
</p>
|
||||
</div>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label="Status">
|
||||
<PamSessionStatusBadge status={status} />
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label="IP Address">
|
||||
<p>{actorIp}</p>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label="User Agent">
|
||||
<p className="truncate">{actorUserAgent}</p>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label="Created At">
|
||||
<p>{new Date(createdAt).toLocaleString()}</p>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label="Expires At">
|
||||
<p>{expiresAt ? new Date(expiresAt).toLocaleString() : "Never"}</p>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label="Started At">
|
||||
<p>{startedAt ? new Date(startedAt).toLocaleString() : "Not Started"}</p>
|
||||
</DetailItem>
|
||||
|
||||
<DetailItem label="Ended At">
|
||||
<p>{endedAt ? new Date(endedAt).toLocaleString() : "Ongoing"}</p>
|
||||
</DetailItem>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,37 @@
|
||||
import { faTerminal } from "@fortawesome/free-solid-svg-icons";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
|
||||
import { TPamSession } from "@app/hooks/api/pam";
|
||||
|
||||
type Props = {
|
||||
session: TPamSession;
|
||||
};
|
||||
|
||||
export const PamSessionLogsSection = ({ session }: Props) => {
|
||||
return (
|
||||
<div className="flex h-full flex-col gap-4 rounded-lg border border-mineshaft-600 bg-mineshaft-900 p-4">
|
||||
<div className="flex items-center border-b border-mineshaft-400 pb-4">
|
||||
<h3 className="text-lg font-semibold text-mineshaft-100">Session Logs</h3>
|
||||
</div>
|
||||
<div className="flex grow flex-col gap-4 text-xs">
|
||||
{session.commandLogs.length > 0 ? (
|
||||
session.commandLogs.map((log) => (
|
||||
<div key={log.timestamp} className="flex flex-col">
|
||||
<div className="flex items-center gap-1.5 text-bunker-400">
|
||||
<FontAwesomeIcon icon={faTerminal} className="size-3" />
|
||||
<span>{new Date(log.timestamp).toLocaleString()}</span>
|
||||
</div>
|
||||
|
||||
<div className="whitespace-pre-wrap font-mono">{log.input}</div>
|
||||
<div className="whitespace-pre-wrap font-mono text-bunker-300">{log.output}</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<div className="flex w-full grow items-center justify-center text-bunker-300">
|
||||
No session logs
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
import { createFileRoute, linkOptions } from "@tanstack/react-router";
|
||||
|
||||
import { PamSessionByIDPage } from "./PamSessionByIDPage";
|
||||
|
||||
export const Route = createFileRoute(
|
||||
"/_authenticate/_inject-org-details/_org-layout/projects/pam/$projectId/_pam-layout/sessions/$sessionId"
|
||||
)({
|
||||
component: PamSessionByIDPage,
|
||||
beforeLoad: ({ context, params }) => {
|
||||
return {
|
||||
breadcrumbs: [
|
||||
...context.breadcrumbs,
|
||||
{
|
||||
label: "Sessions",
|
||||
link: linkOptions({
|
||||
to: "/projects/pam/$projectId/sessions",
|
||||
params
|
||||
})
|
||||
},
|
||||
{
|
||||
label: "Details"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user