keycloak-uncached
Changes
federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPFederationProviderFactory.java 1(+1 -0)
forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/users.js 13(+12 -1)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/federated-ldap.html 6(+6 -0)
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);
}
}