keycloak-aplcache

Details

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 ebde5d3..c772d36 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 b3ec00d..650f8b0 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 906e225..4924f19 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 8881e42..fdb73d7 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() {