2020-12-11 14:48:39 +00:00
|
|
|
import {
|
2021-08-25 14:19:26 +00:00
|
|
|
ActionGroup,
|
2020-12-11 14:48:39 +00:00
|
|
|
Button,
|
2021-09-03 10:50:45 +00:00
|
|
|
Chip,
|
|
|
|
ChipGroup,
|
2021-08-25 14:19:26 +00:00
|
|
|
Dropdown,
|
|
|
|
DropdownToggle,
|
2021-08-26 10:37:56 +00:00
|
|
|
Flex,
|
|
|
|
FlexItem,
|
2021-08-25 14:19:26 +00:00
|
|
|
Form,
|
|
|
|
FormGroup,
|
2020-12-11 14:48:39 +00:00
|
|
|
Modal,
|
|
|
|
ModalVariant,
|
2021-08-25 14:19:26 +00:00
|
|
|
Select,
|
2021-09-03 10:50:45 +00:00
|
|
|
SelectOption,
|
2021-08-25 14:19:26 +00:00
|
|
|
SelectVariant,
|
|
|
|
TextInput,
|
2020-12-11 14:48:39 +00:00
|
|
|
} from "@patternfly/react-core";
|
|
|
|
import {
|
2021-04-15 10:23:36 +00:00
|
|
|
cellWidth,
|
2020-12-11 14:48:39 +00:00
|
|
|
Table,
|
|
|
|
TableBody,
|
|
|
|
TableHeader,
|
|
|
|
TableVariant,
|
|
|
|
} from "@patternfly/react-table";
|
2021-09-09 10:45:19 +00:00
|
|
|
import { CodeEditor, Language } from "@patternfly/react-code-editor";
|
2021-08-26 08:39:35 +00:00
|
|
|
import type AdminEventRepresentation from "@keycloak/keycloak-admin-client/lib/defs/adminEventRepresentation";
|
2021-07-21 09:30:18 +00:00
|
|
|
import moment from "moment";
|
2021-09-09 10:45:19 +00:00
|
|
|
import React, { FunctionComponent, useMemo, useState } from "react";
|
2021-09-03 10:50:45 +00:00
|
|
|
import { Controller, useForm } from "react-hook-form";
|
2021-07-21 09:30:18 +00:00
|
|
|
import { useTranslation } from "react-i18next";
|
2022-02-02 10:33:57 +00:00
|
|
|
import { pickBy } from "lodash-es";
|
2020-12-11 14:48:39 +00:00
|
|
|
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
2021-07-21 09:30:18 +00:00
|
|
|
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
|
|
|
import { useAdminClient } from "../context/auth/AdminClient";
|
|
|
|
import { useRealm } from "../context/realm-context/RealmContext";
|
2021-09-03 10:50:45 +00:00
|
|
|
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
|
2021-10-08 18:21:09 +00:00
|
|
|
import { prettyPrintJSON } from "../util";
|
2022-02-23 14:28:59 +00:00
|
|
|
import { CellResourceLinkRenderer } from "./ResourceLinks";
|
|
|
|
|
2021-08-25 14:19:26 +00:00
|
|
|
import "./events.css";
|
2020-12-11 14:48:39 +00:00
|
|
|
|
|
|
|
type DisplayDialogProps = {
|
|
|
|
titleKey: string;
|
|
|
|
onClose: () => void;
|
|
|
|
};
|
|
|
|
|
2021-08-25 14:19:26 +00:00
|
|
|
type AdminEventSearchForm = {
|
2021-09-03 10:50:45 +00:00
|
|
|
resourceTypes: string[];
|
|
|
|
operationTypes: string[];
|
2021-08-25 14:19:26 +00:00
|
|
|
resourcePath: string;
|
|
|
|
dateFrom: string;
|
|
|
|
dateTo: string;
|
2021-09-03 10:50:45 +00:00
|
|
|
authClient: string;
|
|
|
|
authUser: string;
|
|
|
|
authRealm: string;
|
|
|
|
authIpAddress: string;
|
2021-08-25 14:19:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const defaultValues: AdminEventSearchForm = {
|
2021-09-03 10:50:45 +00:00
|
|
|
resourceTypes: [],
|
|
|
|
operationTypes: [],
|
2021-08-25 14:19:26 +00:00
|
|
|
resourcePath: "",
|
|
|
|
dateFrom: "",
|
|
|
|
dateTo: "",
|
2021-09-03 10:50:45 +00:00
|
|
|
authClient: "",
|
|
|
|
authUser: "",
|
|
|
|
authRealm: "",
|
|
|
|
authIpAddress: "",
|
2021-08-25 14:19:26 +00:00
|
|
|
};
|
|
|
|
|
2021-08-10 11:18:48 +00:00
|
|
|
const DisplayDialog: FunctionComponent<DisplayDialogProps> = ({
|
|
|
|
titleKey,
|
|
|
|
onClose,
|
|
|
|
children,
|
|
|
|
}) => {
|
2020-12-11 14:48:39 +00:00
|
|
|
const { t } = useTranslation("events");
|
|
|
|
return (
|
|
|
|
<Modal
|
|
|
|
variant={ModalVariant.medium}
|
|
|
|
title={t(titleKey)}
|
|
|
|
isOpen={true}
|
|
|
|
onClose={onClose}
|
|
|
|
>
|
|
|
|
{children}
|
|
|
|
</Modal>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export const AdminEvents = () => {
|
|
|
|
const { t } = useTranslation("events");
|
|
|
|
const adminClient = useAdminClient();
|
2021-07-21 09:30:18 +00:00
|
|
|
const { realm } = useRealm();
|
2021-09-03 10:50:45 +00:00
|
|
|
const serverInfo = useServerInfo();
|
|
|
|
const resourceTypes = serverInfo.enums?.["resourceType"];
|
|
|
|
const operationTypes = serverInfo.enums?.["operationType"];
|
2020-12-11 14:48:39 +00:00
|
|
|
|
2021-04-15 10:23:36 +00:00
|
|
|
const [key, setKey] = useState(0);
|
2021-08-25 14:19:26 +00:00
|
|
|
const [searchDropdownOpen, setSearchDropdownOpen] = useState(false);
|
2021-09-03 10:50:45 +00:00
|
|
|
const [selectResourceTypesOpen, setSelectResourceTypesOpen] = useState(false);
|
|
|
|
const [selectOperationTypesOpen, setSelectOperationTypesOpen] =
|
|
|
|
useState(false);
|
|
|
|
const [activeFilters, setActiveFilters] = useState<
|
|
|
|
Partial<AdminEventSearchForm>
|
|
|
|
>({});
|
2020-12-11 14:48:39 +00:00
|
|
|
|
|
|
|
const [authEvent, setAuthEvent] = useState<AdminEventRepresentation>();
|
2021-07-05 11:24:10 +00:00
|
|
|
const [representationEvent, setRepresentationEvent] =
|
|
|
|
useState<AdminEventRepresentation>();
|
2020-12-11 14:48:39 +00:00
|
|
|
|
2021-09-03 10:50:45 +00:00
|
|
|
const filterLabels: Record<keyof AdminEventSearchForm, string> = {
|
|
|
|
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"),
|
|
|
|
};
|
|
|
|
|
2021-08-25 14:19:26 +00:00
|
|
|
const {
|
2021-09-03 10:50:45 +00:00
|
|
|
getValues,
|
2021-08-25 14:19:26 +00:00
|
|
|
register,
|
2021-09-03 10:50:45 +00:00
|
|
|
reset,
|
2021-08-25 14:19:26 +00:00
|
|
|
formState: { isDirty },
|
2021-09-03 10:50:45 +00:00
|
|
|
control,
|
2021-08-25 14:19:26 +00:00
|
|
|
} = useForm<AdminEventSearchForm>({
|
|
|
|
shouldUnregister: false,
|
|
|
|
mode: "onChange",
|
|
|
|
defaultValues,
|
|
|
|
});
|
|
|
|
|
2021-09-03 10:50:45 +00:00
|
|
|
function loader(first?: number, max?: number) {
|
|
|
|
return adminClient.realms.findAdminEvents({
|
|
|
|
// 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),
|
2020-12-11 14:48:39 +00:00
|
|
|
realm,
|
2021-09-03 10:50:45 +00:00
|
|
|
first,
|
|
|
|
max,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function submitSearch() {
|
|
|
|
setSearchDropdownOpen(false);
|
|
|
|
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<AdminEventSearchForm> = pickBy(
|
|
|
|
getValues(),
|
|
|
|
(value) => value !== "" || (Array.isArray(value) && value.length > 0)
|
|
|
|
);
|
|
|
|
|
|
|
|
setActiveFilters(newFilters);
|
|
|
|
setKey(key + 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
function refresh() {
|
|
|
|
commitFilters();
|
|
|
|
}
|
2020-12-11 14:48:39 +00:00
|
|
|
|
2021-08-25 14:19:26 +00:00
|
|
|
const adminEventSearchFormDisplay = () => {
|
|
|
|
return (
|
2021-08-26 12:15:28 +00:00
|
|
|
<Flex
|
|
|
|
direction={{ default: "column" }}
|
|
|
|
spaceItems={{ default: "spaceItemsNone" }}
|
|
|
|
>
|
|
|
|
<FlexItem>
|
|
|
|
<Dropdown
|
|
|
|
id="admin-events-search-select"
|
|
|
|
data-testid="AdminEventsSearchSelector"
|
|
|
|
className="pf-u-ml-md"
|
|
|
|
toggle={
|
|
|
|
<DropdownToggle
|
|
|
|
data-testid="adminEventsSearchSelectorToggle"
|
|
|
|
onToggle={(isOpen) => setSearchDropdownOpen(isOpen)}
|
|
|
|
className="keycloak__events_search_selector_dropdown__toggle"
|
|
|
|
>
|
|
|
|
{t("searchForAdminEvent")}
|
|
|
|
</DropdownToggle>
|
|
|
|
}
|
|
|
|
isOpen={searchDropdownOpen}
|
|
|
|
>
|
|
|
|
<Form
|
|
|
|
isHorizontal
|
2021-09-03 10:50:45 +00:00
|
|
|
className="keycloak__events_search__form"
|
2021-08-26 12:15:28 +00:00
|
|
|
data-testid="searchForm"
|
2021-08-25 14:19:26 +00:00
|
|
|
>
|
2021-08-26 12:15:28 +00:00
|
|
|
<FormGroup
|
2021-09-03 10:50:45 +00:00
|
|
|
label={t("resourceTypes")}
|
|
|
|
fieldId="kc-resourceTypes"
|
|
|
|
className="keycloak__events_search__form_label"
|
2021-08-26 09:15:59 +00:00
|
|
|
>
|
2021-09-03 10:50:45 +00:00
|
|
|
<Controller
|
|
|
|
name="resourceTypes"
|
|
|
|
control={control}
|
|
|
|
render={({
|
|
|
|
onChange,
|
|
|
|
value,
|
|
|
|
}: {
|
|
|
|
onChange: (newValue: string[]) => void;
|
|
|
|
value: string[];
|
|
|
|
}) => (
|
|
|
|
<Select
|
|
|
|
className="keycloak__events_search__type_select"
|
|
|
|
name="resourceTypes"
|
|
|
|
data-testid="resource-types-searchField"
|
|
|
|
chipGroupProps={{
|
|
|
|
numChips: 1,
|
2021-10-26 20:16:19 +00:00
|
|
|
expandedText: t("common:hide"),
|
|
|
|
collapsedText: t("common:showRemaining"),
|
2021-09-03 10:50:45 +00:00
|
|
|
}}
|
|
|
|
variant={SelectVariant.typeaheadMulti}
|
|
|
|
typeAheadAriaLabel="Select"
|
|
|
|
onToggle={(isOpen) => setSelectResourceTypesOpen(isOpen)}
|
|
|
|
selections={value}
|
|
|
|
onSelect={(_, selectedValue) => {
|
|
|
|
const option = selectedValue.toString();
|
|
|
|
const changedValue = value.includes(option)
|
|
|
|
? value.filter((item) => item !== option)
|
|
|
|
: [...value, option];
|
|
|
|
|
|
|
|
onChange(changedValue);
|
|
|
|
}}
|
|
|
|
onClear={(resource) => {
|
|
|
|
resource.stopPropagation();
|
|
|
|
onChange([]);
|
|
|
|
}}
|
|
|
|
isOpen={selectResourceTypesOpen}
|
|
|
|
aria-labelledby={"resourceTypes"}
|
|
|
|
chipGroupComponent={
|
|
|
|
<ChipGroup>
|
|
|
|
{value.map((chip) => (
|
|
|
|
<Chip
|
|
|
|
key={chip}
|
|
|
|
onClick={(resource) => {
|
|
|
|
resource.stopPropagation();
|
|
|
|
onChange(value.filter((val) => val !== chip));
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{chip}
|
|
|
|
</Chip>
|
|
|
|
))}
|
|
|
|
</ChipGroup>
|
|
|
|
}
|
|
|
|
>
|
|
|
|
{resourceTypes?.map((option) => (
|
|
|
|
<SelectOption key={option} value={option} />
|
|
|
|
))}
|
|
|
|
</Select>
|
|
|
|
)}
|
|
|
|
/>
|
2021-08-26 12:15:28 +00:00
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
2021-09-03 10:50:45 +00:00
|
|
|
label={t("operationTypes")}
|
|
|
|
fieldId="kc-operationTypes"
|
|
|
|
className="keycloak__events_search__form_label"
|
2021-08-26 12:15:28 +00:00
|
|
|
>
|
2021-09-03 10:50:45 +00:00
|
|
|
<Controller
|
|
|
|
name="operationTypes"
|
|
|
|
control={control}
|
|
|
|
render={({
|
|
|
|
onChange,
|
|
|
|
value,
|
|
|
|
}: {
|
|
|
|
onChange: (newValue: string[]) => void;
|
|
|
|
value: string[];
|
|
|
|
}) => (
|
|
|
|
<Select
|
|
|
|
className="keycloak__events_search__type_select"
|
|
|
|
name="operationTypes"
|
|
|
|
data-testid="operation-types-searchField"
|
|
|
|
chipGroupProps={{
|
|
|
|
numChips: 1,
|
2021-10-26 20:16:19 +00:00
|
|
|
expandedText: t("common:hide"),
|
|
|
|
collapsedText: t("common:showRemaining"),
|
2021-09-03 10:50:45 +00:00
|
|
|
}}
|
|
|
|
variant={SelectVariant.typeaheadMulti}
|
|
|
|
typeAheadAriaLabel="Select"
|
|
|
|
onToggle={(isOpen) => setSelectOperationTypesOpen(isOpen)}
|
|
|
|
selections={value}
|
|
|
|
onSelect={(_, selectedValue) => {
|
|
|
|
const option = selectedValue.toString();
|
|
|
|
const changedValue = value.includes(option)
|
|
|
|
? value.filter((item) => item !== option)
|
|
|
|
: [...value, option];
|
|
|
|
|
|
|
|
onChange(changedValue);
|
|
|
|
}}
|
|
|
|
onClear={(operation) => {
|
|
|
|
operation.stopPropagation();
|
|
|
|
onChange([]);
|
|
|
|
}}
|
|
|
|
isOpen={selectOperationTypesOpen}
|
|
|
|
aria-labelledby={"operationTypes"}
|
|
|
|
chipGroupComponent={
|
|
|
|
<ChipGroup>
|
|
|
|
{value.map((chip) => (
|
|
|
|
<Chip
|
|
|
|
key={chip}
|
|
|
|
onClick={(operation) => {
|
|
|
|
operation.stopPropagation();
|
|
|
|
onChange(value.filter((val) => val !== chip));
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
{chip}
|
|
|
|
</Chip>
|
|
|
|
))}
|
|
|
|
</ChipGroup>
|
|
|
|
}
|
|
|
|
>
|
|
|
|
{operationTypes?.map((option) => (
|
|
|
|
<SelectOption key={option} value={option} />
|
|
|
|
))}
|
|
|
|
</Select>
|
|
|
|
)}
|
|
|
|
/>
|
2021-08-26 12:15:28 +00:00
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
2021-09-03 10:50:45 +00:00
|
|
|
label={t("resourcePath")}
|
|
|
|
fieldId="kc-resourcePath"
|
2021-08-26 12:15:28 +00:00
|
|
|
className="keycloak__events_search__form_label"
|
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
ref={register()}
|
|
|
|
type="text"
|
2021-09-03 10:50:45 +00:00
|
|
|
id="kc-resourcePath"
|
|
|
|
name="resourcePath"
|
|
|
|
data-testid="resourcePath-searchField"
|
2021-08-26 12:15:28 +00:00
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("realm")}
|
|
|
|
fieldId="kc-realm"
|
|
|
|
className="keycloak__events_search__form_label"
|
|
|
|
>
|
2021-09-03 10:50:45 +00:00
|
|
|
<TextInput
|
|
|
|
ref={register()}
|
|
|
|
type="text"
|
|
|
|
id="kc-realm"
|
|
|
|
name="authRealm"
|
|
|
|
data-testid="realm-searchField"
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("client")}
|
|
|
|
fieldId="kc-client"
|
|
|
|
className="keycloak__events_search__form_label"
|
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
ref={register()}
|
|
|
|
type="text"
|
|
|
|
id="kc-client"
|
|
|
|
name="authClient"
|
|
|
|
data-testid="client-searchField"
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("user")}
|
|
|
|
fieldId="kc-user"
|
|
|
|
className="keycloak__events_search__form_label"
|
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
ref={register()}
|
|
|
|
type="text"
|
|
|
|
id="kc-user"
|
|
|
|
name="authUser"
|
|
|
|
data-testid="user-searchField"
|
|
|
|
/>
|
2021-08-26 12:15:28 +00:00
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("ipAddress")}
|
|
|
|
fieldId="kc-ipAddress"
|
|
|
|
className="keycloak__events_search__form_label"
|
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
ref={register()}
|
|
|
|
type="text"
|
|
|
|
id="kc-ipAddress"
|
2021-09-03 10:50:45 +00:00
|
|
|
name="authIpAddress"
|
2021-08-26 12:15:28 +00:00
|
|
|
data-testid="ipAddress-searchField"
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("dateFrom")}
|
|
|
|
fieldId="kc-dateFrom"
|
|
|
|
className="keycloak__events_search__form_label"
|
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
ref={register()}
|
|
|
|
type="text"
|
|
|
|
id="kc-dateFrom"
|
|
|
|
name="dateFrom"
|
|
|
|
className="pf-c-form-control pf-m-icon pf-m-calendar"
|
|
|
|
placeholder="yyyy-MM-dd"
|
|
|
|
data-testid="dateFrom-searchField"
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<FormGroup
|
|
|
|
label={t("dateTo")}
|
|
|
|
fieldId="kc-dateTo"
|
|
|
|
className="keycloak__events_search__form_label"
|
|
|
|
>
|
|
|
|
<TextInput
|
|
|
|
ref={register()}
|
|
|
|
type="text"
|
|
|
|
id="kc-dateTo"
|
|
|
|
name="dateTo"
|
|
|
|
className="pf-c-form-control pf-m-icon pf-m-calendar"
|
|
|
|
placeholder="yyyy-MM-dd"
|
|
|
|
data-testid="dateTo-searchField"
|
|
|
|
/>
|
|
|
|
</FormGroup>
|
|
|
|
<ActionGroup>
|
|
|
|
<Button
|
2021-09-03 10:50:45 +00:00
|
|
|
className="keycloak__user_events_search__form_btn"
|
2021-08-26 12:15:28 +00:00
|
|
|
variant={"primary"}
|
2021-09-03 10:50:45 +00:00
|
|
|
onClick={submitSearch}
|
2021-08-26 12:15:28 +00:00
|
|
|
data-testid="search-events-btn"
|
|
|
|
isDisabled={!isDirty}
|
2021-08-26 10:37:56 +00:00
|
|
|
>
|
2021-08-26 12:15:28 +00:00
|
|
|
{t("searchAdminEventsBtn")}
|
|
|
|
</Button>
|
|
|
|
</ActionGroup>
|
|
|
|
</Form>
|
|
|
|
</Dropdown>
|
|
|
|
<Button
|
|
|
|
className="pf-u-ml-md"
|
|
|
|
onClick={refresh}
|
|
|
|
data-testid="refresh-btn"
|
|
|
|
>
|
|
|
|
{t("refresh")}
|
|
|
|
</Button>
|
|
|
|
</FlexItem>
|
2021-09-03 10:50:45 +00:00
|
|
|
<FlexItem>
|
|
|
|
{Object.entries(activeFilters).length > 0 && (
|
|
|
|
<div className="keycloak__searchChips pf-u-ml-md">
|
|
|
|
{Object.entries(activeFilters).map((filter) => {
|
|
|
|
const [key, value] = filter as [
|
|
|
|
keyof AdminEventSearchForm,
|
|
|
|
string | string[]
|
|
|
|
];
|
|
|
|
|
|
|
|
return (
|
|
|
|
<ChipGroup
|
|
|
|
className="pf-u-mt-md pf-u-mr-md"
|
|
|
|
key={key}
|
|
|
|
categoryName={filterLabels[key]}
|
|
|
|
isClosable
|
|
|
|
onClick={() => removeFilter(key)}
|
|
|
|
>
|
|
|
|
{typeof value === "string" ? (
|
|
|
|
<Chip isReadOnly>{value}</Chip>
|
|
|
|
) : (
|
|
|
|
value.map((entry) => (
|
|
|
|
<Chip
|
|
|
|
key={entry}
|
|
|
|
onClick={() => removeFilterValue(key, entry)}
|
|
|
|
>
|
|
|
|
{entry}
|
|
|
|
</Chip>
|
|
|
|
))
|
|
|
|
)}
|
|
|
|
</ChipGroup>
|
|
|
|
);
|
|
|
|
})}
|
|
|
|
</div>
|
|
|
|
)}
|
|
|
|
</FlexItem>
|
2021-08-26 12:15:28 +00:00
|
|
|
</Flex>
|
2021-08-25 14:19:26 +00:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2021-09-07 11:38:36 +00:00
|
|
|
const rows = [
|
|
|
|
[t("realm"), authEvent?.authDetails?.realmId],
|
|
|
|
[t("client"), authEvent?.authDetails?.clientId],
|
|
|
|
[t("user"), authEvent?.authDetails?.userId],
|
|
|
|
[t("ipAddress"), authEvent?.authDetails?.ipAddress],
|
|
|
|
];
|
|
|
|
|
2021-09-09 10:45:19 +00:00
|
|
|
const code = useMemo(
|
|
|
|
() =>
|
2021-10-08 18:21:09 +00:00
|
|
|
representationEvent?.representation
|
|
|
|
? prettyPrintJSON(JSON.parse(representationEvent.representation))
|
|
|
|
: "",
|
2021-09-09 10:45:19 +00:00
|
|
|
[representationEvent?.representation]
|
|
|
|
);
|
|
|
|
|
2020-12-11 14:48:39 +00:00
|
|
|
return (
|
|
|
|
<>
|
|
|
|
{authEvent && (
|
|
|
|
<DisplayDialog titleKey="auth" onClose={() => setAuthEvent(undefined)}>
|
|
|
|
<Table
|
|
|
|
aria-label="authData"
|
2021-09-07 11:38:36 +00:00
|
|
|
data-testid="auth-dialog"
|
2020-12-11 14:48:39 +00:00
|
|
|
variant={TableVariant.compact}
|
|
|
|
cells={[t("attribute"), t("value")]}
|
2021-09-07 11:38:36 +00:00
|
|
|
rows={rows}
|
2020-12-11 14:48:39 +00:00
|
|
|
>
|
|
|
|
<TableHeader />
|
|
|
|
<TableBody />
|
|
|
|
</Table>
|
|
|
|
</DisplayDialog>
|
|
|
|
)}
|
|
|
|
{representationEvent && (
|
|
|
|
<DisplayDialog
|
|
|
|
titleKey="representation"
|
2021-09-07 11:38:36 +00:00
|
|
|
data-testid="representation-dialog"
|
2020-12-11 14:48:39 +00:00
|
|
|
onClose={() => setRepresentationEvent(undefined)}
|
|
|
|
>
|
2021-09-09 10:45:19 +00:00
|
|
|
<CodeEditor
|
|
|
|
isLineNumbersVisible
|
|
|
|
isReadOnly
|
|
|
|
code={code}
|
|
|
|
language={Language.json}
|
2021-09-20 12:42:07 +00:00
|
|
|
height="8rem"
|
2021-09-09 10:45:19 +00:00
|
|
|
/>
|
2020-12-11 14:48:39 +00:00
|
|
|
</DisplayDialog>
|
|
|
|
)}
|
2020-12-14 08:57:05 +00:00
|
|
|
<KeycloakDataTable
|
2020-12-11 14:48:39 +00:00
|
|
|
key={key}
|
|
|
|
loader={loader}
|
|
|
|
isPaginated
|
2020-12-11 17:25:54 +00:00
|
|
|
ariaLabelKey="events:adminEvents"
|
2021-08-25 14:19:26 +00:00
|
|
|
toolbarItem={adminEventSearchFormDisplay()}
|
2020-12-11 14:48:39 +00:00
|
|
|
actions={[
|
|
|
|
{
|
|
|
|
title: t("auth"),
|
|
|
|
onRowClick: (event) => setAuthEvent(event),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
title: t("representation"),
|
|
|
|
onRowClick: (event) => setRepresentationEvent(event),
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
columns={[
|
|
|
|
{
|
|
|
|
name: "time",
|
|
|
|
displayKey: "events:time",
|
2021-04-20 12:28:21 +00:00
|
|
|
cellRenderer: (row) => moment(row.time).format("LLL"),
|
2020-12-11 14:48:39 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "resourcePath",
|
|
|
|
displayKey: "events:resourcePath",
|
2022-02-23 14:28:59 +00:00
|
|
|
cellRenderer: CellResourceLinkRenderer,
|
2020-12-11 14:48:39 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "resourceType",
|
|
|
|
displayKey: "events:resourceType",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "operationType",
|
|
|
|
displayKey: "events:operationType",
|
2021-04-15 10:23:36 +00:00
|
|
|
transforms: [cellWidth(10)],
|
2020-12-11 14:48:39 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "",
|
|
|
|
displayKey: "events:user",
|
|
|
|
cellRenderer: (event) => event.authDetails?.userId,
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
emptyState={
|
|
|
|
<ListEmptyState
|
|
|
|
message={t("emptyEvents")}
|
|
|
|
instructions={t("emptyEventsInstructions")}
|
|
|
|
/>
|
|
|
|
}
|
2021-12-07 16:27:48 +00:00
|
|
|
isSearching={Object.keys(activeFilters).length > 0}
|
2020-12-11 14:48:39 +00:00
|
|
|
/>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
};
|