import { useState } from "react"; import { useHistory } from "react-router-dom"; import { Link } from "react-router-dom-v5-compat"; import { Trans, useTranslation } from "react-i18next"; import { sortBy } from "lodash-es"; import { AlertVariant, Button, ButtonVariant, Label, PageSection, Tab, TabTitleText, ToolbarItem, } from "@patternfly/react-core"; import type AuthenticationFlowRepresentation from "@keycloak/keycloak-admin-client/lib/defs/authenticationFlowRepresentation"; import { useAdminClient } from "../context/auth/AdminClient"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { useRealm } from "../context/realm-context/RealmContext"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { useAlerts } from "../components/alert/Alerts"; import useToggle from "../utils/useToggle"; import { DuplicateFlowModal } from "./DuplicateFlowModal"; import { toCreateFlow } from "./routes/CreateFlow"; import { toFlow } from "./routes/Flow"; import { RequiredActions } from "./RequiredActions"; import { Policies } from "./policies/Policies"; import helpUrls from "../help-urls"; import { BindFlowDialog } from "./BindFlowDialog"; import { UsedBy } from "./components/UsedBy"; import { routableTab, RoutableTabs, } from "../components/routable-tabs/RoutableTabs"; import { AuthenticationTab, toAuthentication } from "./routes/Authentication"; import { addTrailingSlash } from "../util"; import { getAuthorizationHeaders } from "../utils/getAuthorizationHeaders"; import "./authentication-section.css"; type UsedBy = "SPECIFIC_CLIENTS" | "SPECIFIC_PROVIDERS" | "DEFAULT"; export type AuthenticationType = AuthenticationFlowRepresentation & { usedBy?: { type?: UsedBy; values: string[] }; }; export const REALM_FLOWS = new Map([ ["browserFlow", "browser"], ["registrationFlow", "registration"], ["directGrantFlow", "direct grant"], ["resetCredentialsFlow", "reset credentials"], ["clientAuthenticationFlow", "clients"], ["dockerAuthenticationFlow", "docker auth"], ]); export default function AuthenticationSection() { const { t } = useTranslation("authentication"); const { adminClient } = useAdminClient(); const { realm } = useRealm(); const history = useHistory(); const [key, setKey] = useState(0); const refresh = () => setKey(key + 1); const { addAlert, addError } = useAlerts(); const [selectedFlow, setSelectedFlow] = useState(); const [open, toggleOpen] = useToggle(); const [bindFlowOpen, toggleBindFlow] = useToggle(); const loader = async () => { const flowsRequest = await fetch( `${addTrailingSlash( adminClient.baseUrl )}admin/realms/${realm}/admin-ui-authentication-management/flows`, { method: "GET", headers: getAuthorizationHeaders(await adminClient.getAccessToken()), } ); const flows = await flowsRequest.json(); return sortBy(flows as AuthenticationType[], (flow) => flow.usedBy?.type); }; const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: "authentication:deleteConfirmFlow", children: ( {" "} {{ flow: selectedFlow ? selectedFlow.alias : "" }}. ), continueButtonLabel: "common:delete", continueButtonVariant: ButtonVariant.danger, onConfirm: async () => { try { await adminClient.authenticationManagement.deleteFlow({ flowId: selectedFlow!.id!, }); refresh(); addAlert(t("deleteFlowSuccess"), AlertVariant.success); } catch (error) { addError("authentication:deleteFlowError", error); } }, }); const UsedByRenderer = (authType: AuthenticationType) => ( ); const AliasRenderer = ({ id, alias, usedBy, builtIn, }: AuthenticationType) => ( <> {alias} {" "} {builtIn && } ); const route = (tab: AuthenticationTab) => routableTab({ to: toAuthentication({ realm, tab }), history, }); return ( <> {open && ( { refresh(); toggleOpen(); }} /> )} {bindFlowOpen && ( { toggleBindFlow(); refresh(); }} flowAlias={selectedFlow?.alias!} /> )} {t("flows")}} {...route("flows")} > } actionResolver={({ data }) => [ { title: t("duplicate"), onClick: () => { toggleOpen(); setSelectedFlow(data); }, }, ...(data.usedBy?.type !== "DEFAULT" ? [ { title: t("bindFlow"), onClick: () => { toggleBindFlow(); setSelectedFlow(data); }, }, ] : []), ...(!data.builtIn && !data.usedBy ? [ { title: t("common:delete"), onClick: () => { setSelectedFlow(data); toggleDeleteDialog(); }, }, ] : []), ]} columns={[ { name: "alias", displayKey: "authentication:flowName", cellRenderer: AliasRenderer, }, { name: "usedBy", displayKey: "authentication:usedBy", cellRenderer: UsedByRenderer, }, { name: "description", displayKey: "common:description", }, ]} emptyState={ } /> {t("requiredActions")}} {...route("required-actions")} > {t("policies")}} {...route("policies")} > ); }