2022-08-03 12:12:07 +00:00
|
|
|
import { useState } from "react";
|
2023-01-18 12:09:49 +00:00
|
|
|
import { Link } from "react-router-dom";
|
2021-01-26 19:39:01 +00:00
|
|
|
import { Trans, useTranslation } from "react-i18next";
|
2022-03-07 17:36:52 +00:00
|
|
|
import { sortBy } from "lodash-es";
|
2021-01-26 19:39:01 +00:00
|
|
|
import {
|
|
|
|
AlertVariant,
|
|
|
|
Button,
|
|
|
|
ButtonVariant,
|
|
|
|
Label,
|
|
|
|
PageSection,
|
|
|
|
Tab,
|
|
|
|
TabTitleText,
|
2021-08-09 08:47:34 +00:00
|
|
|
ToolbarItem,
|
2021-01-26 19:39:01 +00:00
|
|
|
} from "@patternfly/react-core";
|
2020-09-09 09:07:17 +00:00
|
|
|
|
2021-08-26 08:39:35 +00:00
|
|
|
import type AuthenticationFlowRepresentation from "@keycloak/keycloak-admin-client/lib/defs/authenticationFlowRepresentation";
|
2021-01-26 19:39:01 +00:00
|
|
|
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";
|
2021-11-30 13:07:44 +00:00
|
|
|
import useToggle from "../utils/useToggle";
|
2021-01-26 19:39:01 +00:00
|
|
|
import { DuplicateFlowModal } from "./DuplicateFlowModal";
|
2021-08-09 08:47:34 +00:00
|
|
|
import { toCreateFlow } from "./routes/CreateFlow";
|
|
|
|
import { toFlow } from "./routes/Flow";
|
2021-10-19 15:30:57 +00:00
|
|
|
import { RequiredActions } from "./RequiredActions";
|
2021-12-01 09:24:46 +00:00
|
|
|
import { Policies } from "./policies/Policies";
|
2022-03-07 17:36:52 +00:00
|
|
|
import helpUrls from "../help-urls";
|
|
|
|
import { BindFlowDialog } from "./BindFlowDialog";
|
2022-05-02 14:51:05 +00:00
|
|
|
import { UsedBy } from "./components/UsedBy";
|
2022-06-22 11:35:10 +00:00
|
|
|
import {
|
|
|
|
RoutableTabs,
|
2023-01-04 09:57:25 +00:00
|
|
|
useRoutableTab,
|
2022-06-22 11:35:10 +00:00
|
|
|
} from "../components/routable-tabs/RoutableTabs";
|
|
|
|
import { AuthenticationTab, toAuthentication } from "./routes/Authentication";
|
2022-09-13 12:36:41 +00:00
|
|
|
import { addTrailingSlash } from "../util";
|
|
|
|
import { getAuthorizationHeaders } from "../utils/getAuthorizationHeaders";
|
2022-12-08 09:30:44 +00:00
|
|
|
import useLocaleSort, { mapByKey } from "../utils/useLocaleSort";
|
2021-01-26 19:39:01 +00:00
|
|
|
|
|
|
|
import "./authentication-section.css";
|
|
|
|
|
2022-09-13 12:36:41 +00:00
|
|
|
type UsedBy = "SPECIFIC_CLIENTS" | "SPECIFIC_PROVIDERS" | "DEFAULT";
|
2021-01-26 19:39:01 +00:00
|
|
|
|
2022-05-02 14:51:05 +00:00
|
|
|
export type AuthenticationType = AuthenticationFlowRepresentation & {
|
2022-09-13 12:36:41 +00:00
|
|
|
usedBy?: { type?: UsedBy; values: string[] };
|
2021-01-26 19:39:01 +00:00
|
|
|
};
|
|
|
|
|
2022-09-13 12:36:41 +00:00
|
|
|
export const REALM_FLOWS = new Map<string, string>([
|
|
|
|
["browserFlow", "browser"],
|
|
|
|
["registrationFlow", "registration"],
|
|
|
|
["directGrantFlow", "direct grant"],
|
|
|
|
["resetCredentialsFlow", "reset credentials"],
|
|
|
|
["clientAuthenticationFlow", "clients"],
|
|
|
|
["dockerAuthenticationFlow", "docker auth"],
|
|
|
|
]);
|
2021-01-26 19:39:01 +00:00
|
|
|
|
2021-10-29 16:11:06 +00:00
|
|
|
export default function AuthenticationSection() {
|
2021-01-26 19:39:01 +00:00
|
|
|
const { t } = useTranslation("authentication");
|
2022-07-14 13:02:28 +00:00
|
|
|
const { adminClient } = useAdminClient();
|
2021-01-26 19:39:01 +00:00
|
|
|
const { realm } = useRealm();
|
|
|
|
const [key, setKey] = useState(0);
|
2022-03-07 17:36:52 +00:00
|
|
|
const refresh = () => setKey(key + 1);
|
2021-07-28 12:01:42 +00:00
|
|
|
const { addAlert, addError } = useAlerts();
|
2022-12-08 09:30:44 +00:00
|
|
|
const localeSort = useLocaleSort();
|
2021-01-26 19:39:01 +00:00
|
|
|
const [selectedFlow, setSelectedFlow] = useState<AuthenticationType>();
|
2022-03-07 17:36:52 +00:00
|
|
|
const [open, toggleOpen] = useToggle();
|
|
|
|
const [bindFlowOpen, toggleBindFlow] = useToggle();
|
2021-01-26 19:39:01 +00:00
|
|
|
|
|
|
|
const loader = async () => {
|
2022-09-13 12:36:41 +00:00
|
|
|
const flowsRequest = await fetch(
|
|
|
|
`${addTrailingSlash(
|
|
|
|
adminClient.baseUrl
|
|
|
|
)}admin/realms/${realm}/admin-ui-authentication-management/flows`,
|
|
|
|
{
|
|
|
|
method: "GET",
|
|
|
|
headers: getAuthorizationHeaders(await adminClient.getAccessToken()),
|
2021-01-26 19:39:01 +00:00
|
|
|
}
|
2022-09-13 12:36:41 +00:00
|
|
|
);
|
|
|
|
const flows = await flowsRequest.json();
|
2021-01-26 19:39:01 +00:00
|
|
|
|
2022-12-08 09:30:44 +00:00
|
|
|
if (!flows) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
return sortBy(
|
|
|
|
localeSort<AuthenticationType>(flows, mapByKey("alias")),
|
|
|
|
(flow) => flow.usedBy?.type
|
|
|
|
);
|
2021-01-26 19:39:01 +00:00
|
|
|
};
|
2022-03-07 17:36:52 +00:00
|
|
|
|
2023-01-04 09:57:25 +00:00
|
|
|
const useTab = (tab: AuthenticationTab) =>
|
|
|
|
useRoutableTab(toAuthentication({ realm, tab }));
|
|
|
|
|
|
|
|
const flowsTab = useTab("flows");
|
|
|
|
const requiredActionsTab = useTab("required-actions");
|
|
|
|
const policiesTab = useTab("policies");
|
|
|
|
|
2021-01-26 19:39:01 +00:00
|
|
|
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
|
|
|
titleKey: "authentication:deleteConfirmFlow",
|
|
|
|
children: (
|
|
|
|
<Trans i18nKey="authentication:deleteConfirmFlowMessage">
|
|
|
|
{" "}
|
|
|
|
<strong>{{ flow: selectedFlow ? selectedFlow.alias : "" }}</strong>.
|
|
|
|
</Trans>
|
|
|
|
),
|
|
|
|
continueButtonLabel: "common:delete",
|
|
|
|
continueButtonVariant: ButtonVariant.danger,
|
|
|
|
onConfirm: async () => {
|
|
|
|
try {
|
|
|
|
await adminClient.authenticationManagement.deleteFlow({
|
|
|
|
flowId: selectedFlow!.id!,
|
|
|
|
});
|
|
|
|
refresh();
|
|
|
|
addAlert(t("deleteFlowSuccess"), AlertVariant.success);
|
|
|
|
} catch (error) {
|
2021-07-28 12:01:42 +00:00
|
|
|
addError("authentication:deleteFlowError", error);
|
2021-01-26 19:39:01 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2022-05-02 14:51:05 +00:00
|
|
|
const UsedByRenderer = (authType: AuthenticationType) => (
|
|
|
|
<UsedBy authType={authType} />
|
2021-01-26 19:39:01 +00:00
|
|
|
);
|
|
|
|
|
2021-08-09 08:47:34 +00:00
|
|
|
const AliasRenderer = ({
|
|
|
|
id,
|
|
|
|
alias,
|
|
|
|
usedBy,
|
|
|
|
builtIn,
|
|
|
|
}: AuthenticationType) => (
|
2021-01-26 19:39:01 +00:00
|
|
|
<>
|
2021-08-09 08:47:34 +00:00
|
|
|
<Link
|
|
|
|
to={toFlow({
|
|
|
|
realm,
|
|
|
|
id: id!,
|
2022-09-13 12:36:41 +00:00
|
|
|
usedBy: usedBy?.type || "notInUse",
|
2021-08-09 08:47:34 +00:00
|
|
|
builtIn: builtIn ? "builtIn" : undefined,
|
|
|
|
})}
|
|
|
|
key={`link-${id}`}
|
|
|
|
>
|
2022-08-19 09:15:35 +00:00
|
|
|
{alias}
|
2021-01-26 19:39:01 +00:00
|
|
|
</Link>{" "}
|
|
|
|
{builtIn && <Label key={`label-${id}`}>{t("buildIn")}</Label>}
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<DeleteConfirm />
|
|
|
|
{open && (
|
|
|
|
<DuplicateFlowModal
|
|
|
|
name={selectedFlow ? selectedFlow.alias! : ""}
|
|
|
|
description={selectedFlow?.description!}
|
2021-11-30 13:07:44 +00:00
|
|
|
toggleDialog={toggleOpen}
|
2021-01-26 19:39:01 +00:00
|
|
|
onComplete={() => {
|
|
|
|
refresh();
|
2022-03-07 17:36:52 +00:00
|
|
|
toggleOpen();
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
)}
|
|
|
|
{bindFlowOpen && (
|
|
|
|
<BindFlowDialog
|
|
|
|
onClose={() => {
|
|
|
|
toggleBindFlow();
|
|
|
|
refresh();
|
2021-01-26 19:39:01 +00:00
|
|
|
}}
|
2022-03-07 17:36:52 +00:00
|
|
|
flowAlias={selectedFlow?.alias!}
|
2021-01-26 19:39:01 +00:00
|
|
|
/>
|
|
|
|
)}
|
2021-12-21 15:32:53 +00:00
|
|
|
<ViewHeader
|
|
|
|
titleKey="authentication:title"
|
|
|
|
subKey="authentication:authenticationExplain"
|
|
|
|
helpUrl={helpUrls.authenticationUrl}
|
|
|
|
divider={false}
|
|
|
|
/>
|
2021-03-31 13:16:58 +00:00
|
|
|
<PageSection variant="light" className="pf-u-p-0">
|
2022-06-22 11:35:10 +00:00
|
|
|
<RoutableTabs
|
|
|
|
isBox
|
|
|
|
defaultLocation={toAuthentication({ realm, tab: "flows" })}
|
|
|
|
>
|
2021-01-26 19:39:01 +00:00
|
|
|
<Tab
|
2022-06-22 11:35:10 +00:00
|
|
|
data-testid="flows"
|
2021-01-26 19:39:01 +00:00
|
|
|
title={<TabTitleText>{t("flows")}</TabTitleText>}
|
2023-01-04 09:57:25 +00:00
|
|
|
{...flowsTab}
|
2021-01-26 19:39:01 +00:00
|
|
|
>
|
|
|
|
<KeycloakDataTable
|
|
|
|
key={key}
|
|
|
|
loader={loader}
|
|
|
|
ariaLabelKey="authentication:title"
|
2021-10-14 14:54:07 +00:00
|
|
|
searchPlaceholderKey="authentication:searchForFlow"
|
2021-08-09 08:47:34 +00:00
|
|
|
toolbarItem={
|
|
|
|
<ToolbarItem>
|
|
|
|
<Button
|
2021-10-01 10:52:45 +00:00
|
|
|
component={(props) => (
|
|
|
|
<Link {...props} to={toCreateFlow({ realm })} />
|
|
|
|
)}
|
2021-08-09 08:47:34 +00:00
|
|
|
>
|
|
|
|
{t("createFlow")}
|
|
|
|
</Button>
|
|
|
|
</ToolbarItem>
|
|
|
|
}
|
2022-06-27 07:13:43 +00:00
|
|
|
actionResolver={({ data }) => [
|
|
|
|
{
|
|
|
|
title: t("duplicate"),
|
|
|
|
onClick: () => {
|
|
|
|
toggleOpen();
|
|
|
|
setSelectedFlow(data);
|
2021-01-26 19:39:01 +00:00
|
|
|
},
|
2022-06-27 07:13:43 +00:00
|
|
|
},
|
2022-09-26 17:21:03 +00:00
|
|
|
...(data.usedBy?.type !== "DEFAULT"
|
2022-06-27 07:13:43 +00:00
|
|
|
? [
|
|
|
|
{
|
|
|
|
title: t("bindFlow"),
|
|
|
|
onClick: () => {
|
|
|
|
toggleBindFlow();
|
|
|
|
setSelectedFlow(data);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
]
|
|
|
|
: []),
|
2022-09-13 12:36:41 +00:00
|
|
|
...(!data.builtIn && !data.usedBy
|
2022-06-27 07:13:43 +00:00
|
|
|
? [
|
|
|
|
{
|
|
|
|
title: t("common:delete"),
|
|
|
|
onClick: () => {
|
|
|
|
setSelectedFlow(data);
|
|
|
|
toggleDeleteDialog();
|
2022-03-07 17:36:52 +00:00
|
|
|
},
|
2021-01-26 19:39:01 +00:00
|
|
|
},
|
2022-06-27 07:13:43 +00:00
|
|
|
]
|
|
|
|
: []),
|
|
|
|
]}
|
2021-01-26 19:39:01 +00:00
|
|
|
columns={[
|
|
|
|
{
|
|
|
|
name: "alias",
|
|
|
|
displayKey: "authentication:flowName",
|
|
|
|
cellRenderer: AliasRenderer,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "usedBy",
|
|
|
|
displayKey: "authentication:usedBy",
|
2022-05-02 14:51:05 +00:00
|
|
|
cellRenderer: UsedByRenderer,
|
2021-01-26 19:39:01 +00:00
|
|
|
},
|
|
|
|
{
|
|
|
|
name: "description",
|
|
|
|
displayKey: "common:description",
|
|
|
|
},
|
|
|
|
]}
|
|
|
|
emptyState={
|
|
|
|
<ListEmptyState
|
|
|
|
message={t("emptyEvents")}
|
|
|
|
instructions={t("emptyEventsInstructions")}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
</Tab>
|
2021-10-19 15:30:57 +00:00
|
|
|
<Tab
|
2022-06-22 11:35:10 +00:00
|
|
|
data-testid="requiredActions"
|
2021-10-19 15:30:57 +00:00
|
|
|
title={<TabTitleText>{t("requiredActions")}</TabTitleText>}
|
2023-01-04 09:57:25 +00:00
|
|
|
{...requiredActionsTab}
|
2021-10-19 15:30:57 +00:00
|
|
|
>
|
|
|
|
<RequiredActions />
|
|
|
|
</Tab>
|
2021-12-01 09:24:46 +00:00
|
|
|
<Tab
|
2022-06-22 11:35:10 +00:00
|
|
|
data-testid="policies"
|
2021-12-01 09:24:46 +00:00
|
|
|
title={<TabTitleText>{t("policies")}</TabTitleText>}
|
2023-01-04 09:57:25 +00:00
|
|
|
{...policiesTab}
|
2021-12-01 09:24:46 +00:00
|
|
|
>
|
|
|
|
<Policies />
|
|
|
|
</Tab>
|
2022-06-22 11:35:10 +00:00
|
|
|
</RoutableTabs>
|
2021-01-26 19:39:01 +00:00
|
|
|
</PageSection>
|
|
|
|
</>
|
|
|
|
);
|
2021-10-29 16:11:06 +00:00
|
|
|
}
|