From 3328a8b2098dbd55ae6931bc8ddcc1ce2db3addf Mon Sep 17 00:00:00 2001 From: Simon Levermann Date: Wed, 21 Dec 2022 11:23:54 +0100 Subject: [PATCH] Normalize Required Actions --- .../NormalizationConfiguration.java | 4 +- .../normalize/RealmNormalizationService.java | 6 ++ .../RequiredActionNormalizationService.java | 86 +++++++++++++++++++ 3 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/adorsys/keycloak/config/service/normalize/RequiredActionNormalizationService.java 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 4ab27149f..4bdf72048 100644 --- a/src/main/java/de/adorsys/keycloak/config/configuration/NormalizationConfiguration.java +++ b/src/main/java/de/adorsys/keycloak/config/configuration/NormalizationConfiguration.java @@ -35,6 +35,7 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.ProtocolMapperRepresentation; import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.representations.idm.RequiredActionProviderRepresentation; import org.keycloak.representations.idm.RoleRepresentation; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; @@ -114,6 +115,7 @@ private JaversBuilder commonJavers() { .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"))) - .registerEntity(new EntityDefinition(IdentityProviderMapperRepresentation.class, "name", List.of("id"))); + .registerEntity(new EntityDefinition(IdentityProviderMapperRepresentation.class, "name", List.of("id"))) + .registerEntity(new EntityDefinition(RequiredActionProviderRepresentation.class, "alias")); } } diff --git a/src/main/java/de/adorsys/keycloak/config/service/normalize/RealmNormalizationService.java b/src/main/java/de/adorsys/keycloak/config/service/normalize/RealmNormalizationService.java index 48234aca0..0dcdd4d97 100644 --- a/src/main/java/de/adorsys/keycloak/config/service/normalize/RealmNormalizationService.java +++ b/src/main/java/de/adorsys/keycloak/config/service/normalize/RealmNormalizationService.java @@ -52,6 +52,7 @@ public class RealmNormalizationService { private final GroupNormalizationService groupNormalizationService; private final AuthFlowNormalizationService authFlowNormalizationService; private final IdentityProviderNormalizationService identityProviderNormalizationService; + private final RequiredActionNormalizationService requiredActionNormalizationService; private final JaversUtil javersUtil; @Autowired @@ -67,6 +68,7 @@ public RealmNormalizationService(NormalizationKeycloakConfigProperties keycloakC GroupNormalizationService groupNormalizationService, AuthFlowNormalizationService authFlowNormalizationService, IdentityProviderNormalizationService identityProviderNormalizationService, + RequiredActionNormalizationService requiredActionNormalizationService, JaversUtil javersUtil) { this.keycloakConfigProperties = keycloakConfigProperties; this.javers = javers; @@ -80,6 +82,7 @@ public RealmNormalizationService(NormalizationKeycloakConfigProperties keycloakC this.groupNormalizationService = groupNormalizationService; this.authFlowNormalizationService = authFlowNormalizationService; this.identityProviderNormalizationService = identityProviderNormalizationService; + this.requiredActionNormalizationService = requiredActionNormalizationService; this.javersUtil = javersUtil; // TODO allow extra "default" values to be ignored? @@ -154,6 +157,9 @@ public RealmRepresentation normalizeRealm(RealmRepresentation exportedRealm) { baselineRealm.getIdentityProviders())); minimizedRealm.setIdentityProviderMappers(identityProviderNormalizationService.normalizeMappers(exportedRealm.getIdentityProviderMappers(), baselineRealm.getIdentityProviderMappers())); + + minimizedRealm.setRequiredActions(requiredActionNormalizationService.normalizeRequiredActions(exportedRealm.getRequiredActions(), + baselineRealm.getRequiredActions())); return minimizedRealm; } diff --git a/src/main/java/de/adorsys/keycloak/config/service/normalize/RequiredActionNormalizationService.java b/src/main/java/de/adorsys/keycloak/config/service/normalize/RequiredActionNormalizationService.java new file mode 100644 index 000000000..410ed9359 --- /dev/null +++ b/src/main/java/de/adorsys/keycloak/config/service/normalize/RequiredActionNormalizationService.java @@ -0,0 +1,86 @@ +/*- + * ---license-start + * keycloak-config-cli + * --- + * Copyright (C) 2017 - 2022 adorsys GmbH & Co. KG @ https://adorsys.com + * --- + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * ---license-end + */ + +package de.adorsys.keycloak.config.service.normalize; + +import de.adorsys.keycloak.config.util.JaversUtil; +import org.javers.core.Javers; +import org.javers.core.diff.changetype.PropertyChange; +import org.keycloak.representations.idm.RequiredActionProviderRepresentation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; + +@Service +@ConditionalOnProperty(prefix = "run", name = "operation", havingValue = "NORMALIZE") +public class RequiredActionNormalizationService { + + private static final Logger logger = LoggerFactory.getLogger(RequiredActionNormalizationService.class); + + private final Javers javers; + private final JaversUtil javersUtil; + + public RequiredActionNormalizationService(Javers javers, JaversUtil javersUtil) { + this.javers = javers; + this.javersUtil = javersUtil; + } + + public List normalizeRequiredActions(List exportedActions, + List baselineActions) { + List exportedOrEmpty = exportedActions == null ? List.of() : exportedActions; + List baselineOrEmpty = baselineActions == null ? List.of() : baselineActions; + + var exportedMap = exportedOrEmpty.stream() + .collect(Collectors.toMap(RequiredActionProviderRepresentation::getAlias, Function.identity())); + var baselineMap = baselineOrEmpty.stream() + .collect(Collectors.toMap(RequiredActionProviderRepresentation::getAlias, Function.identity())); + + var normalizedActions = new ArrayList(); + for (var entry : baselineMap.entrySet()) { + var alias = entry.getKey(); + var exportedAction = exportedMap.remove(alias); + if (exportedAction == null) { + logger.warn("Default realm requiredAction '{}' was deleted in exported realm. It may be reintroduced during import", alias); + continue; + } + var baselineAction = entry.getValue(); + + var diff = javers.compare(baselineAction, exportedAction); + if (diff.hasChanges()) { + var normalizedAction = new RequiredActionProviderRepresentation(); + normalizedAction.setAlias(alias); + for (var change : diff.getChangesByType(PropertyChange.class)) { + javersUtil.applyChange(normalizedAction, change); + } + normalizedAction.setEnabled(exportedAction.isEnabled()); + normalizedAction.setDefaultAction(exportedAction.isDefaultAction()); + normalizedActions.add(normalizedAction); + } + } + normalizedActions.addAll(exportedMap.values()); + return normalizedActions.isEmpty() ? null : normalizedActions; + } +}