From c9b3ad8923bd580f938dc37cf7bc2022d4c4a54f Mon Sep 17 00:00:00 2001 From: Marc Scheib Date: Tue, 16 Apr 2024 17:15:49 +0200 Subject: [PATCH] fix: add a custom component class to resolve serialization issues with JSONB --- .../configure/boundary/ComponentImporter.java | 8 +-- .../entity/CustomComponentRepresentation.java | 52 +++++++++++++++++++ 2 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/cycrilabs/keycloak/configurator/commands/configure/entity/CustomComponentRepresentation.java diff --git a/src/main/java/com/cycrilabs/keycloak/configurator/commands/configure/boundary/ComponentImporter.java b/src/main/java/com/cycrilabs/keycloak/configurator/commands/configure/boundary/ComponentImporter.java index 37aa55f..ee3015b 100644 --- a/src/main/java/com/cycrilabs/keycloak/configurator/commands/configure/boundary/ComponentImporter.java +++ b/src/main/java/com/cycrilabs/keycloak/configurator/commands/configure/boundary/ComponentImporter.java @@ -9,6 +9,7 @@ import org.keycloak.representations.idm.ComponentRepresentation; +import com.cycrilabs.keycloak.configurator.commands.configure.entity.CustomComponentRepresentation; import com.cycrilabs.keycloak.configurator.shared.control.JsonUtil; import com.cycrilabs.keycloak.configurator.shared.entity.EntityType; @@ -23,13 +24,12 @@ public EntityType getType() { @Override protected Object importFile(final Path file) { - final ComponentRepresentation component = - JsonUtil.loadEntity(file, ComponentRepresentation.class); + final CustomComponentRepresentation component = JsonUtil.loadEntity(file, CustomComponentRepresentation.class); final String[] fileNameParts = file.toString().split(PATH_SEPARATOR); final String realmName = fileNameParts[fileNameParts.length - 3]; - if (component.getParentId() != null) { + if (component.getParentId() != null && !component.getParentId().equals(realmName)) { final ComponentRepresentation parent = findComponentByName(realmName, component.getParentId()); if (parent == null) { @@ -45,7 +45,7 @@ protected Object importFile(final Path file) { try (final Response response = keycloak.realm(realmName) .components() - .add(component)) { + .add(component.toAPI())) { if (response.getStatus() == 409) { Log.errorf("Could not import component from file for realm '%s': %s", realmName, extractError(response).getErrorMessage()); diff --git a/src/main/java/com/cycrilabs/keycloak/configurator/commands/configure/entity/CustomComponentRepresentation.java b/src/main/java/com/cycrilabs/keycloak/configurator/commands/configure/entity/CustomComponentRepresentation.java new file mode 100644 index 0000000..4c8f683 --- /dev/null +++ b/src/main/java/com/cycrilabs/keycloak/configurator/commands/configure/entity/CustomComponentRepresentation.java @@ -0,0 +1,52 @@ +package com.cycrilabs.keycloak.configurator.commands.configure.entity; + +import java.util.List; +import java.util.Map; + +import lombok.Getter; +import lombok.Setter; + +import org.keycloak.common.util.MultivaluedHashMap; +import org.keycloak.representations.idm.ComponentRepresentation; + +/** + * Custom representation of a Keycloak component that avoids MultivaluedHashMap for the config property. + * This class can't be serialized/deserialized out-of-the-box by JSONB. + */ +@Getter +@Setter +public class CustomComponentRepresentation { + + private String id; + private String name; + private String providerId; + private String providerType; + private String parentId; + private String subType; + private Map> config; + + public ComponentRepresentation toAPI() { + final ComponentRepresentation componentRepresentation = new ComponentRepresentation(); + componentRepresentation.setId(id); + componentRepresentation.setName(name); + componentRepresentation.setProviderId(providerId); + componentRepresentation.setProviderType(providerType); + componentRepresentation.setParentId(parentId); + componentRepresentation.setSubType(subType); + componentRepresentation.setConfig(new MultivaluedHashMap<>(config)); + return componentRepresentation; + } + + public static CustomComponentRepresentation fromAPI(final ComponentRepresentation componentRepresentation) { + final CustomComponentRepresentation customComponentRepresentation = new CustomComponentRepresentation(); + customComponentRepresentation.setId(componentRepresentation.getId()); + customComponentRepresentation.setName(componentRepresentation.getName()); + customComponentRepresentation.setProviderId(componentRepresentation.getProviderId()); + customComponentRepresentation.setProviderType(componentRepresentation.getProviderType()); + customComponentRepresentation.setParentId(componentRepresentation.getParentId()); + customComponentRepresentation.setSubType(componentRepresentation.getSubType()); + customComponentRepresentation.setConfig(componentRepresentation.getConfig()); + return customComponentRepresentation; + } + +}