keycloak-uncached

Details

diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
index c42f74c..5ad1136 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProvider.java
@@ -7,7 +7,6 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ModelException;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
-import org.keycloak.models.SocialLinkModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.picketlink.idm.IdentityManagementException;
@@ -28,12 +27,14 @@ import java.util.Map;
 import java.util.Set;
 
 /**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public class LDAPFederationProvider implements UserFederationProvider {
     private static final Logger logger = Logger.getLogger(LDAPFederationProvider.class);
     public static final String LDAP_ID = "LDAP_ID";
+    public static final String SYNC_REGISTRATIONS = "syncRegistrations";
 
     protected KeycloakSession session;
     protected UserFederationProviderModel model;
@@ -86,12 +87,13 @@ public class LDAPFederationProvider implements UserFederationProvider {
     }
 
     @Override
-    public boolean isRegistrationSupported() {
-        return true;
+    public boolean synchronizeRegistrations() {
+        return "true".equalsIgnoreCase(model.getConfig().get(SYNC_REGISTRATIONS));
     }
 
     @Override
     public UserModel register(RealmModel realm, UserModel user) {
+        if (!synchronizeRegistrations()) throw new IllegalStateException("Registration is not supported by this ldap server");
         IdentityManager identityManager = getIdentityManager();
 
         try {
@@ -100,6 +102,7 @@ public class LDAPFederationProvider implements UserFederationProvider {
             picketlinkUser.setLastName(user.getLastName());
             picketlinkUser.setEmail(user.getEmail());
             identityManager.add(picketlinkUser);
+            user.setAttribute(LDAP_ID, picketlinkUser.getId());
             return proxy(user);
         } catch (IdentityManagementException ie) {
             throw convertIDMException(ie);
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
index 155c2a0..969d9b2 100755
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java
@@ -11,6 +11,7 @@ import java.util.Collections;
 import java.util.List;
 
 /**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
index 0161019..2b17931 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js
@@ -434,7 +434,6 @@ module.controller('GenericUserFederationCtrl', function($scope, $location, Notif
 });
 
 
-
 module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog, realm, instance, UserFederationInstances, RealmLDAPConnectionTester) {
     console.log('LDAPCtrl');
 
@@ -445,6 +444,9 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
         $scope.instance.providerName = "ldap";
         $scope.instance.config = {};
         $scope.instance.priority = 0;
+        $scope.syncRegistrations = false;
+    } else {
+        $scope.syncRegistrations = instance.config.syncRegistrations && instance.config.syncRegistrations == "true";
     }
 
     $scope.ldapVendors = [
@@ -464,6 +466,14 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
 
     $scope.lastVendor = $scope.instance.config.vendor;
 
+    $scope.$watch('syncRegistrations', function() {
+        if ($scope.syncRegistrations) {
+            $scope.instance.config.syncRegistrations = "true";
+        } else {
+            $scope.instance.config.syncRegistrations = "false";
+        }
+    })
+
     $scope.$watch('instance', function() {
         if (!angular.equals($scope.instance, instance)) {
             $scope.changed = true;
@@ -510,6 +520,7 @@ module.controller('LDAPCtrl', function($scope, $location, Notifications, Dialog,
             $scope.instance.providerName = "ldap";
             $scope.instance.config = {};
             $scope.instance.priority = 0;
+            $scope.syncRegistrations = false;
         }
         $scope.changed = false;
         $scope.lastVendor = $scope.instance.config.vendor;
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
index 9916be5..ffac6b7 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html
@@ -33,6 +33,12 @@
                         <input class="form-control" id="priority" type="text" ng-model="instance.priority">
                     </div>
                 </div>
+                <div class="form-group clearfix block">
+                    <label class="col-sm-2 control-label" for="syncRegistrations">Sync Registrations</label>
+                    <div class="col-sm-4">
+                        <input ng-model="syncRegistrations" name="syncRegistrations" id="syncRegistrations" onoffswitch />
+                    </div>
+                </div>
                 <div class="form-group clearfix">
                     <label class="col-sm-2 control-label" for="vendor">Vendor</label>
                     <div class="col-sm-4">
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
index 14be8cc..787ca90 100755
--- a/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationManager.java
@@ -21,13 +21,7 @@ public class UserFederationManager implements UserProvider {
     @Override
     public UserModel addUser(RealmModel realm, String id, String username, boolean addDefaultRoles) {
         UserModel user = session.userStorage().addUser(realm, id, username, addDefaultRoles);
-        for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
-            UserFederationProvider fed = session.getProvider(UserFederationProvider.class, federation.getProviderName());
-            if (fed.isRegistrationSupported()) {
-                return fed.register(realm, user);
-            }
-        }
-        return user;
+        return registerWithFederation(realm, user);
     }
 
     protected UserFederationProvider getFederationProvider(UserFederationProviderModel model) {
@@ -39,9 +33,14 @@ public class UserFederationManager implements UserProvider {
     @Override
     public UserModel addUser(RealmModel realm, String username) {
         UserModel user = session.userStorage().addUser(realm, username);
+        return registerWithFederation(realm, user);
+    }
+
+    protected UserModel registerWithFederation(RealmModel realm, UserModel user) {
         for (UserFederationProviderModel federation : realm.getUserFederationProviders()) {
             UserFederationProvider fed = getFederationProvider(federation);
-            if (fed.isRegistrationSupported()) {
+            if (fed.synchronizeRegistrations()) {
+                user.setFederationLink(federation.getId());
                 return fed.register(realm, user);
             }
         }
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java b/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java
index 2ea322f..c45812f 100755
--- a/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationProvider.java
@@ -7,6 +7,7 @@ import java.util.Map;
 import java.util.Set;
 
 /**
+ * SPI for plugging in federation storage.
  *
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -18,8 +19,25 @@ public interface UserFederationProvider extends Provider {
     public static final String FIRST_NAME = UserModel.EMAIL;
     public static final String LAST_NAME = UserModel.LAST_NAME;
 
+    /**
+     * Gives the provider an option to proxy UserModels loaded from local storage.
+     * This method is called whenever a UserModel is pulled from local storage.
+     * For example, the LDAP provider proxies the UserModel and does on-demand synchronization with
+     * LDAP whenever UserModel update methods are invoked.  It also overrides UserModel.updateCredential for the
+     * credential types it supports
+     *
+     * @param local
+     * @return
+     */
     UserModel proxy(UserModel local);
