keycloak-scim/js/apps/admin-ui/src/authentication/AuthenticationSection.tsx

293 lines
9 KiB
TypeScript
Raw Normal View History

import { fetchWithError } from "@keycloak/keycloak-admin-client";
import type AuthenticationFlowRepresentation from "@keycloak/keycloak-admin-client/lib/defs/authenticationFlowRepresentation";
import RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import {
AlertVariant,
Button,
ButtonVariant,
Label,
PageSection,
Tab,
TabTitleText,
Initial version of the authentication section (#887) * initial version of create authentication screen * initial version of authentication details * added flow details labels to view header * not in use fix * create execution tree * fixed collapsable row layout * fix drag and drop expand * fix merge error * move to modal * diff and post drag and drop changes * fixed locating the parent row * move "live text" for d&d to common messages * firefox fix * initial version of the diagram * use dagre to layout automatically * moved to sperate file * conditional node * now renders subflows sequential * changed to render sequential or parallel flows * fixed render of sub flows * added button edge, drawer and selectable nodes * add requirement dropdown * also do move so we can merge * also do move so we can merge * fixed merge * added refresh * change requirement * fixed merge error * now uses the new routes * Split out routes into multiple files * Update src/authentication/AuthenticationSection.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * Update src/authentication/FlowDetails.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * Update src/authentication/FlowDetails.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * Update src/authentication/FlowDetails.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * Update src/authentication/FlowDetails.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * fixed labels * merge fix * make execution of these parrallel * added some tests * Update src/authentication/components/FlowRequirementDropdown.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * more review changes * fixed merge error Co-authored-by: Jon Koops <jonkoops@gmail.com>
2021-08-09 08:47:34 +00:00
ToolbarItem,
} from "@patternfly/react-core";
2023-03-02 12:51:16 +00:00
import { sortBy } from "lodash-es";
import { useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { adminClient } from "../admin-client";
import { useAlerts } from "../components/alert/Alerts";
2023-03-02 12:51:16 +00:00
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
2023-03-02 12:51:16 +00:00
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
2022-06-22 11:35:10 +00:00
import {
RoutableTabs,
useRoutableTab,
2022-06-22 11:35:10 +00:00
} from "../components/routable-tabs/RoutableTabs";
2023-03-02 12:51:16 +00:00
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
import { ViewHeader } from "../components/view-header/ViewHeader";
import { useRealm } from "../context/realm-context/RealmContext";
import helpUrls from "../help-urls";
import { addTrailingSlash } from "../util";
import { getAuthorizationHeaders } from "../utils/getAuthorizationHeaders";
import { useFetch } from "../utils/useFetch";
import useLocaleSort, { mapByKey } from "../utils/useLocaleSort";
2023-03-02 12:51:16 +00:00
import useToggle from "../utils/useToggle";
import { BindFlowDialog } from "./BindFlowDialog";
import { DuplicateFlowModal } from "./DuplicateFlowModal";
import { RequiredActions } from "./RequiredActions";
import { UsedBy } from "./components/UsedBy";
import { Policies } from "./policies/Policies";
2023-03-02 12:51:16 +00:00
import { AuthenticationTab, toAuthentication } from "./routes/Authentication";
import { toCreateFlow } from "./routes/CreateFlow";
import { toFlow } from "./routes/Flow";
type UsedBy = "SPECIFIC_CLIENTS" | "SPECIFIC_PROVIDERS" | "DEFAULT";
export type AuthenticationType = AuthenticationFlowRepresentation & {
usedBy?: { type?: UsedBy; values: string[] };
2023-03-02 12:51:16 +00:00
realm: RealmRepresentation;
};
export const REALM_FLOWS = new Map<string, string>([
["browserFlow", "browser"],
["registrationFlow", "registration"],
["directGrantFlow", "direct grant"],
["resetCredentialsFlow", "reset credentials"],
["clientAuthenticationFlow", "clients"],
["dockerAuthenticationFlow", "docker auth"],
["firstBrokerLoginFlow", "firstBrokerLogin"],
]);
const AliasRenderer = ({ id, alias, usedBy, builtIn }: AuthenticationType) => {
const { t } = useTranslation();
const { realm } = useRealm();
return (
<>
<Link
to={toFlow({
realm,
id: id!,
usedBy: usedBy?.type || "notInUse",
builtIn: builtIn ? "builtIn" : undefined,
})}
key={`link-${id}`}
>
{alias}
</Link>{" "}
{builtIn && <Label key={`label-${id}`}>{t("buildIn")}</Label>}
</>
);
};
2021-10-29 16:11:06 +00:00
export default function AuthenticationSection() {
const { t } = useTranslation();
2023-03-02 12:51:16 +00:00
const { realm: realmName } = useRealm();
const [key, setKey] = useState(0);
2023-03-02 12:51:16 +00:00
const refresh = () => {
setRealm(undefined);
setKey(key + 1);
};
const { addAlert, addError } = useAlerts();
const localeSort = useLocaleSort();
const [selectedFlow, setSelectedFlow] = useState<AuthenticationType>();
2022-03-07 17:36:52 +00:00
const [open, toggleOpen] = useToggle();
const [bindFlowOpen, toggleBindFlow] = useToggle();
2023-03-02 12:51:16 +00:00
const [realm, setRealm] = useState<RealmRepresentation>();
useFetch(() => adminClient.realms.findOne({ realm: realmName }), setRealm, [
key,
]);
const loader = async () => {
const flowsRequest = await fetchWithError(
`${addTrailingSlash(
adminClient.baseUrl,
2023-03-02 12:51:16 +00:00
)}admin/realms/${realmName}/ui-ext/authentication-management/flows`,
{
method: "GET",
headers: getAuthorizationHeaders(await adminClient.getAccessToken()),
},
);
const flows = await flowsRequest.json();
if (!flows) {
return [];
}
return sortBy(
localeSort<AuthenticationType>(flows, mapByKey("alias")),
(flow) => flow.usedBy?.type,
);
};
2022-03-07 17:36:52 +00:00
const useTab = (tab: AuthenticationTab) =>
2023-03-02 12:51:16 +00:00
useRoutableTab(toAuthentication({ realm: realmName, tab }));
const flowsTab = useTab("flows");
const requiredActionsTab = useTab("required-actions");
const policiesTab = useTab("policies");
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
titleKey: "deleteConfirmFlow",
children: (
<Trans i18nKey="deleteConfirmFlowMessage">
{" "}
<strong>{{ flow: selectedFlow ? selectedFlow.alias : "" }}</strong>.
</Trans>
),
continueButtonLabel: "delete",
continueButtonVariant: ButtonVariant.danger,
onConfirm: async () => {
try {
await adminClient.authenticationManagement.deleteFlow({
flowId: selectedFlow!.id!,
});
refresh();
addAlert(t("deleteFlowSuccess"), AlertVariant.success);
} catch (error) {
addError("deleteFlowError", error);
}
},
});
2023-03-02 12:51:16 +00:00
if (!realm) return <KeycloakSpinner />;
return (
<>
<DeleteConfirm />
{open && (
<DuplicateFlowModal
name={selectedFlow ? selectedFlow.alias! : ""}
description={selectedFlow?.description!}
toggleDialog={toggleOpen}
onComplete={() => {
refresh();
2022-03-07 17:36:52 +00:00
toggleOpen();
}}
/>
)}
{bindFlowOpen && (
<BindFlowDialog
onClose={() => {
toggleBindFlow();
refresh();
}}
2022-03-07 17:36:52 +00:00
flowAlias={selectedFlow?.alias!}
/>
)}
<ViewHeader
titleKey="titleAuthentication"
subKey="authenticationExplain"
helpUrl={helpUrls.authenticationUrl}
divider={false}
/>
<PageSection variant="light" className="pf-v5-u-p-0">
2022-06-22 11:35:10 +00:00
<RoutableTabs
isBox
2023-03-02 12:51:16 +00:00
defaultLocation={toAuthentication({ realm: realmName, tab: "flows" })}
2022-06-22 11:35:10 +00:00
>
<Tab
2022-06-22 11:35:10 +00:00
data-testid="flows"
title={<TabTitleText>{t("flows")}</TabTitleText>}
{...flowsTab}
>
<KeycloakDataTable
key={key}
loader={loader}
ariaLabelKey="titleAuthentication"
searchPlaceholderKey="searchForFlow"
Initial version of the authentication section (#887) * initial version of create authentication screen * initial version of authentication details * added flow details labels to view header * not in use fix * create execution tree * fixed collapsable row layout * fix drag and drop expand * fix merge error * move to modal * diff and post drag and drop changes * fixed locating the parent row * move "live text" for d&d to common messages * firefox fix * initial version of the diagram * use dagre to layout automatically * moved to sperate file * conditional node * now renders subflows sequential * changed to render sequential or parallel flows * fixed render of sub flows * added button edge, drawer and selectable nodes * add requirement dropdown * also do move so we can merge * also do move so we can merge * fixed merge * added refresh * change requirement * fixed merge error * now uses the new routes * Split out routes into multiple files * Update src/authentication/AuthenticationSection.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * Update src/authentication/FlowDetails.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * Update src/authentication/FlowDetails.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * Update src/authentication/FlowDetails.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * Update src/authentication/FlowDetails.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * fixed labels * merge fix * make execution of these parrallel * added some tests * Update src/authentication/components/FlowRequirementDropdown.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * more review changes * fixed merge error Co-authored-by: Jon Koops <jonkoops@gmail.com>
2021-08-09 08:47:34 +00:00
toolbarItem={
<ToolbarItem>
<Button
component={(props) => (
2023-03-02 12:51:16 +00:00
<Link
{...props}
to={toCreateFlow({ realm: realmName })}
/>
)}
Initial version of the authentication section (#887) * initial version of create authentication screen * initial version of authentication details * added flow details labels to view header * not in use fix * create execution tree * fixed collapsable row layout * fix drag and drop expand * fix merge error * move to modal * diff and post drag and drop changes * fixed locating the parent row * move "live text" for d&d to common messages * firefox fix * initial version of the diagram * use dagre to layout automatically * moved to sperate file * conditional node * now renders subflows sequential * changed to render sequential or parallel flows * fixed render of sub flows * added button edge, drawer and selectable nodes * add requirement dropdown * also do move so we can merge * also do move so we can merge * fixed merge * added refresh * change requirement * fixed merge error * now uses the new routes * Split out routes into multiple files * Update src/authentication/AuthenticationSection.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * Update src/authentication/FlowDetails.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * Update src/authentication/FlowDetails.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * Update src/authentication/FlowDetails.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * Update src/authentication/FlowDetails.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * fixed labels * merge fix * make execution of these parrallel * added some tests * Update src/authentication/components/FlowRequirementDropdown.tsx Co-authored-by: Jon Koops <jonkoops@gmail.com> * more review changes * fixed merge error Co-authored-by: Jon Koops <jonkoops@gmail.com>
2021-08-09 08:47:34 +00:00
>
{t("createFlow")}
</Button>
</ToolbarItem>
}
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("delete"),
onClick: () => {
setSelectedFlow(data);
toggleDeleteDialog();
2022-03-07 17:36:52 +00:00
},
},
]
: []),
]}
columns={[
{
name: "alias",
displayKey: "flowName",
cellRenderer: (row) => <AliasRenderer {...row} />,
},
{
name: "usedBy",
displayKey: "usedBy",
2023-03-02 12:51:16 +00:00
cellRenderer: (row) => (
<UsedBy authType={row} realm={realm} />
),
},
{
name: "description",
displayKey: "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>}
{...requiredActionsTab}
2021-10-19 15:30:57 +00:00
>
<RequiredActions />
</Tab>
<Tab
2022-06-22 11:35:10 +00:00
data-testid="policies"
title={<TabTitleText>{t("policies")}</TabTitleText>}
{...policiesTab}
>
<Policies />
</Tab>
2022-06-22 11:35:10 +00:00
</RoutableTabs>
</PageSection>
</>
);
2021-10-29 16:11:06 +00:00
}