diff --git a/src/identity-providers/IdentityProvidersSection.tsx b/src/identity-providers/IdentityProvidersSection.tsx index d935696e06..9ea79efe07 100644 --- a/src/identity-providers/IdentityProvidersSection.tsx +++ b/src/identity-providers/IdentityProvidersSection.tsx @@ -75,7 +75,7 @@ export const IdentityProvidersSection = () => { key={identityProvider.providerId} to={toIdentityProviderTab({ realm, - providerId: identityProvider.providerId!, + providerId: identityProvider.providerId, alias: identityProvider.alias!, })} > diff --git a/src/identity-providers/add/AddIdentityProvider.tsx b/src/identity-providers/add/AddIdentityProvider.tsx index ba45530a48..70646ddd70 100644 --- a/src/identity-providers/add/AddIdentityProvider.tsx +++ b/src/identity-providers/add/AddIdentityProvider.tsx @@ -18,6 +18,7 @@ import { useAdminClient } from "../../context/auth/AdminClient"; import { useRealm } from "../../context/realm-context/RealmContext"; import { useAlerts } from "../../components/alert/Alerts"; import { GeneralSettings } from "./GeneralSettings"; +import { toIdentityProviderTab } from "../routes/IdentityProviderTab"; export const IdentityProviderCrumb = ({ match, location }: BreadcrumbData) => { const { t } = useTranslation(); @@ -64,7 +65,7 @@ export const AddIdentityProvider = () => { alias: id, }); addAlert(t("createSuccess"), AlertVariant.success); - history.push(`/${realm}/identity-providers/${id}/settings`); + history.push(toIdentityProviderTab({ realm, providerId: id, alias: id })); } catch (error) { addError("identity-providers:createError", error); } diff --git a/src/identity-providers/add/DetailSettings.tsx b/src/identity-providers/add/DetailSettings.tsx index 1dc93d5be6..063e6e69e0 100644 --- a/src/identity-providers/add/DetailSettings.tsx +++ b/src/identity-providers/add/DetailSettings.tsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React from "react"; import { useHistory, useParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { Controller, FormProvider, useForm } from "react-hook-form"; @@ -33,6 +33,9 @@ import { OIDCGeneralSettings } from "./OIDCGeneralSettings"; import { SamlGeneralSettings } from "./SamlGeneralSettings"; import { OIDCAuthentication } from "./OIDCAuthentication"; import { ReqAuthnConstraints } from "./ReqAuthnConstraintsSettings"; +import { KeycloakDataTable } from "../../components/table-toolbar/KeycloakDataTable"; +import { ListEmptyState } from "../../components/list-empty-state/ListEmptyState"; +import type IdentityProviderMapperRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderMapperRepresentation"; type HeaderProps = { onChange: (value: boolean) => void; @@ -41,6 +44,13 @@ type HeaderProps = { toggleDeleteDialog: () => void; }; +type IdPWithMapperAttributes = IdentityProviderMapperRepresentation & { + name: string; + category?: string; + helpText?: string; + type: string; +}; + const Header = ({ onChange, value, save, toggleDeleteDialog }: HeaderProps) => { const { t } = useTranslation("identity-providers"); const { providerId, alias } = @@ -86,9 +96,8 @@ export const DetailSettings = () => { const { providerId, alias } = useParams<{ providerId: string; alias: string }>(); - const [provider, setProvider] = useState(); const form = useForm(); - const { handleSubmit, setValue, getValues, reset } = form; + const { handleSubmit, getValues, reset } = form; const adminClient = useAdminClient(); const { addAlert, addError } = useAlerts(); @@ -97,11 +106,8 @@ export const DetailSettings = () => { useFetch( () => adminClient.identityProviders.findOne({ alias: alias }), - (provider) => { - if (provider) { - setProvider(provider); - Object.entries(provider).map((entry) => setValue(entry[0], entry[1])); - } + (fetchedProvider) => { + reset(fetchedProvider); }, [] ); @@ -113,7 +119,6 @@ export const DetailSettings = () => { { alias }, { ...p, alias, providerId } ); - setProvider(p); addAlert(t("updateSuccess"), AlertVariant.success); } catch (error) { addError("identity-providers:updateError", error); @@ -141,6 +146,30 @@ export const DetailSettings = () => { const isOIDC = providerId.includes("oidc"); const isSAML = providerId.includes("saml"); + const loader = async () => { + const [loaderMappers, loaderMapperTypes] = await Promise.all([ + adminClient.identityProviders.findMappers({ alias }), + adminClient.identityProviders.findMapperTypes({ alias }), + ]); + + const components = loaderMappers.map((loaderMapper) => { + const mapperType = Object.values(loaderMapperTypes).find( + (loaderMapperType) => + loaderMapper.identityProviderMapper! === loaderMapperType.id! + ); + + const result: IdPWithMapperAttributes = { + ...mapperType, + name: loaderMapper.name!, + type: mapperType?.name!, + }; + + return result; + }); + + return components; + }; + if (isOIDC) { sections.splice(1, 0, t("oidcSettings")); } @@ -222,7 +251,7 @@ export const DetailSettings = () => { data-testid={"revert"} variant="link" onClick={() => { - reset(provider); + reset(); }} > {t("common:revert")} @@ -231,6 +260,39 @@ export const DetailSettings = () => { + {t("common:mappers")}} + > + + } + loader={loader} + isPaginated + ariaLabelKey="identity-providers:mappersList" + searchPlaceholderKey="identity-providers:searchForMapper" + columns={[ + { + name: "name", + displayKey: "common:name", + }, + { + name: "category", + displayKey: "common:category", + }, + { + name: "type", + displayKey: "common:type", + }, + ]} + /> + diff --git a/src/identity-providers/messages.ts b/src/identity-providers/messages.ts index aeb40c9fa9..3b6bbbd02b 100644 --- a/src/identity-providers/messages.ts +++ b/src/identity-providers/messages.ts @@ -5,6 +5,12 @@ export default { searchForProvider: "Search for provider", provider: "Provider details", addProvider: "Add provider", + addMapper: "Add mapper", + mappersList: "Mappers list", + noMappers: "No Mappers", + noMappersInstructions: + "There are currently no mappers for this identity provider.", + searchForMapper: "Search for mapper", addKeycloakOpenIdProvider: "Add Keycloak OpenID Connect provider", addOpenIdProvider: "Add OpenID Connect provider", addSamlProvider: "Add SAML provider", diff --git a/src/identity-providers/routes/IdentityProviderTab.ts b/src/identity-providers/routes/IdentityProviderTab.ts index 10a430bf74..fc5e4d4892 100644 --- a/src/identity-providers/routes/IdentityProviderTab.ts +++ b/src/identity-providers/routes/IdentityProviderTab.ts @@ -13,7 +13,7 @@ export type IdentityProviderTabParams = { }; export const IdentityProviderTabRoute: RouteDef = { - path: "/:realm/identity-providers/:providerId/:alias/:tab?", + path: "/:realm/identity-providers/:providerId?/:alias/:tab?", component: DetailSettings, access: "manage-identity-providers", };