diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java index ebde5d3442..c772d36558 100755 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/LDAPStorageProvider.java @@ -174,8 +174,9 @@ public class LDAPStorageProvider implements UserStorageProvider, @Override public UserModel addUser(RealmModel realm, String username) { - if (editMode == UserStorageProvider.EditMode.READ_ONLY || editMode == UserStorageProvider.EditMode.UNSYNCED) throw new IllegalStateException("Registration is not supported by this ldap server"); - if (!synchronizeRegistrations()) throw new IllegalStateException("Registration is not supported by this ldap server"); + if (!synchronizeRegistrations()) { + return null; + } UserModel user = session.userLocalStorage().addUser(realm, username); user.setFederationLink(model.getId()); LDAPObject ldapUser = LDAPUtils.addUserToLDAP(this, realm, user); diff --git a/server-spi/src/main/java/org/keycloak/storage/user/UserRegistrationProvider.java b/server-spi/src/main/java/org/keycloak/storage/user/UserRegistrationProvider.java index b3ec00d85a..650f8b0c20 100644 --- a/server-spi/src/main/java/org/keycloak/storage/user/UserRegistrationProvider.java +++ b/server-spi/src/main/java/org/keycloak/storage/user/UserRegistrationProvider.java @@ -29,8 +29,35 @@ import org.keycloak.models.UserModel; */ public interface UserRegistrationProvider { + /** + * All storage providers that implement this interface will be looped through. + * If this method returns null, then the next storage provider's addUser() method will be called. + * If no storage providers handle the add, then the user will be created in local storage. + * + * Returning null is useful when you want optional support for adding users. For example, + * our LDAP provider can enable and disable the ability to add users. + * + * @param realm + * @param username + * @return + */ UserModel addUser(RealmModel realm, String username); + /** + * Called if user originated from this provider. + * + * + * If a local user is linked to this provider, this method will be called before + * local storage's removeUser() method is invoked. + + * If you are using an import strategy, and this is a local user linked to this provider, + * this method will be called before local storage's removeUser() method is invoked. Also, + * you DO NOT need to remove the imported user. The runtime will handle this for you. + * + * @param realm + * @param user + * @return + */ boolean removeUser(RealmModel realm, UserModel user); diff --git a/services/src/main/java/org/keycloak/storage/UserStorageManager.java b/services/src/main/java/org/keycloak/storage/UserStorageManager.java index 906e2252ad..4924f19fee 100755 --- a/services/src/main/java/org/keycloak/storage/UserStorageManager.java +++ b/services/src/main/java/org/keycloak/storage/UserStorageManager.java @@ -116,10 +116,11 @@ public class UserStorageManager implements UserProvider, OnUserCache { @Override public UserModel addUser(RealmModel realm, String username) { - UserRegistrationProvider registry = getFirstStorageProvider(session, realm, UserRegistrationProvider.class); - if (registry != null) { - return registry.addUser(realm, username); + for (UserRegistrationProvider provider : getStorageProviders(session, realm, UserRegistrationProvider.class)) { + UserModel user = provider.addUser(realm, username); + if (user != null) return user; } + return localStorage().addUser(realm, username.toLowerCase()); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPProvidersIntegrationTest.java index 8881e42303..fdb73d70dd 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPProvidersIntegrationTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPProvidersIntegrationTest.java @@ -149,6 +149,27 @@ public class LDAPProvidersIntegrationTest { // // } + /** + * KEYCLOAK-3986 + * + */ + @Test + public void testSyncRegistrationOff() { + KeycloakSession session = keycloakRule.startSession(); + try { + RealmManager manager = new RealmManager(session); + RealmModel appRealm = manager.getRealm("test"); + UserStorageProviderModel newModel = new UserStorageProviderModel(ldapModel); + newModel.getConfig().putSingle(LDAPConstants.SYNC_REGISTRATIONS, "false"); + appRealm.updateComponent(newModel); + UserModel newUser1 = session.users().addUser(appRealm, "newUser1"); + Assert.assertNull(newUser1.getFederationLink()); + } finally { + keycloakRule.stopSession(session, false); + } + + + } @Test public void caseInSensitiveImport() {