/* eslint-disable */ // @ts-nocheck import type AdminEventRepresentation from "@keycloak/keycloak-admin-client/lib/defs/adminEventRepresentation"; import { Action, KeycloakDataTable, KeycloakSelect, ListEmptyState, SelectVariant, TextControl, useFetch, } from "../../shared/keycloak-ui-shared"; import { ActionGroup, Button, Chip, ChipGroup, DatePicker, DescriptionList, DescriptionListDescription, DescriptionListGroup, DescriptionListTerm, Flex, FlexItem, Form, FormGroup, Modal, ModalVariant, SelectOption, } from "../../shared/@patternfly/react-core"; import { Table, TableVariant, Tbody, Td, Th, Thead, Tr, cellWidth, } from "../../shared/@patternfly/react-table"; import { pickBy } from "lodash-es"; import { PropsWithChildren, useMemo, useState } from "react"; import { Controller, FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useAdminClient } from "../admin-client"; import { EventsBanners } from "../Banners"; import DropdownPanel from "../components/dropdown-panel/DropdownPanel"; import CodeEditor from "../components/form/CodeEditor"; import { useRealm } from "../context/realm-context/RealmContext"; import { useServerInfo } from "../context/server-info/ServerInfoProvider"; import { prettyPrintJSON } from "../util"; import useFormatDate, { FORMAT_DATE_AND_TIME } from "../utils/useFormatDate"; import { CellResourceLinkRenderer } from "./ResourceLinks"; import "./events.css"; type DisplayDialogProps = { titleKey: string; onClose: () => void; }; type AdminEventSearchForm = { resourceTypes: string[]; operationTypes: string[]; resourcePath: string; dateFrom: string; dateTo: string; authClient: string; authUser: string; authRealm: string; authIpAddress: string; }; const DisplayDialog = ({ titleKey, onClose, children, }: PropsWithChildren) => { const { t } = useTranslation(); return ( {children} ); }; const DetailCell = (event: AdminEventRepresentation) => ( {event.details && Object.entries(event.details).map(([key, value]) => ( {key} {value} ))} {event.error && ( error {event.error} )} ); type AdminEventsProps = { resourcePath?: string; }; export const AdminEvents = ({ resourcePath }: AdminEventsProps) => { const { adminClient } = useAdminClient(); const { t } = useTranslation(); const { realm } = useRealm(); const serverInfo = useServerInfo(); const formatDate = useFormatDate(); const resourceTypes = serverInfo.enums?.["resourceType"]; const operationTypes = serverInfo.enums?.["operationType"]; const [key, setKey] = useState(0); const [searchDropdownOpen, setSearchDropdownOpen] = useState(false); const [selectResourceTypesOpen, setSelectResourceTypesOpen] = useState(false); const [selectOperationTypesOpen, setSelectOperationTypesOpen] = useState(false); const [activeFilters, setActiveFilters] = useState< Partial >({}); const defaultValues: AdminEventSearchForm = { resourceTypes: [], operationTypes: [], resourcePath: resourcePath ? resourcePath : "", dateFrom: "", dateTo: "", authClient: "", authUser: "", authRealm: "", authIpAddress: "", }; const [authEvent, setAuthEvent] = useState(); const [adminEventsEnabled, setAdminEventsEnabled] = useState(); const [representationEvent, setRepresentationEvent] = useState(); const filterLabels: Record = { resourceTypes: t("resourceTypes"), operationTypes: t("operationTypes"), resourcePath: t("resourcePath"), dateFrom: t("dateFrom"), dateTo: t("dateTo"), authClient: t("client"), authUser: t("userId"), authRealm: t("realm"), authIpAddress: t("ipAddress"), }; const form = useForm({ mode: "onChange", defaultValues, }); const { getValues, reset, formState: { isDirty }, control, } = form; useFetch( () => adminClient.realms.getConfigEvents({ realm }), (events) => { setAdminEventsEnabled(events.adminEventsEnabled!); }, [], ); function loader(first?: number, max?: number) { return adminClient.realms.findAdminEvents({ resourcePath, // The admin client wants 'dateFrom' and 'dateTo' to be Date objects, however it cannot actually handle them so we need to cast to any. ...(activeFilters as any), realm, first, max, }); } function submitSearch() { setSearchDropdownOpen(false); commitFilters(); } function resetSearch() { reset(); commitFilters(); } function removeFilter(key: keyof AdminEventSearchForm) { const formValues: AdminEventSearchForm = { ...getValues() }; delete formValues[key]; reset({ ...defaultValues, ...formValues }); commitFilters(); } function removeFilterValue( key: keyof AdminEventSearchForm, valueToRemove: string, ) { const formValues = getValues(); const fieldValue = formValues[key]; const newFieldValue = Array.isArray(fieldValue) ? fieldValue.filter((val) => val !== valueToRemove) : fieldValue; reset({ ...formValues, [key]: newFieldValue }); commitFilters(); } function commitFilters() { const newFilters: Partial = pickBy( getValues(), (value) => value !== "" || (Array.isArray(value) && value.length > 0), ); if (resourcePath) { delete newFilters.resourcePath; } setActiveFilters(newFilters); setKey(key + 1); } const code = useMemo( () => representationEvent?.representation ? prettyPrintJSON(JSON.parse(representationEvent.representation)) : "", [representationEvent?.representation], ); return ( <> {authEvent && ( setAuthEvent(undefined)}>
{t("attribute")} {t("value")}
{t("realm")} {authEvent.authDetails?.realmId}
{t("client")} {authEvent.authDetails?.clientId}
{t("user")} {authEvent.authDetails?.userId}
{t("ipAddress")} {authEvent.authDetails?.ipAddress}
)} {representationEvent && ( setRepresentationEvent(undefined)} > )} {!adminEventsEnabled && } event.details !== undefined, cellRenderer: DetailCell, }, ]} isPaginated ariaLabelKey="adminEvents" toolbarItem={
( setSelectResourceTypesOpen(isOpen) } selections={field.value} onSelect={(selectedValue) => { const option = selectedValue.toString(); const changedValue = field.value.includes(option) ? field.value.filter( (item: string) => item !== option, ) : [...field.value, option]; field.onChange(changedValue); }} onClear={() => { field.onChange([]); }} isOpen={selectResourceTypesOpen} aria-labelledby={"resourceTypes"} chipGroupComponent={ {field.value.map((chip: string) => ( { resource.stopPropagation(); field.onChange( field.value.filter( (val: string) => val !== chip, ), ); }} > {chip} ))} } > {resourceTypes?.map((option) => ( {option} ))} )} /> ( setSelectOperationTypesOpen(isOpen) } selections={field.value} onSelect={(selectedValue) => { const option = selectedValue.toString(); const changedValue = field.value.includes(option) ? field.value.filter( (item: string) => item !== option, ) : [...field.value, option]; field.onChange(changedValue); }} onClear={() => { field.onChange([]); }} isOpen={selectOperationTypesOpen} aria-labelledby={"operationTypes"} chipGroupComponent={ {field.value.map((chip: string) => ( { operation.stopPropagation(); field.onChange( field.value.filter( (val: string) => val !== chip, ), ); }} > {chip} ))} } > {operationTypes?.map((option) => ( {option} ))} )} /> {!resourcePath && ( )} ( field.onChange(value)} inputProps={{ id: "kc-dateFrom" }} /> )} /> ( field.onChange(value)} inputProps={{ id: "kc-dateTo" }} /> )} />
{Object.entries(activeFilters).length > 0 && (
{Object.entries(activeFilters).map((filter) => { const [key, value] = filter as [ keyof AdminEventSearchForm, string | string[], ]; if (key === "resourcePath" && !!resourcePath) { return null; } return ( removeFilter(key)} > {typeof value === "string" ? ( {value} ) : ( value.map((entry) => ( removeFilterValue(key, entry)} > {entry} )) )} ); })}
)}
} actions={ [ { title: t("auth"), onRowClick: (event) => setAuthEvent(event), }, { title: t("representation"), onRowClick: (event) => setRepresentationEvent(event), }, ] as Action[] } columns={[ { name: "time", displayKey: "time", cellRenderer: (row) => formatDate(new Date(row.time!), FORMAT_DATE_AND_TIME), }, { name: "resourcePath", displayKey: "resourcePath", cellRenderer: CellResourceLinkRenderer, }, { name: "resourceType", displayKey: "resourceType", }, { name: "operationType", displayKey: "operationType", transforms: [cellWidth(10)], }, { name: "", displayKey: "user", cellRenderer: (event) => event.authDetails?.userId || "", }, ]} emptyState={ setKey(key + 1)} /> } isSearching={Object.keys(activeFilters).length > 0} /> ); };