-    boolean isRegistrationSupported();
+
+    /**
+     * Should user registrations be synchronized with this provider?
+     * FYI, only one provider will be chosen (by priority) to have this synchronization
+     *
+     * @return
+     */
+    boolean synchronizeRegistrations();
     UserModel register(RealmModel realm, UserModel user);
     boolean removeUser(RealmModel realm, UserModel user);
 
@@ -54,8 +72,30 @@ public interface UserFederationProvider extends Provider {
     void preRemove(RealmModel realm);
     void preRemove(RealmModel realm, RoleModel role);
 
+    /**
+     * Is the Keycloak UserModel still valid and/or existing in federated storage?
+     *
+     * @param local
+     * @return
+     */
     boolean isValid(UserModel local);
+
+    /**
+     * What UserCredentialModel types are supported by this provider.  Keycloak will only call
+     * validCredentials() with the credential types specified in this method.
+     *
+     * @return
+     */
     Set<String> getSupportedCredentialTypes();
+
+    /**
+     * Validate credentials for this user.
+     *
+     * @param realm
+     * @param user
+     * @param input
+     * @return
+     */
     boolean validCredentials(RealmModel realm, UserModel user, List<UserCredentialModel> input);
     boolean validCredentials(RealmModel realm, UserModel user, UserCredentialModel... input);
     void close();}
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index 2173d7e..6378134 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -121,8 +121,8 @@ public class RealmAdminResource {
                 CacheRealmProvider cacheRealmProvider = (CacheRealmProvider)session.realms();
                 rep.setRealmCacheEnabled(cacheRealmProvider.isEnabled());
             }
