From b81838353acc0ef59e05168478391a3caa28f031 Mon Sep 17 00:00:00 2001 From: Tolya Date: Wed, 13 Nov 2024 13:58:35 +0300 Subject: [PATCH] Use temp auth flow instead remove overrides (remove the gap in import config when client without overrides). --- ...edAuthenticationFlowWorkaroundFactory.java | 56 +++++++++++++++++-- .../AuthenticationFlowsImportService.java | 2 +- 2 files changed, 52 insertions(+), 6 deletions(-) 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 10869fd9b..e7ee4b673 100644 --- a/src/main/java/de/adorsys/keycloak/config/factory/UsedAuthenticationFlowWorkaroundFactory.java +++ b/src/main/java/de/adorsys/keycloak/config/factory/UsedAuthenticationFlowWorkaroundFactory.java @@ -25,6 +25,7 @@ import de.adorsys.keycloak.config.repository.ClientRepository; import de.adorsys.keycloak.config.repository.IdentityProviderRepository; import de.adorsys.keycloak.config.repository.RealmRepository; +import de.adorsys.keycloak.config.util.CloneUtil; import org.apache.logging.log4j.util.Strings; import org.keycloak.representations.idm.AuthenticationFlowRepresentation; import org.keycloak.representations.idm.ClientRepresentation; @@ -70,6 +71,7 @@ public UsedAuthenticationFlowWorkaround buildFor(RealmImport realmImport) { */ public class UsedAuthenticationFlowWorkaround { private static final String TEMPORARY_CREATED_AUTH_FLOW = "TEMPORARY_CREATED_AUTH_FLOW"; + private static final String TEMPORARY_CREATED_CLIENT_AUTH_FLOW = "TEMPORARY_CREATED_CLIENT_AUTH_FLOW"; private final Logger logger = LoggerFactory.getLogger(UsedAuthenticationFlowWorkaround.class); private final RealmImport realmImport; private final Map resetFirstBrokerLoginFlow = new HashMap<>(); @@ -101,10 +103,12 @@ public void disableTopLevelFlowIfNeeded(String topLevelFlowAlias) { /** * Find and remove flow overrides with specified ID in all realm clients. * - * @param flowId flow ID to remove overrides + * @param patchedAuthenticationFlow flow to remove overrides * @return Map "client" -> "auth name" -> "flow id" which were removed. Used to restore overrides. */ - public Map> removeFlowOverridesInClients(String flowId) { + public Map> removeFlowOverridesInClients(AuthenticationFlowRepresentation patchedAuthenticationFlow) { + final String flowId = patchedAuthenticationFlow.getId(); + final Map> clientsWithFlow = new HashMap<>(); // For all clients for (ClientRepresentation client : clientRepository.getAll(realmImport.getRealm())) { @@ -116,8 +120,11 @@ public Map> removeFlowOverridesInClients(String flow 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); + + // Search or create temporary auth flow + final String temporaryClientFlow = createTemporaryClientFlow(patchedAuthenticationFlow); + + authenticationFlowBindingOverrides.put(flowBinding.getKey(), temporaryClientFlow); updateClient = true; } } @@ -136,8 +143,11 @@ public Map> removeFlowOverridesInClients(String flow * @param clientsWithFlow map "client" -> "auth name" -> "flow id" to restore flow overrides. */ public void restoreClientOverrides(Map> clientsWithFlow) { + boolean removeTemporaryFlow = false; + // restore overrides with the new patched flow for (Map.Entry> clientWithFlow : clientsWithFlow.entrySet()) { + removeTemporaryFlow = true; final String clientId = clientWithFlow.getKey(); final Map overrides = clientWithFlow.getValue(); @@ -146,6 +156,12 @@ public void restoreClientOverrides(Map> clientsWithF client.getAuthenticationFlowBindingOverrides().putAll(overrides); clientRepository.update(realmImport.getRealm(), client); } + + if (removeTemporaryFlow) { + searchForTemporaryCreatedClientFlow().ifPresent(flow -> { + authenticationFlowRepository.delete(realmImport.getRealm(), flow.getId()); + }); + } } private void disableBrowserFlowIfNeeded(String topLevelFlowAlias, RealmRepresentation existingRealm) { @@ -339,11 +355,20 @@ private String searchTemporaryCreatedTopLevelFlowForReplacement() { } private Optional searchForTemporaryCreatedFlow() { + List existingTopLevelFlows = authenticationFlowRepository + .getTopLevelFlows(realmImport.getRealm()); + + return existingTopLevelFlows.stream() + .filter(f -> Objects.equals(f.getAlias(), TEMPORARY_CREATED_AUTH_FLOW)) + .findFirst(); + } + + private Optional searchForTemporaryCreatedClientFlow() { List existingTopLevelFlows = authenticationFlowRepository .getTopLevelFlows(realmImport.getRealm()); return existingTopLevelFlows.stream() - .filter(f -> Objects.equals(f.getAlias(), TEMPORARY_CREATED_AUTH_FLOW)) + .filter(f -> Objects.equals(f.getAlias(), TEMPORARY_CREATED_CLIENT_AUTH_FLOW)) .findFirst(); } @@ -508,5 +533,26 @@ private AuthenticationFlowRepresentation setupTemporaryCreatedFlow() { return tempFlow; } + + private AuthenticationFlowRepresentation setupTemporaryClientFlow(AuthenticationFlowRepresentation patchedAuthenticationFlow) { + AuthenticationFlowRepresentation tempFlow = CloneUtil.deepClone(patchedAuthenticationFlow, "id", "alias"); + + tempFlow.setAlias(TEMPORARY_CREATED_CLIENT_AUTH_FLOW); + tempFlow.setProviderId(TEMPORARY_CREATED_CLIENT_AUTH_FLOW); + + return tempFlow; + } + + private String createTemporaryClientFlow(AuthenticationFlowRepresentation patchedAuthenticationFlow) { + Optional authenticationFlowRepresentation = searchForTemporaryCreatedClientFlow(); + if (authenticationFlowRepresentation.isPresent()) { + return authenticationFlowRepresentation.get().getId(); + } + + authenticationFlowRepository.createTopLevel(realmImport.getRealm(), setupTemporaryClientFlow(patchedAuthenticationFlow)); + + return searchForTemporaryCreatedClientFlow().orElseThrow(() -> new RuntimeException("Unable to create temporary client authorization flow")) + .getId(); + } } } 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 d30f240f1..9de2e9cef 100644 --- a/src/main/java/de/adorsys/keycloak/config/service/AuthenticationFlowsImportService.java +++ b/src/main/java/de/adorsys/keycloak/config/service/AuthenticationFlowsImportService.java @@ -314,7 +314,7 @@ private void recreateTopLevelFlow( UsedAuthenticationFlowWorkaroundFactory.UsedAuthenticationFlowWorkaround workaround = workaroundFactory.buildFor(realmImport); workaround.disableTopLevelFlowIfNeeded(topLevelFlowToImport.getAlias()); - final Map> overrides = workaround.removeFlowOverridesInClients(patchedAuthenticationFlow.getId()); + final Map> overrides = workaround.removeFlowOverridesInClients(patchedAuthenticationFlow); authenticatorConfigImportService.deleteAuthenticationConfigs(realmImport, patchedAuthenticationFlow); authenticationFlowRepository.delete(realmImport.getRealm(), patchedAuthenticationFlow.getId());