diff --git a/src/realm-settings/JavaKeystoreModal.tsx b/src/realm-settings/JavaKeystoreModal.tsx index 41f07050cc..b9b0f6eeff 100644 --- a/src/realm-settings/JavaKeystoreModal.tsx +++ b/src/realm-settings/JavaKeystoreModal.tsx @@ -21,6 +21,7 @@ import { useAlerts } from "../components/alert/Alerts"; import type ComponentRepresentation from "keycloak-admin/lib/defs/componentRepresentation"; import { HelpItem } from "../components/help-enabler/HelpItem"; import { useServerInfo } from "../context/server-info/ServerInfoProvider"; +import { KEY_PROVIDER_TYPE } from "../util"; type JavaKeystoreModalProps = { providerType: string; @@ -45,7 +46,7 @@ JavaKeystoreModalProps) => { useState(false); const allComponentTypes = - serverInfo.componentTypes?.["org.keycloak.keys.KeyProvider"] ?? []; + serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? []; const save = async (component: ComponentRepresentation) => { try { @@ -53,7 +54,8 @@ JavaKeystoreModalProps) => { ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, + config: { priority: ["0"] }, }); handleModalToggle(); addAlert(t("saveProviderSuccess"), AlertVariant.success); diff --git a/src/realm-settings/KeysProvidersTab.tsx b/src/realm-settings/KeysProvidersTab.tsx index f4456d4f55..ac65b144d7 100644 --- a/src/realm-settings/KeysProvidersTab.tsx +++ b/src/realm-settings/KeysProvidersTab.tsx @@ -23,6 +23,7 @@ import { Toolbar, ToolbarGroup, ToolbarItem, + Tooltip, } from "@patternfly/react-core"; import { SearchIcon } from "@patternfly/react-icons"; @@ -43,6 +44,7 @@ import { HMACGeneratedModal } from "./key-providers/hmac-generated/HMACGenerated import { ECDSAGeneratedModal } from "./key-providers/ecdsa-generated/ECDSAGeneratedModal"; import { RSAModal } from "./RSAModal"; import { RSAGeneratedModal } from "./key-providers/rsa-generated/RSAGeneratedModal"; +import { KEY_PROVIDER_TYPE } from "../util"; type ComponentData = KeyMetadataRepresentation & { id?: string; @@ -50,6 +52,7 @@ type ComponentData = KeyMetadataRepresentation & { name?: string; toggleHidden?: boolean; config?: any; + parentId?: string; }; type KeysTabInnerProps = { @@ -75,11 +78,9 @@ export const KeysTabInner = ({ components, refresh }: KeysTabInnerProps) => { const [isCreateModalOpen, setIsCreateModalOpen] = useState(false); const serverInfo = useServerInfo(); - const providerTypes = serverInfo.componentTypes![ - "org.keycloak.keys.KeyProvider" - ].map((item) => item.id); - - const itemIds = components.map((_, idx) => "data" + idx); + const providerTypes = ( + serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? [] + ).map((item) => item.id); const [itemOrder, setItemOrder] = useState([]); const [providerDropdownOpen, setProviderDropdownOpen] = useState(false); @@ -93,6 +94,7 @@ export const KeysTabInner = ({ components, refresh }: KeysTabInnerProps) => { const [liveText, setLiveText] = useState(""); useEffect(() => { + const itemIds = components.map((component) => component.id!); setItemOrder(["data", ...itemIds]); }, [components, searchVal]); @@ -117,7 +119,7 @@ export const KeysTabInner = ({ components, refresh }: KeysTabInnerProps) => { }, }); - const onDragStart = (id: string) => { + const onDragStart = async (id: string) => { setLiveText(t("common:onDragStart", { item: id })); setId(id); }; @@ -130,9 +132,40 @@ export const KeysTabInner = ({ components, refresh }: KeysTabInnerProps) => { setLiveText(t("common:onDragCancel")); }; - const onDragFinish = (itemOrder: string[]) => { - setItemOrder(["data", ...itemOrder.filter((i) => i !== "data")]); + const onDragFinish = async (itemOrder: string[]) => { + setItemOrder(itemOrder); setLiveText(t("common:onDragFinish")); + const updateAll = components.map((component: ComponentData) => { + const componentToSave = { ...component }; + delete componentToSave.providerDescription; + + return adminClient.components.update( + { id: component.id! }, + { + ...componentToSave, + config: { + priority: [ + ( + itemOrder.length - + itemOrder.indexOf(component.id!) + + 100 + ).toString(), + ], + }, + } + ); + }); + + try { + await Promise.all(updateAll); + refresh(); + addAlert( + t("realm-settings:saveProviderListSuccess"), + AlertVariant.success + ); + } catch (error) { + addError("realm-settings:saveProviderError", error); + } }; const onSearch = () => { @@ -278,7 +311,6 @@ export const KeysTabInner = ({ components, refresh }: KeysTabInnerProps) => { - { isCompact > - + + <>{t("realm-settings:name")} , - + <>{t("realm-settings:provider")} , - + <>{t("realm-settings:providerDescription")} , ]} @@ -317,22 +351,23 @@ export const KeysTabInner = ({ components, refresh }: KeysTabInnerProps) => { {(filteredComponents.length === 0 ? components : filteredComponents - ).map((component: ComponentData, idx) => ( + ).map((component, idx) => ( - + - + + + { try { @@ -52,7 +53,8 @@ export const RSAGeneratedModal = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, + config: { priority: ["0"] }, }); handleModalToggle(); addAlert(t("saveProviderSuccess"), AlertVariant.success); diff --git a/src/realm-settings/RSAModal.tsx b/src/realm-settings/RSAModal.tsx index 21f85ffbca..e0d16c50fd 100644 --- a/src/realm-settings/RSAModal.tsx +++ b/src/realm-settings/RSAModal.tsx @@ -23,6 +23,7 @@ import type ComponentRepresentation from "keycloak-admin/lib/defs/componentRepre import { HelpItem } from "../components/help-enabler/HelpItem"; import { useServerInfo } from "../context/server-info/ServerInfoProvider"; import { useParams } from "react-router-dom"; +import { KEY_PROVIDER_TYPE } from "../util"; type RSAModalProps = { providerType: string; @@ -50,7 +51,7 @@ export const RSAModal = ({ const [certificateFileName, setCertificateFileName] = useState(""); const allComponentTypes = - serverInfo.componentTypes?.["org.keycloak.keys.KeyProvider"] ?? []; + serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? []; const save = async (component: ComponentRepresentation) => { try { @@ -61,7 +62,7 @@ export const RSAModal = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, } ); addAlert(t("saveProviderSuccess"), AlertVariant.success); @@ -70,7 +71,8 @@ export const RSAModal = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, + config: { priority: ["0"] }, }); handleModalToggle(); addAlert(t("saveProviderSuccess"), AlertVariant.success); diff --git a/src/realm-settings/RealmSettingsSection.css b/src/realm-settings/RealmSettingsSection.css index 836cd72fa7..32c39bc287 100644 --- a/src/realm-settings/RealmSettingsSection.css +++ b/src/realm-settings/RealmSettingsSection.css @@ -172,3 +172,7 @@ article.pf-c-card.pf-m-flat.kc-login-settings-template font-size: var(--pf-global--FontSize--md); font-weight: bold; } + +.kc-row-drag-button { + padding: var(--pf-global--spacer--sm); +} diff --git a/src/realm-settings/RealmSettingsSection.tsx b/src/realm-settings/RealmSettingsSection.tsx index 2029fbdcfe..35d4f26361 100644 --- a/src/realm-settings/RealmSettingsSection.tsx +++ b/src/realm-settings/RealmSettingsSection.tsx @@ -26,7 +26,7 @@ import { useRealm } from "../context/realm-context/RealmContext"; import { useServerInfo } from "../context/server-info/ServerInfoProvider"; import { LocalizationTab } from "./LocalizationTab"; import { useWhoAmI } from "../context/whoami/WhoAmI"; -import { toUpperCase } from "../util"; +import { KEY_PROVIDER_TYPE, toUpperCase } from "../util"; import { RealmSettingsEmailTab } from "./EmailTab"; import { EventsTab } from "./event-config/EventsTab"; import { RealmSettingsGeneralTab } from "./GeneralTab"; @@ -149,6 +149,19 @@ const RealmSettingsHeader = ({ ); }; +const sortByPriority = (components: ComponentRepresentation[]) => { + const sortedComponents = [...components].sort((a, b) => { + const priorityA = Number(a.config?.priority); + const priorityB = Number(b.config?.priority); + + return ( + (!isNaN(priorityB) ? priorityB : 0) - (!isNaN(priorityA) ? priorityA : 0) + ); + }); + + return sortedComponents; +}; + export const RealmSettingsSection = () => { const { t } = useTranslation("realm-settings"); const adminClient = useAdminClient(); @@ -165,13 +178,13 @@ export const RealmSettingsSection = () => { const { whoAmI } = useWhoAmI(); const kpComponentTypes = - useServerInfo().componentTypes!["org.keycloak.keys.KeyProvider"]; + useServerInfo().componentTypes?.[KEY_PROVIDER_TYPE] ?? []; useFetch( async () => { const realm = await adminClient.realms.findOne({ realm: realmName }); const realmComponents = await adminClient.components.find({ - type: "org.keycloak.keys.KeyProvider", + type: KEY_PROVIDER_TYPE, realm: realmName, }); const user = await adminClient.users.findOne({ id: whoAmI.getUserId()! }); @@ -179,7 +192,7 @@ export const RealmSettingsSection = () => { return { user, realm, realmComponents }; }, ({ user, realm, realmComponents }) => { - setRealmComponents(realmComponents); + setRealmComponents(sortByPriority(realmComponents)); setCurrentUser(user); setRealm(realm); }, diff --git a/src/realm-settings/key-providers/aes-generated/AESGeneratedForm.tsx b/src/realm-settings/key-providers/aes-generated/AESGeneratedForm.tsx index dd56ee5e90..c155a7d9a9 100644 --- a/src/realm-settings/key-providers/aes-generated/AESGeneratedForm.tsx +++ b/src/realm-settings/key-providers/aes-generated/AESGeneratedForm.tsx @@ -22,7 +22,7 @@ import { useAdminClient, useFetch } from "../../../context/auth/AdminClient"; import { useParams, useRouteMatch } from "react-router-dom"; import { FormAccess } from "../../../components/form-access/FormAccess"; import { ViewHeader } from "../../../components/view-header/ViewHeader"; -import { convertToFormValues } from "../../../util"; +import { convertToFormValues, KEY_PROVIDER_TYPE } from "../../../util"; import { useAlerts } from "../../../components/alert/Alerts"; type AESGeneratedFormProps = { @@ -63,7 +63,7 @@ export const AESGeneratedForm = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, } ); addAlert(t("saveProviderSuccess"), AlertVariant.success); @@ -72,7 +72,8 @@ export const AESGeneratedForm = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, + config: { priority: ["0"] }, }); handleModalToggle?.(); addAlert(t("saveProviderSuccess"), AlertVariant.success); @@ -116,7 +117,7 @@ export const AESGeneratedForm = ({ ); const allComponentTypes = - serverInfo.componentTypes?.["org.keycloak.keys.KeyProvider"] ?? []; + serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? []; const aesSecretSizeOptions = allComponentTypes[0].properties[3].options; diff --git a/src/realm-settings/key-providers/ecdsa-generated/ECDSAGeneratedForm.tsx b/src/realm-settings/key-providers/ecdsa-generated/ECDSAGeneratedForm.tsx index e4478990e1..aa7365ea7c 100644 --- a/src/realm-settings/key-providers/ecdsa-generated/ECDSAGeneratedForm.tsx +++ b/src/realm-settings/key-providers/ecdsa-generated/ECDSAGeneratedForm.tsx @@ -22,7 +22,7 @@ import { useAdminClient, useFetch } from "../../../context/auth/AdminClient"; import { useParams, useRouteMatch } from "react-router-dom"; import { FormAccess } from "../../../components/form-access/FormAccess"; import { ViewHeader } from "../../../components/view-header/ViewHeader"; -import { convertToFormValues } from "../../../util"; +import { convertToFormValues, KEY_PROVIDER_TYPE } from "../../../util"; import { useAlerts } from "../../../components/alert/Alerts"; type ECDSAGeneratedFormProps = { @@ -63,7 +63,7 @@ export const ECDSAGeneratedForm = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, } ); addAlert(t("saveProviderSuccess"), AlertVariant.success); @@ -72,7 +72,8 @@ export const ECDSAGeneratedForm = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, + config: { priority: ["0"] }, }); handleModalToggle?.(); addAlert(t("saveProviderSuccess"), AlertVariant.success); @@ -116,7 +117,7 @@ export const ECDSAGeneratedForm = ({ ); const allComponentTypes = - serverInfo.componentTypes?.["org.keycloak.keys.KeyProvider"] ?? []; + serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? []; const ecdsaEllipticCurveOptions = allComponentTypes[1].properties[3].options; diff --git a/src/realm-settings/key-providers/hmac-generated/HMACGeneratedForm.tsx b/src/realm-settings/key-providers/hmac-generated/HMACGeneratedForm.tsx index 84428839be..bf7fa13819 100644 --- a/src/realm-settings/key-providers/hmac-generated/HMACGeneratedForm.tsx +++ b/src/realm-settings/key-providers/hmac-generated/HMACGeneratedForm.tsx @@ -22,7 +22,7 @@ import { useAdminClient, useFetch } from "../../../context/auth/AdminClient"; import { useParams, useRouteMatch } from "react-router-dom"; import { FormAccess } from "../../../components/form-access/FormAccess"; import { ViewHeader } from "../../../components/view-header/ViewHeader"; -import { convertToFormValues } from "../../../util"; +import { convertToFormValues, KEY_PROVIDER_TYPE } from "../../../util"; import { useAlerts } from "../../../components/alert/Alerts"; type HMACGeneratedFormProps = { @@ -65,7 +65,7 @@ export const HMACGeneratedForm = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, } ); addAlert(t("saveProviderSuccess"), AlertVariant.success); @@ -74,7 +74,8 @@ export const HMACGeneratedForm = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, + config: { priority: ["0"] }, }); handleModalToggle?.(); addAlert(t("saveProviderSuccess"), AlertVariant.success); @@ -121,7 +122,7 @@ export const HMACGeneratedForm = ({ ); const allComponentTypes = - serverInfo.componentTypes?.["org.keycloak.keys.KeyProvider"] ?? []; + serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? []; const hmacSecretSizeOptions = allComponentTypes[2].properties[3].options; diff --git a/src/realm-settings/key-providers/java-keystore/JavaKeystoreForm.tsx b/src/realm-settings/key-providers/java-keystore/JavaKeystoreForm.tsx index 5e96414be8..cc2f8a4da3 100644 --- a/src/realm-settings/key-providers/java-keystore/JavaKeystoreForm.tsx +++ b/src/realm-settings/key-providers/java-keystore/JavaKeystoreForm.tsx @@ -22,7 +22,7 @@ import { useAdminClient, useFetch } from "../../../context/auth/AdminClient"; import { useParams, useRouteMatch } from "react-router-dom"; import { FormAccess } from "../../../components/form-access/FormAccess"; import { ViewHeader } from "../../../components/view-header/ViewHeader"; -import { convertToFormValues } from "../../../util"; +import { convertToFormValues, KEY_PROVIDER_TYPE } from "../../../util"; import { useAlerts } from "../../../components/alert/Alerts"; type JavaKeystoreFormProps = { @@ -63,7 +63,7 @@ export const JavaKeystoreForm = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, } ); addAlert(t("saveProviderSuccess"), AlertVariant.success); @@ -72,7 +72,8 @@ export const JavaKeystoreForm = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, + config: { priority: ["0"] }, }); handleModalToggle?.(); addAlert(t("saveProviderSuccess"), AlertVariant.success); @@ -129,7 +130,7 @@ export const JavaKeystoreForm = ({ ); const allComponentTypes = - serverInfo.componentTypes?.["org.keycloak.keys.KeyProvider"] ?? []; + serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? []; const javaKeystoreAlgorithmOptions = allComponentTypes[3].properties[3].options; diff --git a/src/realm-settings/key-providers/rsa-generated/RSAGeneratedForm.tsx b/src/realm-settings/key-providers/rsa-generated/RSAGeneratedForm.tsx index 05c17281b3..f9c822bb21 100644 --- a/src/realm-settings/key-providers/rsa-generated/RSAGeneratedForm.tsx +++ b/src/realm-settings/key-providers/rsa-generated/RSAGeneratedForm.tsx @@ -22,7 +22,7 @@ import { useAdminClient, useFetch } from "../../../context/auth/AdminClient"; import { useParams, useRouteMatch } from "react-router-dom"; import { FormAccess } from "../../../components/form-access/FormAccess"; import { ViewHeader } from "../../../components/view-header/ViewHeader"; -import { convertToFormValues } from "../../../util"; +import { convertToFormValues, KEY_PROVIDER_TYPE } from "../../../util"; import { useAlerts } from "../../../components/alert/Alerts"; type RSAGeneratedFormProps = { @@ -65,7 +65,7 @@ export const RSAGeneratedForm = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, } ); addAlert(t("saveProviderSuccess"), AlertVariant.success); @@ -74,7 +74,8 @@ export const RSAGeneratedForm = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, + config: { priority: ["0"] }, }); handleModalToggle?.(); addAlert(t("saveProviderSuccess"), AlertVariant.success); @@ -121,7 +122,7 @@ export const RSAGeneratedForm = ({ ); const allComponentTypes = - serverInfo.componentTypes?.["org.keycloak.keys.KeyProvider"] ?? []; + serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? []; const rsaGeneratedKeySizeOptions = allComponentTypes[5].properties[4].options!; diff --git a/src/realm-settings/key-providers/rsa/RSAForm.tsx b/src/realm-settings/key-providers/rsa/RSAForm.tsx index 7f5e5c3107..f5438a3c71 100644 --- a/src/realm-settings/key-providers/rsa/RSAForm.tsx +++ b/src/realm-settings/key-providers/rsa/RSAForm.tsx @@ -23,7 +23,7 @@ import { useAdminClient, useFetch } from "../../../context/auth/AdminClient"; import { useParams, useRouteMatch } from "react-router-dom"; import { FormAccess } from "../../../components/form-access/FormAccess"; import { ViewHeader } from "../../../components/view-header/ViewHeader"; -import { convertToFormValues } from "../../../util"; +import { convertToFormValues, KEY_PROVIDER_TYPE } from "../../../util"; import { useAlerts } from "../../../components/alert/Alerts"; type RSAFormProps = { @@ -70,7 +70,7 @@ export const RSAForm = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, } ); addAlert(t("saveProviderSuccess"), AlertVariant.success); @@ -79,7 +79,8 @@ export const RSAForm = ({ ...component, parentId: component.parentId, providerId: providerType, - providerType: "org.keycloak.keys.KeyProvider", + providerType: KEY_PROVIDER_TYPE, + config: { priority: ["0"] }, }); handleModalToggle?.(); addAlert(t("saveProviderSuccess"), AlertVariant.success); @@ -133,7 +134,7 @@ export const RSAForm = ({ ); const allComponentTypes = - serverInfo.componentTypes?.["org.keycloak.keys.KeyProvider"] ?? []; + serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? []; const rsaAlgOptions = allComponentTypes[4].properties[3].options; diff --git a/src/realm-settings/messages.ts b/src/realm-settings/messages.ts index 32ea0d2d4a..dfd42937bb 100644 --- a/src/realm-settings/messages.ts +++ b/src/realm-settings/messages.ts @@ -4,6 +4,7 @@ export default { partialExport: "Partial export", deleteRealm: "Delete realm", deleteConfirmTitle: "Delete realm?", + dragInstruction: "Click and drag to change priority", deleteConfirm: "If you delete this realm, all associated data will be removed.", deleteProviderTitle: "Delete key provider?", @@ -19,6 +20,8 @@ export default { editProvider: "Edit provider", saveSuccess: "Realm successfully updated", saveProviderSuccess: "The provider has been saved successfully.", + saveProviderListSuccess: + "The priority of the provider has been updated successfully.", saveProviderError: "Error saving provider: ", saveError: "Realm could not be updated: {error}", general: "General", diff --git a/src/util.ts b/src/util.ts index f586dc66fd..ad2e9a658d 100644 --- a/src/util.ts +++ b/src/util.ts @@ -89,7 +89,6 @@ export const convertFormValuesToObject = ( const newKey = firstInstanceOnly ? key.replace(/-/, ".") : key.replace(/-/g, "."); - console.log(newKey); return { [newKey]: obj[key] }; }); return Object.assign({}, ...keyValues); @@ -189,3 +188,5 @@ export const interpolateTimespan = (forHumans: string) => { }); } }; + +export const KEY_PROVIDER_TYPE = "org.keycloak.keys.KeyProvider";