import { AlertVariant, Button, ButtonVariant, Label, PageSection, Text, TextContent, ToolbarItem, Tooltip, } from "@patternfly/react-core"; import { ExclamationCircleIcon, InfoCircleIcon, WarningTriangleIcon, } from "@patternfly/react-icons"; import type UserRepresentation from "@keycloak/keycloak-admin-client/lib/defs/userRepresentation"; import React, { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link, useHistory, useRouteMatch } from "react-router-dom"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { useAdminClient, useFetch } from "../context/auth/AdminClient"; import { useRealm } from "../context/realm-context/RealmContext"; import { emptyFormatter } from "../util"; import { toUser } from "./routes/User"; import "./user-section.css"; type BruteUser = UserRepresentation & { brute?: Record; }; export const UsersSection = () => { const { t } = useTranslation("users"); const adminClient = useAdminClient(); const { addAlert, addError } = useAlerts(); const { realm: realmName } = useRealm(); const history = useHistory(); const { url } = useRouteMatch(); const [listUsers, setListUsers] = useState(false); const [selectedRows, setSelectedRows] = useState([]); const [key, setKey] = useState(""); const refresh = () => setKey(`${new Date().getTime()}`); useFetch( () => { const testParams = { type: "org.keycloak.storage.UserStorageProvider", }; return adminClient.components.find(testParams); }, (response) => { //should *only* list users when no user federation is configured setListUsers(!(response.length > 0)); refresh(); }, [] ); const UserDetailLink = (user: UserRepresentation) => ( {user.username} ); const loader = async (first?: number, max?: number, search?: string) => { const params: { [name: string]: string | number } = { first: first!, max: max!, }; const searchParam = search || ""; if (searchParam) { params.search = searchParam; } if (!listUsers && !searchParam) { return []; } try { const users = await adminClient.users.find({ ...params }); const realm = await adminClient.realms.findOne({ realm: realmName }); if (realm?.bruteForceProtected) { const brutes = await Promise.all( users.map((user: BruteUser) => adminClient.attackDetection.findOne({ id: user.id!, }) ) ); for (let index = 0; index < users.length; index++) { const user: BruteUser = users[index]; user.brute = brutes[index]; } } return users; } catch (error) { addError("users:noUsersFoundError", error); return []; } }; const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: "users:deleteConfirm", messageKey: t("deleteConfirmDialog", { count: selectedRows.length }), continueButtonLabel: "delete", continueButtonVariant: ButtonVariant.danger, onConfirm: async () => { try { for (const user of selectedRows) { await adminClient.users.del({ id: user.id! }); } setSelectedRows([]); refresh(); addAlert(t("userDeletedSuccess"), AlertVariant.success); } catch (error) { addError("users:userDeletedError", error); } }, }); const StatusRow = (user: BruteUser) => { return ( <> {!user.enabled && ( )} {user.brute?.disabled && ( )} {user.enabled && !user.brute?.disabled && "—"} ); }; const ValidatedEmail = (user: UserRepresentation) => { return ( <> {!user.emailVerified && ( {t("notVerified")}} > )}{" "} {emptyFormatter()(user.email)} ); }; const goToCreate = () => history.push(`${url}/add-user`); return ( <> setSelectedRows([...rows])} emptyState={ !listUsers ? ( {t("searchForUserDescription")} ) : ( ) } toolbarItem={ <> } actions={[ { title: t("common:delete"), onRowClick: (user) => { setSelectedRows([user]); toggleDeleteDialog(); }, }, ]} columns={[ { name: "username", displayKey: "users:username", cellRenderer: UserDetailLink, }, { name: "email", displayKey: "users:email", cellRenderer: ValidatedEmail, }, { name: "lastName", displayKey: "users:lastName", cellFormatters: [emptyFormatter()], }, { name: "firstName", displayKey: "users:firstName", cellFormatters: [emptyFormatter()], }, { name: "status", displayKey: "users:status", cellRenderer: StatusRow, }, ]} /> ); };