diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java index acd2182484..580a556da6 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPConfig.java @@ -108,8 +108,8 @@ public class LDAPConfig { return searchScope == null ? SearchControls.SUBTREE_SCOPE : Integer.parseInt(searchScope); } - public String getUuidAttributeName() { - String uuidAttrName = config.get(LDAPConstants.UUID_ATTRIBUTE_NAME); + public String getUuidLDAPAttributeName() { + String uuidAttrName = config.get(LDAPConstants.UUID_LDAP_ATTRIBUTE); if (uuidAttrName == null) { // Differences of unique attribute among various vendors String vendor = getVendor(); diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java index 023d143ada..552d298e20 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java @@ -1,11 +1,9 @@ package org.keycloak.federation.ldap.idm.store.ldap; -import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -123,7 +121,7 @@ public class LDAPIdentityStore implements IdentityStore { for (Condition condition : identityQuery.getConditions()) { // Check if we are searching by ID - String uuidAttrName = getConfig().getUuidAttributeName(); + String uuidAttrName = getConfig().getUuidLDAPAttributeName(); if (condition.getParameter() != null && condition.getParameter().getName().equals(uuidAttrName)) { if (EqualCondition.class.isInstance(condition)) { EqualCondition equalCondition = (EqualCondition) condition; @@ -280,7 +278,7 @@ public class LDAPIdentityStore implements IdentityStore { QueryParameter queryParameter = condition.getParameter(); - if (!getConfig().getUuidAttributeName().equals(queryParameter.getName())) { + if (!getConfig().getUuidLDAPAttributeName().equals(queryParameter.getName())) { String attributeName = queryParameter.getName(); if (attributeName != null) { @@ -400,7 +398,7 @@ public class LDAPIdentityStore implements IdentityStore { String ldapAttributeName = ldapAttribute.getID(); - if (ldapAttributeName.toLowerCase().equals(getConfig().getUuidAttributeName().toLowerCase())) { + if (ldapAttributeName.toLowerCase().equals(getConfig().getUuidLDAPAttributeName().toLowerCase())) { Object uuidValue = ldapAttribute.get(); ldapObject.setUuid(this.operationManager.decodeEntryUUID(uuidValue)); } else { @@ -502,6 +500,9 @@ public class LDAPIdentityStore implements IdentityStore { if (!ldapObject.getReadOnlyAttributeNames().contains(attrName) && (isCreate || !ldapObject.getRdnAttributeName().equals(attrName))) { if (String.class.isInstance(attrValue)) { + if (attrValue.toString().trim().length() == 0) { + attrValue = LDAPConstants.EMPTY_ATTRIBUTE_VALUE; + } entryAttributes.put(attrName, attrValue); } else if (Collection.class.isInstance(attrValue)) { BasicAttribute attr = new BasicAttribute(attrName); @@ -616,9 +617,9 @@ public class LDAPIdentityStore implements IdentityStore { protected String getEntryIdentifier(final LDAPObject ldapObject) { try { // we need this to retrieve the entry's identifier from the ldap server - String uuidAttrName = getConfig().getUuidAttributeName(); + String uuidAttrName = getConfig().getUuidLDAPAttributeName(); List search = this.operationManager.search(ldapObject.getDn().toString(), "(" + ldapObject.getDn().getFirstRdn() + ")", Arrays.asList(uuidAttrName), SearchControls.OBJECT_SCOPE); - Attribute id = search.get(0).getAttributes().get(getConfig().getUuidAttributeName()); + Attribute id = search.get(0).getAttributes().get(getConfig().getUuidLDAPAttributeName()); if (id == null) { throw new ModelException("Could not retrieve identifier for entry [" + ldapObject.getDn().toString() + "]."); diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java index 0a2a767e3d..0a9f4dcf09 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPOperationManager.java @@ -419,7 +419,7 @@ public class LDAPOperationManager { } private String getUuidAttributeName() { - return this.config.getUuidAttributeName(); + return this.config.getUuidLDAPAttributeName(); } public Attributes getAttributes(final String entryUUID, final String baseDN, Set returningAttributes) { diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js index 37b774fb06..2444be45b3 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/users.js @@ -542,6 +542,7 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications, instance.config.useKerberosForPasswordAuthentication = false; instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE; + instance.config.searchScope = "1"; $scope.fullSyncEnabled = false; $scope.changedSyncEnabled = false; @@ -558,6 +559,9 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications, if (!instance.config.batchSizeForSync) { instance.config.batchSizeForSync = DEFAULT_BATCH_SIZE; } + if (!instance.config.searchScope) { + instance.config.searchScope = "1"; + } $scope.fullSyncEnabled = (instance.fullSyncPeriod && instance.fullSyncPeriod > 0); $scope.changedSyncEnabled = (instance.changedSyncPeriod && instance.changedSyncPeriod > 0); } @@ -577,6 +581,11 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications, { "id": "other", "name": "Other" } ]; + $scope.searchScopes = [ + { "id": "1", "name": "One Level" }, + { "id": "2", "name": "Subtree" } + ]; + $scope.realm = realm; $scope.$watch('fullSyncEnabled', function(newVal, oldVal) { @@ -607,12 +616,23 @@ module.controller('LDAPCtrl', function($scope, $location, $route, Notifications, $scope.lastVendor = $scope.instance.config.vendor; if ($scope.lastVendor === "ad") { - $scope.instance.config.usernameLDAPAttribute = "sAMAccountName"; + $scope.instance.config.usernameLDAPAttribute = "cn"; $scope.instance.config.userObjectClasses = "person, organizationalPerson, user"; } else { $scope.instance.config.usernameLDAPAttribute = "uid"; $scope.instance.config.userObjectClasses = "inetOrgPerson, organizationalPerson"; } + + $scope.instance.config.rdnLDAPAttribute = $scope.instance.config.usernameLDAPAttribute; + + var vendorToUUID = { + rhds: "nsuniqueid", + tivoli: "uniqueidentifier", + edirectory: "guid", + ad: "objectGUID", + other: "entryUUID" + }; + $scope.instance.config.uuidLDAPAttribute = vendorToUUID[$scope.lastVendor]; } }, true); diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-ldap.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-ldap.html index 64728a303a..ec916a0e1c 100755 --- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-ldap.html +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/federated-ldap.html @@ -69,10 +69,24 @@
- +
Name of LDAP attribute, which is mapped as Keycloak username. For many LDAP server vendors it's 'uid'. For Active directory it's usually 'sAMAccountName' or 'cn'
+
+ +
+ +
+ Name of LDAP attribute, which is used as RDN (top attribute) of typical user DN. Usually it's the same as Username LDAP attribute, however for Active directory it could be 'cn' when username attribute might be 'sAMAccountName' +
+
+ +
+ +
+ Name of LDAP attribute, which is used as unique object identifier (UUID) for objects in LDAP. For many LDAP server vendors it's 'entryUUID' however some are different. For example for Active directory it should be 'objectGUID' +
@@ -91,18 +105,13 @@
- +
- +
- Base DN of LDAP tree where your data are. Base DN is usually ancestor of User DN Suffix -
-
- -
- -
- Base DN of LDAP tree where your users are. This DN is parent of all DNs of LDAP users + Full DN of LDAP tree where your users are. This DN is parent of LDAP users. It could be for example 'ou=users,dc=example,dc=com' assuming + that your typical user will have DN like 'uid=john,ou=users,dc=example,dc=com' +
@@ -121,6 +130,19 @@ Test authentication
+
+ +
+
+ +
+
+ For one level, we search for users just in DNs specified by User DNs. For subtree, we search in whole of their subtree. See LDAP documentation for more details +
diff --git a/model/api/src/main/java/org/keycloak/models/LDAPConstants.java b/model/api/src/main/java/org/keycloak/models/LDAPConstants.java index ac15a5cb15..94e792d35c 100644 --- a/model/api/src/main/java/org/keycloak/models/LDAPConstants.java +++ b/model/api/src/main/java/org/keycloak/models/LDAPConstants.java @@ -14,6 +14,7 @@ public class LDAPConstants { public static final String USERNAME_LDAP_ATTRIBUTE = "usernameLDAPAttribute"; public static final String RDN_LDAP_ATTRIBUTE = "rdnLDAPAttribute"; + public static final String UUID_LDAP_ATTRIBUTE = "uuidLDAPAttribute"; public static final String USER_OBJECT_CLASSES = "userObjectClasses"; public static final String CONNECTION_URL = "connectionUrl"; @@ -23,7 +24,6 @@ public class LDAPConstants { public static final String BIND_CREDENTIAL = "bindCredential"; public static final String SEARCH_SCOPE = "searchScope"; - public static final String UUID_ATTRIBUTE_NAME = "uuidAttributeName"; public static final String CONNECTION_POOLING = "connectionPooling"; public static final String PAGINATION = "pagination"; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java index 2d630e92c4..74bd0b831e 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java @@ -167,6 +167,10 @@ public class KeycloakServer { System.setProperty("keycloak.theme.cacheTemplates", "false"); } + if (!System.getProperties().containsKey("keycloak.theme.staticMaxAge")) { + System.setProperty("keycloak.theme.staticMaxAge", "-1"); + } + config.setResourcesHome(dir.getAbsolutePath()); }