diff --git a/src/main/java/com/danielfrak/code/keycloak/providers/rest/ConfigurationProperties.java b/src/main/java/com/danielfrak/code/keycloak/providers/rest/ConfigurationProperties.java index ed87d1aa..27eae793 100644 --- a/src/main/java/com/danielfrak/code/keycloak/providers/rest/ConfigurationProperties.java +++ b/src/main/java/com/danielfrak/code/keycloak/providers/rest/ConfigurationProperties.java @@ -16,6 +16,7 @@ public final class ConfigurationProperties { public static final String API_HTTP_BASIC_USERNAME_PROPERTY = "API_HTTP_BASIC_USERNAME"; public static final String API_HTTP_BASIC_PASSWORD_PROPERTY = "API_HTTP_BASIC_PASSWORD"; public static final String USE_USER_ID_FOR_CREDENTIAL_VERIFICATION = "USE_USER_ID_FOR_CREDENTIAL_VERIFICATION"; + public static final String DISABLE_SEVER_FEDERATION_LINK = "DISABLE_SEVER_FEDERATION_LINK"; public static final String ROLE_MAP_PROPERTY = "ROLE_MAP"; public static final String GROUP_MAP_PROPERTY = "GROUP_MAP"; public static final String MIGRATE_UNMAPPED_ROLES_PROPERTY = "MIGRATE_UNMAPPED_ROLES"; @@ -51,6 +52,11 @@ public final class ConfigurationProperties { "Use the id of the user instead of the username as the path" + "parameter when making a credential verification request", BOOLEAN_TYPE, false), + new ProviderConfigProperty(DISABLE_SEVER_FEDERATION_LINK, + "Disable federation link sever", + "When a login is successfull, do not sever the federation link, " + + "allowing the next login to be validated against the legacy system.", + BOOLEAN_TYPE, false), new ProviderConfigProperty(ROLE_MAP_PROPERTY, "Legacy role conversion", "Role conversion in the format 'legacyRole:newRole'", diff --git a/src/main/java/com/danielfrak/code/keycloak/providers/rest/LegacyProvider.java b/src/main/java/com/danielfrak/code/keycloak/providers/rest/LegacyProvider.java index 693cba02..a4e8a9b6 100644 --- a/src/main/java/com/danielfrak/code/keycloak/providers/rest/LegacyProvider.java +++ b/src/main/java/com/danielfrak/code/keycloak/providers/rest/LegacyProvider.java @@ -119,7 +119,11 @@ public void close() { @Override public boolean updateCredential(RealmModel realm, UserModel user, CredentialInput input) { - severFederationLink(user); + var disableSeverFederationConfig = model.getConfig().getFirst(ConfigurationProperties.DISABLE_SEVER_FEDERATION_LINK); + boolean severFederation = !Boolean.parseBoolean(disableSeverFederationConfig); + if (severFederation) { + severFederationLink(user); + } return false; } diff --git a/src/test/java/com/danielfrak/code/keycloak/providers/rest/LegacyProviderTest.java b/src/test/java/com/danielfrak/code/keycloak/providers/rest/LegacyProviderTest.java index b35b2f46..27d702b5 100644 --- a/src/test/java/com/danielfrak/code/keycloak/providers/rest/LegacyProviderTest.java +++ b/src/test/java/com/danielfrak/code/keycloak/providers/rest/LegacyProviderTest.java @@ -27,6 +27,7 @@ import java.util.stream.Stream; import static com.danielfrak.code.keycloak.providers.rest.ConfigurationProperties.USE_USER_ID_FOR_CREDENTIAL_VERIFICATION; +import static com.danielfrak.code.keycloak.providers.rest.ConfigurationProperties.DISABLE_SEVER_FEDERATION_LINK; import static com.danielfrak.code.keycloak.providers.rest.remote.TestLegacyUser.aMinimalLegacyUser; import static java.util.Collections.emptySet; import static org.junit.jupiter.api.Assertions.*; @@ -309,18 +310,42 @@ void shouldRemoveFederationLinkWhenCredentialUpdates() { when(userModel.getFederationLink()) .thenReturn("someId"); + MultivaluedHashMap config = new MultivaluedHashMap<>(); + config.put(DISABLE_SEVER_FEDERATION_LINK, List.of("false")); + when(model.getConfig()).thenReturn(config); + assertFalse(legacyProvider.updateCredential(realmModel, userModel, input)); verify(userModel) .setFederationLink(null); } + @Test + void shouldNotRemoveFederationLinkWhenCredentialUpdatesWithConfig() { + var input = mock(CredentialInput.class); + lenient().when(userModel.getFederationLink()) + .thenReturn("someId"); + + MultivaluedHashMap config = new MultivaluedHashMap<>(); + config.put(DISABLE_SEVER_FEDERATION_LINK, List.of("true")); + when(model.getConfig()).thenReturn(config); + + assertFalse(legacyProvider.updateCredential(realmModel, userModel, input)); + + verify(userModel, never()) + .setFederationLink(null); + } + @Test void shouldNotRemoveFederationLinkWhenBlankAndCredentialUpdates() { var input = mock(CredentialInput.class); when(userModel.getFederationLink()) .thenReturn(" "); + MultivaluedHashMap config = new MultivaluedHashMap<>(); + config.put(DISABLE_SEVER_FEDERATION_LINK, List.of("false")); + when(model.getConfig()).thenReturn(config); + assertFalse(legacyProvider.updateCredential(realmModel, userModel, input)); verify(userModel, never()) @@ -333,6 +358,10 @@ void shouldNotRemoveFederationLinkWhenNullAndCredentialUpdates() { when(userModel.getFederationLink()) .thenReturn(null); + MultivaluedHashMap config = new MultivaluedHashMap<>(); + config.put(DISABLE_SEVER_FEDERATION_LINK, List.of("false")); + when(model.getConfig()).thenReturn(config); + assertFalse(legacyProvider.updateCredential(realmModel, userModel, input)); verify(userModel, never()) @@ -368,4 +397,4 @@ void removeUserShouldReturnTrue() { var result = legacyProvider.removeUser(realmModel, userModel); assertTrue(result); } -} \ No newline at end of file +}