From e4b30b6752b329db1f86d53833dc96e35e736431 Mon Sep 17 00:00:00 2001 From: Simon Levermann Date: Wed, 8 Mar 2023 09:49:12 +0100 Subject: [PATCH] Better handling for roles --- .../NormalizationConfiguration.java | 7 +- .../normalize/GroupNormalizationService.java | 1 + .../normalize/RoleNormalizationService.java | 114 +++++++++++------- 3 files changed, 78 insertions(+), 44 deletions(-) diff --git a/src/main/java/de/adorsys/keycloak/config/configuration/NormalizationConfiguration.java b/src/main/java/de/adorsys/keycloak/config/configuration/NormalizationConfiguration.java index 4bdf72048..3de29fc23 100644 --- a/src/main/java/de/adorsys/keycloak/config/configuration/NormalizationConfiguration.java +++ b/src/main/java/de/adorsys/keycloak/config/configuration/NormalizationConfiguration.java @@ -22,6 +22,8 @@ import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; import org.javers.core.Javers; import org.javers.core.JaversBuilder; @@ -66,6 +68,8 @@ public Javers unOrderedJavers() { public YAMLMapper yamlMapper() { var ym = new YAMLMapper(); ym.setSerializationInclusion(JsonInclude.Include.NON_NULL); + ym.enable(SerializationFeature.INDENT_OUTPUT); + ym.enable(YAMLGenerator.Feature.INDENT_ARRAYS_WITH_INDICATOR); return ym; } @@ -73,6 +77,7 @@ public YAMLMapper yamlMapper() { public ObjectMapper objectMapper() { var om = new ObjectMapper(); om.setSerializationInclusion(JsonInclude.Include.NON_NULL); + om.enable(SerializationFeature.INDENT_OUTPUT); return om; } @@ -111,7 +116,7 @@ private JaversBuilder commonJavers() { List.of("id", "authorizationSettings", "protocolMappers"))) .registerEntity(new EntityDefinition(ProtocolMapperRepresentation.class, "name", List.of("id"))) .registerEntity(new EntityDefinition(ClientScopeRepresentation.class, "name", List.of("id", "protocolMappers"))) - .registerEntity(new EntityDefinition(RoleRepresentation.class, "name", List.of("id", "containerId", "composites"))) + .registerEntity(new EntityDefinition(RoleRepresentation.class, "name", List.of("id", "containerId", "composites", "attributes"))) .registerEntity(new EntityDefinition(GroupRepresentation.class, "path", List.of("id", "subGroups", "attributes", "clientRoles"))) .registerEntity(new EntityDefinition(AuthenticationFlowRepresentation.class, "alias", List.of("id", "authenticationExecutions"))) .registerEntity(new EntityDefinition(IdentityProviderRepresentation.class, "alias", List.of("internalId"))) diff --git a/src/main/java/de/adorsys/keycloak/config/service/normalize/GroupNormalizationService.java b/src/main/java/de/adorsys/keycloak/config/service/normalize/GroupNormalizationService.java index 520006c1b..0e08894a6 100644 --- a/src/main/java/de/adorsys/keycloak/config/service/normalize/GroupNormalizationService.java +++ b/src/main/java/de/adorsys/keycloak/config/service/normalize/GroupNormalizationService.java @@ -145,6 +145,7 @@ public void normalizeGroupList(List groups) { normalizeGroupList(group.getSubGroups()); } } + group.setId(null); } } } diff --git a/src/main/java/de/adorsys/keycloak/config/service/normalize/RoleNormalizationService.java b/src/main/java/de/adorsys/keycloak/config/service/normalize/RoleNormalizationService.java index 49e302cd8..e57521ccf 100644 --- a/src/main/java/de/adorsys/keycloak/config/service/normalize/RoleNormalizationService.java +++ b/src/main/java/de/adorsys/keycloak/config/service/normalize/RoleNormalizationService.java @@ -31,6 +31,7 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Service; +import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -61,69 +62,96 @@ public RolesRepresentation normalizeRoles(RolesRepresentation exportedRoles, Rol var baselineOrEmpty = baselineRoles == null ? new RolesRepresentation() : baselineRoles; var clientRoles = normalizeClientRoles(exportedOrEmpty.getClient(), baselineOrEmpty.getClient()); var realmRoles = normalizeRealmRoles(exportedOrEmpty.getRealm(), baselineOrEmpty.getRealm()); - if (clientRoles == null && realmRoles == null) { - return null; - } var normalizedRoles = new RolesRepresentation(); - normalizedRoles.setClient(clientRoles); - normalizedRoles.setRealm(realmRoles); + if (!clientRoles.isEmpty()) { + normalizedRoles.setClient(clientRoles); + } + if (!realmRoles.isEmpty()) { + normalizedRoles.setRealm(realmRoles); + } return normalizedRoles; } - public List normalizeRealmRoles(List exportedRealmRoles, List baselineRealmRoles) { - List exportedOrEmpty = exportedRealmRoles == null ? List.of() : exportedRealmRoles; - List baselineOrEmpty = baselineRealmRoles == null ? List.of() : baselineRealmRoles; + public List normalizeRealmRoles(List exportedRoles, List baselineRoles) { + return normalizeRoleList(exportedRoles, baselineRoles, null); + } + + public Map> normalizeClientRoles(Map> exportedRoles, Map> baselineRoles) { + Map> exportedOrEmpty = exportedRoles == null ? Map.of() : exportedRoles; + Map> baselineOrEmpty = baselineRoles == null ? Map.of() : baselineRoles; + + var normalizedRoles = new HashMap>(); + for (var entry : baselineOrEmpty.entrySet()) { + var clientId = entry.getKey(); + var baselineClientRoles = entry.getValue(); + var exportedClientRoles = exportedOrEmpty.remove(clientId); + exportedClientRoles = exportedClientRoles == null ? List.of() : exportedClientRoles; + + var normalizedClientRoles = normalizeRoleList(exportedClientRoles, baselineClientRoles, clientId); + if (!normalizedClientRoles.isEmpty()) { + normalizedRoles.put(clientId, normalizedClientRoles); + } + } + + for (var entry : exportedOrEmpty.entrySet()) { + var clientId = entry.getKey(); + var roles = entry.getValue(); + + if (!roles.isEmpty()) { + normalizedRoles.put(clientId, normalizeList(roles)); + } + } + return normalizedRoles; + } - var exportedMap = exportedOrEmpty.stream().collect(Collectors.toMap(RoleRepresentation::getName, Function.identity())); - var baselineMap = baselineOrEmpty.stream().collect(Collectors.toMap(RoleRepresentation::getName, Function.identity())); + public List normalizeRoleList(List exportedRoles, List baselineRoles, String clientId) { + List exportedOrEmpty = exportedRoles == null ? List.of() : exportedRoles; + List baselineOrEmpty = baselineRoles == null ? List.of() : baselineRoles; + var exportedMap = exportedOrEmpty.stream() + .collect(Collectors.toMap(RoleRepresentation::getName, Function.identity())); + var baselineMap = baselineOrEmpty.stream() + .collect(Collectors.toMap(RoleRepresentation::getName, Function.identity())); var normalizedRoles = new ArrayList(); for (var entry : baselineMap.entrySet()) { var roleName = entry.getKey(); - var baselineRole = entry.getValue(); var exportedRole = exportedMap.remove(roleName); if (exportedRole == null) { - logger.warn("Default realm role '{}' was deleted in exported realm. It may be reintroduced during import", roleName); + if (clientId == null) { + logger.warn("Default realm role '{}' was deleted in exported realm. It may be reintroduced during import!", roleName); + } else { + logger.warn("Default realm client-role '{}' for client '{}' was deleted in the exported realm. " + + "It may be reintroduced during import!", roleName, clientId); + } continue; } + + var baselineRole = entry.getValue(); + var diff = unOrderedJavers.compare(baselineRole, exportedRole); + if (diff.hasChanges() - || attributeNormalizationService.listAttributesChanged(exportedRole.getAttributes(), baselineRole.getAttributes()) - || compositesChanged(exportedRole.getComposites(), baselineRole.getComposites())) { - var normalizedRole = new RoleRepresentation(); - normalizedRole.setName(roleName); - for (var change : diff.getChangesByType(PropertyChange.class)) { - javersUtil.applyChange(normalizedRole, change); - } - normalizedRole.setAttributes(attributeNormalizationService.normalizeListAttributes(exportedRole.getAttributes(), - baselineRole.getAttributes())); - normalizedRoles.add(normalizedRole); - normalizedRole.setComposites(exportedRole.getComposites()); + || compositesChanged(exportedRole.getComposites(), baselineRole.getComposites()) + || attributeNormalizationService.listAttributesChanged(exportedRole.getAttributes(), baselineRole.getAttributes())) { + normalizedRoles.add(exportedRole); } } - return normalizedRoles.isEmpty() ? null : normalizedRoles; + normalizedRoles.addAll(exportedMap.values()); + return normalizeList(normalizedRoles); } - private boolean compositesChanged(RoleRepresentation.Composites exportedComposites, RoleRepresentation.Composites baselineComposites) { - return unOrderedJavers.compare(baselineComposites, exportedComposites).hasChanges(); - } - - public Map> normalizeClientRoles(Map> exportedClientRoles, - Map> baselineClientRoles) { - Map> exportedOrEmpty = exportedClientRoles == null ? Map.of() : exportedClientRoles; - Map> baselineOrEmpty = baselineClientRoles == null ? Map.of() : baselineClientRoles; - - Map> normalizedClientRoles = new HashMap<>(); - for (var entry : baselineOrEmpty.entrySet()) { - var clientName = entry.getKey(); - var baselineRoles = entry.getValue(); - var exportedRoles = exportedOrEmpty.remove(clientName); - - var normalizedRoles = normalizeRealmRoles(exportedRoles, baselineRoles); - if (normalizedRoles != null) { - normalizedClientRoles.put(clientName, normalizedRoles); + public List normalizeList(List roles) { + for (var role : roles) { + role.setId(null); + if (role.getAttributes().isEmpty()) { + role.setAttributes(null); } } - return normalizedClientRoles.isEmpty() ? null : normalizedClientRoles; + return roles; + } + + public boolean compositesChanged(RoleRepresentation.Composites exportedComposites, RoleRepresentation.Composites baselineComposites) { + return unOrderedJavers.compare(baselineComposites, exportedComposites) + .hasChanges(); } }