diff --git a/src/main/java/de/adorsys/keycloak/config/factory/UsedAuthenticationFlowWorkaroundFactory.java b/src/main/java/de/adorsys/keycloak/config/factory/UsedAuthenticationFlowWorkaroundFactory.java index 4bd3848a0..10869fd9b 100644 --- a/src/main/java/de/adorsys/keycloak/config/factory/UsedAuthenticationFlowWorkaroundFactory.java +++ b/src/main/java/de/adorsys/keycloak/config/factory/UsedAuthenticationFlowWorkaroundFactory.java @@ -22,10 +22,12 @@ import de.adorsys.keycloak.config.model.RealmImport; import de.adorsys.keycloak.config.repository.AuthenticationFlowRepository; +import de.adorsys.keycloak.config.repository.ClientRepository; import de.adorsys.keycloak.config.repository.IdentityProviderRepository; import de.adorsys.keycloak.config.repository.RealmRepository; import org.apache.logging.log4j.util.Strings; import org.keycloak.representations.idm.AuthenticationFlowRepresentation; +import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.slf4j.Logger; @@ -41,16 +43,19 @@ public class UsedAuthenticationFlowWorkaroundFactory { private final RealmRepository realmRepository; private final IdentityProviderRepository identityProviderRepository; private final AuthenticationFlowRepository authenticationFlowRepository; + private final ClientRepository clientRepository; @Autowired public UsedAuthenticationFlowWorkaroundFactory( RealmRepository realmRepository, IdentityProviderRepository identityProviderRepository, - AuthenticationFlowRepository authenticationFlowRepository + AuthenticationFlowRepository authenticationFlowRepository, + ClientRepository clientRepository ) { this.realmRepository = realmRepository; this.identityProviderRepository = identityProviderRepository; this.authenticationFlowRepository = authenticationFlowRepository; + this.clientRepository = clientRepository; } public UsedAuthenticationFlowWorkaround buildFor(RealmImport realmImport) { @@ -93,6 +98,56 @@ public void disableTopLevelFlowIfNeeded(String topLevelFlowAlias) { disablePostBrokerLoginFlowsIfNeeded(topLevelFlowAlias, existingRealm); } + /** + * Find and remove flow overrides with specified ID in all realm clients. + * + * @param flowId flow ID to remove overrides + * @return Map "client" -> "auth name" -> "flow id" which were removed. Used to restore overrides. + */ + public Map> removeFlowOverridesInClients(String flowId) { + final Map> clientsWithFlow = new HashMap<>(); + // For all clients + for (ClientRepresentation client : clientRepository.getAll(realmImport.getRealm())) { + boolean updateClient = false; + final Map authenticationFlowBindingOverrides = client.getAuthenticationFlowBindingOverrides(); + // Search overrides with flowId + for (Map.Entry flowBinding : authenticationFlowBindingOverrides.entrySet()) { + if (flowId.equals(flowBinding.getValue())) { + final Map clientBinding = clientsWithFlow.computeIfAbsent(client.getClientId(), k -> new HashMap<>()); + // Save override and ... + clientBinding.put(flowBinding.getKey(), flowBinding.getValue()); + // Set null to the value to remove this override on update + authenticationFlowBindingOverrides.put(flowBinding.getKey(), null); + updateClient = true; + } + } + // Update client only if needed + if (updateClient) { + clientRepository.update(realmImport.getRealm(), client); + } + } + + return clientsWithFlow; + } + + /** + * Restore flow overrides in clients. + * + * @param clientsWithFlow map "client" -> "auth name" -> "flow id" to restore flow overrides. + */ + public void restoreClientOverrides(Map> clientsWithFlow) { + // restore overrides with the new patched flow + for (Map.Entry> clientWithFlow : clientsWithFlow.entrySet()) { + final String clientId = clientWithFlow.getKey(); + final Map overrides = clientWithFlow.getValue(); + + final ClientRepresentation client = clientRepository.getByClientId(realmImport.getRealm(), clientId); + // Add all overrides with patched flow to existing overrides + client.getAuthenticationFlowBindingOverrides().putAll(overrides); + clientRepository.update(realmImport.getRealm(), client); + } + } + private void disableBrowserFlowIfNeeded(String topLevelFlowAlias, RealmRepresentation existingRealm) { if (Objects.equals(existingRealm.getBrowserFlow(), topLevelFlowAlias)) { logger.debug( diff --git a/src/main/java/de/adorsys/keycloak/config/service/AuthenticationFlowsImportService.java b/src/main/java/de/adorsys/keycloak/config/service/AuthenticationFlowsImportService.java index 52fabee73..d30f240f1 100644 --- a/src/main/java/de/adorsys/keycloak/config/service/AuthenticationFlowsImportService.java +++ b/src/main/java/de/adorsys/keycloak/config/service/AuthenticationFlowsImportService.java @@ -39,6 +39,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -313,10 +314,14 @@ private void recreateTopLevelFlow( UsedAuthenticationFlowWorkaroundFactory.UsedAuthenticationFlowWorkaround workaround = workaroundFactory.buildFor(realmImport); workaround.disableTopLevelFlowIfNeeded(topLevelFlowToImport.getAlias()); + final Map> overrides = workaround.removeFlowOverridesInClients(patchedAuthenticationFlow.getId()); + authenticatorConfigImportService.deleteAuthenticationConfigs(realmImport, patchedAuthenticationFlow); authenticationFlowRepository.delete(realmImport.getRealm(), patchedAuthenticationFlow.getId()); authenticationFlowRepository.createTopLevel(realmImport.getRealm(), patchedAuthenticationFlow); + workaround.restoreClientOverrides(overrides); + AuthenticationFlowRepresentation createdTopLevelFlow = authenticationFlowRepository.getByAlias( realmImport.getRealm(), topLevelFlowToImport.getAlias() ); diff --git a/src/test/java/de/adorsys/keycloak/config/AbstractImportIT.java b/src/test/java/de/adorsys/keycloak/config/AbstractImportIT.java index ac1bededc..d835860c0 100644 --- a/src/test/java/de/adorsys/keycloak/config/AbstractImportIT.java +++ b/src/test/java/de/adorsys/keycloak/config/AbstractImportIT.java @@ -61,6 +61,7 @@ abstract public class AbstractImportIT extends AbstractImportTest { .withEnv("KEYCLOAK_ADMIN", "admin") .withEnv("KEYCLOAK_ADMIN_PASSWORD", "admin123") .withEnv("QUARKUS_PROFILE", "dev") + .withEnv("KC_LOG_LEVEL", KEYCLOAK_LOG_LEVEL) .withExtraHost("host.docker.internal", "host-gateway") .waitingFor(Wait.forHttp("/")) .withStartupTimeout(Duration.ofSeconds(300));