diff --git a/public/resources/en/authentication.json b/public/resources/en/authentication.json
index ec0371a5e7..d74c52b941 100644
--- a/public/resources/en/authentication.json
+++ b/public/resources/en/authentication.json
@@ -67,6 +67,8 @@
"flowName": "Flow name",
"searchForFlow": "Search for flow",
"usedBy": "Used by",
+ "flowUsedBy": "Flow used by",
+ "flowUsedByDescription": "This flow is applied by the following {{value}}",
"buildIn": "Built-in",
"appliedByProviders": "Applied by the following providers",
"appliedByClients": "Applied by the following clients",
diff --git a/src/authentication/AuthenticationSection.tsx b/src/authentication/AuthenticationSection.tsx
index 76fb03bfb4..86a538e177 100644
--- a/src/authentication/AuthenticationSection.tsx
+++ b/src/authentication/AuthenticationSection.tsx
@@ -8,12 +8,10 @@ import {
ButtonVariant,
Label,
PageSection,
- Popover,
Tab,
TabTitleText,
ToolbarItem,
} from "@patternfly/react-core";
-import { CheckCircleIcon } from "@patternfly/react-icons";
import type AuthenticationFlowRepresentation from "@keycloak/keycloak-admin-client/lib/defs/authenticationFlowRepresentation";
import { useAdminClient } from "../context/auth/AdminClient";
@@ -33,12 +31,13 @@ import { RequiredActions } from "./RequiredActions";
import { Policies } from "./policies/Policies";
import helpUrls from "../help-urls";
import { BindFlowDialog } from "./BindFlowDialog";
+import { UsedBy } from "./components/UsedBy";
import "./authentication-section.css";
type UsedBy = "specificClients" | "default" | "specificProviders";
-type AuthenticationType = AuthenticationFlowRepresentation & {
+export type AuthenticationType = AuthenticationFlowRepresentation & {
usedBy: { type?: UsedBy; values: string[] };
};
@@ -64,7 +63,7 @@ export default function AuthenticationSection() {
const [bindFlowOpen, toggleBindFlow] = useToggle();
const loader = async () => {
- const [clients, idps, realmRep, flows] = await Promise.all([
+ const [allClients, allIdps, realmRep, flows] = await Promise.all([
adminClient.clients.find(),
adminClient.identityProviders.find(),
adminClient.realms.findOne({ realm }),
@@ -80,31 +79,32 @@ export default function AuthenticationSection() {
for (const flow of flows as AuthenticationType[]) {
flow.usedBy = { values: [] };
- const client = clients.find(
+ const clients = allClients.filter(
(client) =>
client.authenticationFlowBindingOverrides &&
(client.authenticationFlowBindingOverrides["direct_grant"] ===
flow.id ||
client.authenticationFlowBindingOverrides["browser"] === flow.id)
);
- if (client) {
+ if (clients.length > 0) {
flow.usedBy.type = "specificClients";
- flow.usedBy.values.push(client.clientId!);
+ flow.usedBy.values = clients.map(({ clientId }) => clientId!);
}
- const idp = idps.find(
+ const idps = allIdps.filter(
(idp) =>
idp.firstBrokerLoginFlowAlias === flow.alias ||
idp.postBrokerLoginFlowAlias === flow.alias
);
- if (idp) {
+ if (idps.length > 0) {
flow.usedBy.type = "specificProviders";
- flow.usedBy.values.push(idp.alias!);
+ flow.usedBy.values = idps.map(({ alias }) => alias!);
}
const isDefault = defaultFlows.includes(flow.alias);
if (isDefault) {
flow.usedBy.type = "default";
+ flow.usedBy.values.push(flow.alias!);
}
}
@@ -134,47 +134,8 @@ export default function AuthenticationSection() {
},
});
- const UsedBy = ({ id, usedBy: { type, values } }: AuthenticationType) => (
- <>
- {(type === "specificProviders" || type === "specificClients") && (
-
- {t(
- "appliedBy" +
- (type === "specificClients" ? "Clients" : "Providers")
- )}{" "}
- {values.map((used, index) => (
- <>
- {used}
- {index < values.length - 1 ? ", " : ""}
- >
- ))}
-
- }
- >
- <>
- {" "}
- {t(type)}
- >
-
- )}
- {type === "default" && (
- <>
- {" "}
- {t("default")}
- >
- )}
- {!type && t("notInUse")}
- >
+ const UsedByRenderer = (authType: AuthenticationType) => (
+
);
const AliasRenderer = ({
@@ -296,7 +257,7 @@ export default function AuthenticationSection() {
{
name: "usedBy",
displayKey: "authentication:usedBy",
- cellRenderer: UsedBy,
+ cellRenderer: UsedByRenderer,
},
{
name: "description",
diff --git a/src/authentication/components/UsedBy.tsx b/src/authentication/components/UsedBy.tsx
new file mode 100644
index 0000000000..2a6b760e6f
--- /dev/null
+++ b/src/authentication/components/UsedBy.tsx
@@ -0,0 +1,142 @@
+import React from "react";
+import { useTranslation } from "react-i18next";
+import {
+ Button,
+ Modal,
+ ModalVariant,
+ Popover,
+ Text,
+ TextContent,
+ TextVariants,
+} from "@patternfly/react-core";
+import { CheckCircleIcon } from "@patternfly/react-icons";
+
+import type { AuthenticationType } from "../AuthenticationSection";
+import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable";
+import { toUpperCase } from "../../util";
+import useToggle from "../../utils/useToggle";
+
+import "./used-by.css";
+
+type UsedByProps = {
+ authType: AuthenticationType;
+};
+
+const Label = ({ label }: { label: string }) => (
+ <>
+ {" "}
+ {label}
+ >
+);
+
+type UsedByModalProps = {
+ values: string[];
+ onClose: () => void;
+ isSpecificClient: boolean;
+};
+
+const UsedByModal = ({
+ values,
+ isSpecificClient,
+ onClose,
+}: UsedByModalProps) => {
+ const { t } = useTranslation("authentication");
+ return (
+
+ {t("flowUsedBy")}
+
+ {t("flowUsedByDescription", {
+ value: isSpecificClient ? t("clients") : t("identiyProviders"),
+ })}
+
+
+ }
+ variant={ModalVariant.medium}
+ isOpen
+ onClose={onClose}
+ actions={[
+ ,
+ ]}
+ >
+ ({ name: value }))}
+ ariaLabelKey="authentication:usedBy"
+ searchPlaceholderKey="common:search"
+ columns={[
+ {
+ name: "name",
+ },
+ ]}
+ />
+
+ );
+};
+
+export const UsedBy = ({
+ authType: {
+ id,
+ usedBy: { type, values },
+ },
+}: UsedByProps) => {
+ const { t } = useTranslation("authentication");
+ const [open, toggle] = useToggle();
+
+ return (
+ <>
+ {open && (
+
+ )}
+ {(type === "specificProviders" || type === "specificClients") &&
+ (values.length < 8 ? (
+
+ {t(
+ "appliedBy" +
+ (type === "specificClients" ? "Clients" : "Providers")
+ )}{" "}
+ {values.map((used, index) => (
+ <>
+ {used}
+ {index < values.length - 1 ? ", " : ""}
+ >
+ ))}
+
+ }
+ >
+
+
+ ) : (
+
+ ))}
+ {type === "default" && }
+ {!type && t("notInUse")}
+ >
+ );
+};
diff --git a/src/authentication/components/used-by.css b/src/authentication/components/used-by.css
new file mode 100644
index 0000000000..a9988d1411
--- /dev/null
+++ b/src/authentication/components/used-by.css
@@ -0,0 +1,4 @@
+.keycloak__used-by__popover-button {
+ padding: 0;
+ font-size: var(--pf-c-table--cell--FontSize);
+}
\ No newline at end of file