-            if (session.users() instanceof CacheUserProvider) {
-                CacheUserProvider cache = (CacheUserProvider)session.users();
+            if (session.userStorage() instanceof CacheUserProvider) {
+                CacheUserProvider cache = (CacheUserProvider)session.userStorage();
                 rep.setUserCacheEnabled(cache.isEnabled());
             }
             return rep;
@@ -155,8 +155,8 @@ public class RealmAdminResource {
                 CacheRealmProvider cacheRealmProvider = (CacheRealmProvider)session.realms();
                 cacheRealmProvider.setEnabled(rep.isRealmCacheEnabled());
             }
-            if (rep.isUserCacheEnabled() != null && session.users() instanceof CacheUserProvider) {
-                CacheUserProvider cache = (CacheUserProvider)session.users();
+            if (rep.isUserCacheEnabled() != null && session.userStorage() instanceof CacheUserProvider) {
+                CacheUserProvider cache = (CacheUserProvider)session.userStorage();
                 cache.setEnabled(rep.isUserCacheEnabled());
             }
 
diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProvider.java b/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProvider.java
index 5d2c36b..7ebf592 100755
--- a/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProvider.java
+++ b/testsuite/integration/src/main/java/org/keycloak/testutils/DummyUserFederationProvider.java
@@ -22,7 +22,7 @@ public class DummyUserFederationProvider implements UserFederationProvider {
     }
 
     @Override
-    public boolean isRegistrationSupported() {
+    public boolean synchronizeRegistrations() {
         return false;
     }
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java
index 0dbb947..81304c8 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/FederationProvidersIntegrationTest.java
@@ -10,7 +10,9 @@ import org.junit.rules.RuleChain;
 import org.junit.rules.TestRule;
 import org.junit.runners.MethodSorters;
 import org.keycloak.OAuth2Constants;
+import org.keycloak.federation.ldap.LDAPFederationProvider;
 import org.keycloak.federation.ldap.LDAPFederationProviderFactory;
+import org.keycloak.models.UserFederationProviderModel;
 import org.keycloak.testutils.LDAPEmbeddedServer;
 import org.keycloak.testsuite.LDAPTestUtils;
 import org.keycloak.models.KeycloakSession;
@@ -45,6 +47,8 @@ public class FederationProvidersIntegrationTest {
 
     private static Map<String,String> ldapConfig = null;
 
+    private static UserFederationProviderModel ldapModel = null;
+
     private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
 
         @Override
@@ -61,10 +65,11 @@ public class FederationProvidersIntegrationTest {
             ldapConfig.put(LDAPConstants.USER_DN_SUFFIX, ldapServer.getUserDnSuffix());
             String vendor = ldapServer.getVendor();
             ldapConfig.put(LDAPConstants.VENDOR, vendor);
+            ldapConfig.put(LDAPFederationProvider.SYNC_REGISTRATIONS, "true");
 
 
 
-            appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, null);
+            ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, null);
 
             // Configure LDAP
             ldapRule.getEmbeddedServer().setupLdapInRealm(appRealm);
@@ -187,5 +192,13 @@ public class FederationProvidersIntegrationTest {
 
         registerPage.register("firstName", "lastName", "email2", "registerUserSuccess2", "password", "password");
         Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
+
+        KeycloakSession session = keycloakRule.startSession();
+        RealmModel appRealm = session.realms().getRealmByName("test");
+        UserModel user = session.users().getUserByUsername("registerUserSuccess2", appRealm);
+        Assert.assertNotNull(user);
+        Assert.assertNotNull(user.getFederationLink());
+        Assert.assertEquals(user.getFederationLink(), ldapModel.getId());
+        keycloakRule.stopSession(session, false);
     }
 }