keycloak-aplcache
Changes
authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java 4(+4 -0)
forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-ldap.html 11(+11 -0)
picketlink/keycloak-picketlink-api/src/main/java/org/keycloak/picketlink/AbstractIdentityManagerProvider.java 32(+32 -0)
picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPAgentIgnoreCredentialHandler.java 27(+0 -27)
picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LdapConstants.java 5(+5 -0)
picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java 93(+93 -0)
picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/PartitionManagerRegistry.java 47(+37 -10)
picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/RealmIdentityManagerProvider.java 12(+4 -8)
Details
diff --git a/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java b/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java
index ce69bd3..615e2c7 100755
--- a/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java
+++ b/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java
@@ -67,6 +67,10 @@ public class PicketlinkAuthenticationProvider implements AuthenticationProvider
try {
User picketlinkUser = new User(username);
identityManager.add(picketlinkUser);
+
+ // Hack needed due to ActiveDirectory bug in Picketlink TODO: Remove once https://issues.jboss.org/browse/PLINK-485 fixed and updated in keycloak master
+ picketlinkUser = BasicModel.getUser(identityManager, picketlinkUser.getLoginName());
+
return picketlinkUser.getId();
} catch (IdentityManagementException ie) {
throw convertIDMException(ie);
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
index 9b95be0..fc10a08 100755
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/js/controllers/realm.js
@@ -902,6 +902,12 @@ module.controller('RealmSMTPSettingsCtrl', function($scope, Current, Realm, real
module.controller('RealmLdapSettingsCtrl', function($scope, $location, Notifications, Realm, realm) {
console.log('RealmLdapSettingsCtrl');
+ $scope.ldapVendors = [
+ { "id": "ad", "name": "Active Directory" },
+ { "id": "rhds", "name": "Red Hat Directory Server" },
+ { "id": "other", "name": "Other" }
+ ];
+
$scope.realm = realm;
var oldCopy = angular.copy($scope.realm);
diff --git a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-ldap.html b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-ldap.html
index e717ef5..6eb9fdb 100644
--- a/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-ldap.html
+++ b/forms/common-themes/src/main/resources/theme/admin/base/resources/partials/realm-ldap.html
@@ -14,6 +14,17 @@
<fieldset>
<legend><span class="text">Required Settings</span></legend>
<div class="form-group clearfix">
+ <label class="col-sm-2 control-label" for="vendor">Vendor <span class="required">*</span></label>
+ <div class="col-sm-4">
+ <div class="select-kc">
+ <select id="vendor"
+ ng-model="realm.ldapServer.vendor"
+ ng-options="vendor.id as vendor.name for vendor in ldapVendors">
+ </select>
+ </div>
+ </div>
+ </div>
+ <div class="form-group clearfix">
<label class="col-sm-2 control-label" for="ldapConnectionUrl">Connection URL <span class="required">*</span></label>
<div class="col-sm-4">
<input class="form-control" id="ldapConnectionUrl" type="text" ng-model="realm.ldapServer.connectionUrl" placeholder="LDAP connection URL" required>
diff --git a/model/tests/src/main/java/org/keycloak/model/test/LDAPEmbeddedServer.java b/model/tests/src/main/java/org/keycloak/model/test/LDAPEmbeddedServer.java
index 0598c42..70c149f 100644
--- a/model/tests/src/main/java/org/keycloak/model/test/LDAPEmbeddedServer.java
+++ b/model/tests/src/main/java/org/keycloak/model/test/LDAPEmbeddedServer.java
@@ -49,6 +49,7 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
protected boolean startEmbeddedLdapLerver = true;
protected String bindDn = "uid=admin,ou=system";
protected String bindCredential = "secret";
+ protected String vendor;
public static String IDM_TEST_LDAP_CONNECTION_URL = "idm.test.ldap.connection.url";
public static String IDM_TEST_LDAP_BASE_DN = "idm.test.ldap.base.dn";
@@ -59,6 +60,7 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
public static String IDM_TEST_LDAP_START_EMBEDDED_LDAP_SERVER = "idm.test.ldap.start.embedded.ldap.server";
public static String IDM_TEST_LDAP_BIND_DN = "idm.test.ldap.bind.dn";
public static String IDM_TEST_LDAP_BIND_CREDENTIAL = "idm.test.ldap.bind.credential";
+ public static String IDM_TEST_LDAP_VENDOR = "idm.test.ldap.vendor";
public LDAPEmbeddedServer() {
@@ -85,6 +87,7 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
startEmbeddedLdapLerver = Boolean.parseBoolean(p.getProperty(IDM_TEST_LDAP_START_EMBEDDED_LDAP_SERVER, "true"));
bindDn = p.getProperty(IDM_TEST_LDAP_BIND_DN, bindDn);
bindCredential = p.getProperty(IDM_TEST_LDAP_BIND_CREDENTIAL, bindCredential);
+ vendor = p.getProperty(IDM_TEST_LDAP_VENDOR);
}
@Override
@@ -132,6 +135,7 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
ldapConfig.put(LdapConstants.BIND_DN, getBindDn());
ldapConfig.put(LdapConstants.BIND_CREDENTIAL, getBindCredential());
ldapConfig.put(LdapConstants.USER_DN_SUFFIX, getUserDnSuffix());
+ ldapConfig.put(LdapConstants.VENDOR, getVendor());
realm.setLdapServerConfig(ldapConfig);
}
@@ -202,6 +206,10 @@ public class LDAPEmbeddedServer extends AbstractLDAPTest {
return bindCredential;
}
+ public String getVendor() {
+ return vendor;
+ }
+
@Override
public void importLDIF(String fileName) throws Exception {
// import LDIF only in case we are running against embedded LDAP server
diff --git a/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersLDAPTest.java b/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersLDAPTest.java
index 24317a8..cc6eaf1 100755
--- a/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersLDAPTest.java
+++ b/model/tests/src/test/java/org/keycloak/model/test/AuthProvidersLDAPTest.java
@@ -75,23 +75,23 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
@Test
public void testLdapAuthentication() {
- MultivaluedMap<String, String> formData = AuthProvidersExternalModelTest.createFormData("john", "password");
+ MultivaluedMap<String, String> formData = AuthProvidersExternalModelTest.createFormData("johnkeycloak", "password");
// Set password of user in LDAP
- LdapTestUtils.setLdapPassword(providerSession, realm, "john", "password");
+ LdapTestUtils.setLdapPassword(providerSession, realm, "johnkeycloak", "password");
// Verify that user doesn't exists in realm2 and can't authenticate here
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.INVALID_USER, am.authenticateForm(null, realm, formData));
- Assert.assertNull(realm.getUser("john"));
+ Assert.assertNull(realm.getUser("johnkeycloak"));
// Add ldap authenticationProvider
setupAuthenticationProviders();
// Authenticate john and verify that now he exists in realm
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.SUCCESS, am.authenticateForm(null, realm, formData));
- UserModel john = realm.getUser("john");
+ UserModel john = realm.getUser("johnkeycloak");
Assert.assertNotNull(john);
- Assert.assertEquals("john", john.getLoginName());
+ Assert.assertEquals("johnkeycloak", john.getLoginName());
Assert.assertEquals("John", john.getFirstName());
Assert.assertEquals("Doe", john.getLastName());
Assert.assertEquals("john@email.org", john.getEmail());
@@ -119,7 +119,7 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.INVALID_USER, am.authenticateForm(null, realm, formData));
// User exists in ldap
- formData = AuthProvidersExternalModelTest.createFormData("john", "invalid");
+ formData = AuthProvidersExternalModelTest.createFormData("johnkeycloak", "invalid");
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.INVALID_CREDENTIALS, am.authenticateForm(null, realm, formData));
// User exists in realm
@@ -142,23 +142,23 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
// Add ldap
setupAuthenticationProviders();
- LdapTestUtils.setLdapPassword(providerSession, realm, "john", "password");
+ LdapTestUtils.setLdapPassword(providerSession, realm, "johnkeycloak", "password");
// First authenticate successfully to sync john into realm
- MultivaluedMap<String, String> formData = AuthProvidersExternalModelTest.createFormData("john", "password");
+ MultivaluedMap<String, String> formData = AuthProvidersExternalModelTest.createFormData("johnkeycloak", "password");
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.SUCCESS, am.authenticateForm(null, realm, formData));
// Change credential and validate that user can authenticate
AuthenticationProviderManager authProviderManager = AuthenticationProviderManager.getManager(realm, providerSession);
- UserModel john = realm.getUser("john");
+ UserModel john = realm.getUser("johnkeycloak");
try {
Assert.assertTrue(authProviderManager.updatePassword(john, "password-updated"));
} catch (AuthenticationProviderException ape) {
ape.printStackTrace();
Assert.fail("Error not expected");
}
- formData = AuthProvidersExternalModelTest.createFormData("john", "password-updated");
+ formData = AuthProvidersExternalModelTest.createFormData("johnkeycloak", "password-updated");
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.SUCCESS, am.authenticateForm(null, realm, formData));
// Password updated just in LDAP, so validating directly in realm should fail
@@ -174,7 +174,7 @@ public class AuthProvidersLDAPTest extends AbstractModelTest {
ape.printStackTrace();
Assert.fail("Error not expected");
}
- formData = AuthProvidersExternalModelTest.createFormData("john", "password-updated2");
+ formData = AuthProvidersExternalModelTest.createFormData("johnkeycloak", "password-updated2");
Assert.assertEquals(AuthenticationManager.AuthenticationStatus.INVALID_CREDENTIALS, am.authenticateForm(null, realm, formData));
}
diff --git a/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java b/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java
index 1c47fd1..cfd04b5 100755
--- a/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java
+++ b/model/tests/src/test/java/org/keycloak/model/test/ImportTest.java
@@ -201,10 +201,11 @@ public class ImportTest extends AbstractModelTest {
// Test ldap config
Map<String, String> ldapConfig = realm.getLdapServerConfig();
- Assert.assertTrue(ldapConfig.size() == 5);
+ Assert.assertTrue(ldapConfig.size() == 6);
Assert.assertEquals("ldap://localhost:10389", ldapConfig.get("connectionUrl"));
Assert.assertEquals("dc=keycloak,dc=org", ldapConfig.get("baseDn"));
Assert.assertEquals("ou=People,dc=keycloak,dc=org", ldapConfig.get("userDnSuffix"));
+ Assert.assertEquals("other", ldapConfig.get("vendor"));
// Test authentication providers
List<AuthenticationProviderModel> authProviderModels = realm.getAuthenticationProviders();
diff --git a/model/tests/src/test/resources/ldap/users.ldif b/model/tests/src/test/resources/ldap/users.ldif
index 8ba3a98..cd6bac8 100644
--- a/model/tests/src/test/resources/ldap/users.ldif
+++ b/model/tests/src/test/resources/ldap/users.ldif
@@ -9,12 +9,12 @@ objectclass: top
objectclass: organizationalUnit
ou: People
-dn: uid=john,ou=People,dc=keycloak,dc=org
+dn: uid=johnkeycloak,ou=People,dc=keycloak,dc=org
objectclass: top
objectclass: uidObject
objectclass: person
objectclass: inetOrgPerson
-uid: john
+uid: johnkeycloak
cn: John
sn: Doe
mail: john@email.org
diff --git a/model/tests/src/test/resources/testrealm.json b/model/tests/src/test/resources/testrealm.json
index 56bf23e..4ab4ebf 100755
--- a/model/tests/src/test/resources/testrealm.json
+++ b/model/tests/src/test/resources/testrealm.json
@@ -17,7 +17,8 @@
"baseDn": "dc=keycloak,dc=org",
"userDnSuffix": "ou=People,dc=keycloak,dc=org",
"bindDn": "uid=admin,ou=system",
- "bindCredential": "secret"
+ "bindCredential": "secret",
+ "vendor": "other"
},
"socialProviders": {
"google.key": "abc",
diff --git a/picketlink/keycloak-picketlink-api/src/main/java/org/keycloak/picketlink/AbstractIdentityManagerProvider.java b/picketlink/keycloak-picketlink-api/src/main/java/org/keycloak/picketlink/AbstractIdentityManagerProvider.java
new file mode 100644
index 0000000..3ee589e
--- /dev/null
+++ b/picketlink/keycloak-picketlink-api/src/main/java/org/keycloak/picketlink/AbstractIdentityManagerProvider.java
@@ -0,0 +1,32 @@
+package org.keycloak.picketlink;
+
+import org.keycloak.models.RealmModel;
+import org.picketlink.idm.IdentityManager;
+import org.picketlink.idm.PartitionManager;
+
+/**
+ * Per-request IdentityManager caching . Not thread-safe
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public abstract class AbstractIdentityManagerProvider implements IdentityManagerProvider {
+
+ private IdentityManager identityManager;
+
+ @Override
+ public IdentityManager getIdentityManager(RealmModel realm) {
+ if (identityManager == null) {
+ PartitionManager partitionManager = getPartitionManager(realm);
+ identityManager = partitionManager.createIdentityManager();
+ }
+
+ return identityManager;
+ }
+
+ protected abstract PartitionManager getPartitionManager(RealmModel realm);
+
+ @Override
+ public void close() {
+ identityManager = null;
+ }
+}
diff --git a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LdapConstants.java b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LdapConstants.java
index d7fbd12..4eb3454 100644
--- a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LdapConstants.java
+++ b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LdapConstants.java
@@ -5,6 +5,11 @@ package org.keycloak.picketlink.idm;
*/
public class LdapConstants {
+ public static final String VENDOR = "vendor";
+ public static final String VENDOR_RHDS = "rhds";
+ public static final String VENDOR_ACTIVE_DIRECTORY = "ad";
+ public static final String VENDOR_OTHER = "other";
+
public static final String CONNECTION_URL = "connectionUrl";
public static final String BASE_DN = "baseDn";
public static final String USER_DN_SUFFIX = "userDnSuffix";
diff --git a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java
new file mode 100644
index 0000000..62c64f3
--- /dev/null
+++ b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/idm/LDAPKeycloakCredentialHandler.java
@@ -0,0 +1,93 @@
+package org.keycloak.picketlink.idm;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Date;
+
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.DirContext;
+import javax.naming.directory.ModificationItem;
+
+import org.picketlink.idm.IdentityManager;
+import org.picketlink.idm.credential.Password;
+import org.picketlink.idm.ldap.internal.LDAPIdentityStore;
+import org.picketlink.idm.ldap.internal.LDAPOperationManager;
+import org.picketlink.idm.ldap.internal.LDAPPlainTextPasswordCredentialHandler;
+import org.picketlink.idm.model.Account;
+import org.picketlink.idm.model.AttributedType;
+import org.picketlink.idm.model.basic.User;
+import org.picketlink.idm.spi.IdentityContext;
+
+import static org.picketlink.idm.IDMLog.CREDENTIAL_LOGGER;
+import static org.picketlink.idm.model.basic.BasicModel.getUser;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class LDAPKeycloakCredentialHandler extends LDAPPlainTextPasswordCredentialHandler {
+
+ private static Method GET_BINDING_DN_METHOD;
+ private static Method GET_OPERATION_MANAGER_METHOD;
+
+ static {
+ GET_BINDING_DN_METHOD = getMethodOnLDAPStore("getBindingDN", AttributedType.class);
+ GET_OPERATION_MANAGER_METHOD = getMethodOnLDAPStore("getOperationManager");
+ }
+
+ // Used just in ActiveDirectory to put account into "enabled" state (aka userAccountControl=512, see http://support.microsoft.com/kb/305144/en ) after password update. If value is -1, it's ignored
+ private String userAccountControlAfterPasswordUpdate;
+
+ @Override
+ public void setup(LDAPIdentityStore store) {
+ if (store.getConfig().isActiveDirectory() || Boolean.getBoolean("keycloak.ldap.ad.skipUserAccountControlAfterPasswordUpdate")) {
+ String userAccountControlProp = System.getProperty("keycloak.ldap.ad.userAccountControlAfterPasswordUpdate");
+ this.userAccountControlAfterPasswordUpdate = userAccountControlProp!=null ? userAccountControlProp : "512";
+ CREDENTIAL_LOGGER.info("Will use userAccountControl=" + userAccountControlAfterPasswordUpdate + " after password update of user in Active Directory");
+ }
+ }
+
+ // Overridden as in Keycloak, we don't have Agents
+ @Override
+ protected Account getAccount(IdentityContext context, String loginName) {
+ IdentityManager identityManager = getIdentityManager(context);
+
+ if (CREDENTIAL_LOGGER.isDebugEnabled()) {
+ CREDENTIAL_LOGGER.debugf("Trying to find account [%s] using default account type [%s]", loginName, User.class);
+ }
+
+ return getUser(identityManager, loginName);
+ }
+
+ @Override
+ public void update(IdentityContext context, Account account, Password password, LDAPIdentityStore store, Date effectiveDate, Date expiryDate) {
+ super.update(context, account, password, store, effectiveDate, expiryDate);
+
+ if (userAccountControlAfterPasswordUpdate != null) {
+ ModificationItem[] mods = new ModificationItem[1];
+ BasicAttribute mod0 = new BasicAttribute("userAccountControl", userAccountControlAfterPasswordUpdate);
+ mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, mod0);
+
+ try {
+ String bindingDN = (String) GET_BINDING_DN_METHOD.invoke(store, account);
+ LDAPOperationManager operationManager = (LDAPOperationManager) GET_OPERATION_MANAGER_METHOD.invoke(store);
+ operationManager.modifyAttribute(bindingDN, mod0);
+ } catch (IllegalAccessException iae) {
+ throw new RuntimeException(iae);
+ } catch (InvocationTargetException ite) {
+ Throwable cause = ite.getTargetException() != null ? ite.getTargetException() : ite;
+ throw new RuntimeException(cause);
+ }
+ }
+ }
+
+ // Hack as methods are protected on LDAPIdentityStore :/
+ private static Method getMethodOnLDAPStore(String methodName, Class... classes) {
+ try {
+ Method m = LDAPIdentityStore.class.getDeclaredMethod(methodName, classes);
+ m.setAccessible(true);
+ return m;
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+}
diff --git a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/PartitionManagerRegistry.java b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/PartitionManagerRegistry.java
index 261fdb3..6204aa7 100644
--- a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/PartitionManagerRegistry.java
+++ b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/PartitionManagerRegistry.java
@@ -6,15 +6,15 @@ import java.util.concurrent.ConcurrentHashMap;
import org.jboss.logging.Logger;
import org.keycloak.models.RealmModel;
-import org.keycloak.picketlink.idm.LDAPAgentIgnoreCredentialHandler;
+import org.keycloak.picketlink.idm.LDAPKeycloakCredentialHandler;
import org.keycloak.picketlink.idm.LdapConstants;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.config.IdentityConfigurationBuilder;
+import org.picketlink.idm.config.LDAPIdentityStoreConfiguration;
import org.picketlink.idm.internal.DefaultPartitionManager;
import org.picketlink.idm.model.basic.User;
import static org.picketlink.common.constants.LDAPConstants.CN;
-import static org.picketlink.common.constants.LDAPConstants.CREATE_TIMESTAMP;
import static org.picketlink.common.constants.LDAPConstants.EMAIL;
import static org.picketlink.common.constants.LDAPConstants.SN;
import static org.picketlink.common.constants.LDAPConstants.UID;
@@ -39,8 +39,8 @@ public class PartitionManagerRegistry {
// Ldap config might have changed for the realm. In this case, we must re-initialize
if (context == null || !ldapConfig.equals(context.config)) {
- logger.infof("Creating new partition manager for the realm: %s, LDAP Connection URL: %s, LDAP Base DN: %s", realm.getId(),
- ldapConfig.get(LdapConstants.CONNECTION_URL), ldapConfig.get(LdapConstants.BASE_DN));
+ logger.infof("Creating new partition manager for the realm: %s, LDAP Connection URL: %s, LDAP Base DN: %s, LDAP Vendor: %s", realm.getId(),
+ ldapConfig.get(LdapConstants.CONNECTION_URL), ldapConfig.get(LdapConstants.BASE_DN), ldapConfig.get(LdapConstants.VENDOR));
PartitionManager manager = createPartitionManager(ldapConfig);
context = new PartitionManagerContext(ldapConfig, manager);
partitionManagers.put(realm.getId(), context);
@@ -66,26 +66,43 @@ public class PartitionManagerRegistry {
checkSystemProperty("com.sun.jndi.ldap.connect.pool.protocol", "plain");
checkSystemProperty("com.sun.jndi.ldap.connect.pool.debug", "off");
+ String vendor = ldapConfig.get(LdapConstants.VENDOR);
+
+ // RHDS is using "nsuniqueid" as unique identifier instead of "entryUUID"
+ if (vendor != null && vendor.equals(LdapConstants.VENDOR_RHDS)) {
+ checkSystemProperty(LDAPIdentityStoreConfiguration.ENTRY_IDENTIFIER_ATTRIBUTE_NAME, "nsuniqueid");
+ }
+
+ boolean activeDirectory = vendor != null && vendor.equals(LdapConstants.VENDOR_ACTIVE_DIRECTORY);
+
+ // Try to compute properties based on LDAP server type, but still allow to override them through System properties TODO: Should allow better way than overriding from System properties. Perhaps init from XML?
+ String ldapLoginName = getNameOfLDAPAttribute("keycloak.ldap.idm.loginName", UID, CN, activeDirectory);
+ String ldapFirstName = getNameOfLDAPAttribute("keycloak.ldap.idm.firstName", CN, "givenName", activeDirectory);
+ String ldapLastName = getNameOfLDAPAttribute("keycloak.ldap.idm.lastName", SN, SN, activeDirectory);
+ String ldapEmail = getNameOfLDAPAttribute("keycloak.ldap.idm.email", EMAIL, EMAIL, activeDirectory);
+
+ logger.infof("LDAP Attributes mapping: loginName: %s, firstName: %s, lastName: %s, email: %s", ldapLoginName, ldapFirstName, ldapLastName, ldapEmail);
+
// Use same mapping for User and Agent for now
builder
.named("SIMPLE_LDAP_STORE_CONFIG")
.stores()
.ldap()
.connectionProperties(connectionProps)
- .addCredentialHandler(LDAPAgentIgnoreCredentialHandler.class)
+ .addCredentialHandler(LDAPKeycloakCredentialHandler.class)
.baseDN(ldapConfig.get(LdapConstants.BASE_DN))
.bindDN(ldapConfig.get(LdapConstants.BIND_DN))
.bindCredential(ldapConfig.get(LdapConstants.BIND_CREDENTIAL))
.url(ldapConfig.get(LdapConstants.CONNECTION_URL))
+ .activeDirectory(activeDirectory)
.supportAllFeatures()
.mapping(User.class)
.baseDN(ldapConfig.get(LdapConstants.USER_DN_SUFFIX))
.objectClasses("inetOrgPerson", "organizationalPerson")
- .attribute("loginName", UID, true)
- .attribute("firstName", CN)
- .attribute("lastName", SN)
- .attribute("email", EMAIL)
- .readOnlyAttribute("createdDate", CREATE_TIMESTAMP);
+ .attribute("loginName", ldapLoginName, true)
+ .attribute("firstName", ldapFirstName)
+ .attribute("lastName", ldapLastName)
+ .attribute("email", ldapEmail);
return new DefaultPartitionManager(builder.buildAll());
}
@@ -96,6 +113,16 @@ public class PartitionManagerRegistry {
}
}
+ private String getNameOfLDAPAttribute(String systemPropertyName, String defaultAttrName, String defaultAttrNameInActiveDirectory, boolean activeDirectory) {
+ // System property has biggest priority if available
+ String sysProperty = System.getProperty(systemPropertyName);
+ if (sysProperty != null) {
+ return sysProperty;
+ }
+
+ return activeDirectory ? defaultAttrNameInActiveDirectory : defaultAttrName;
+ }
+
private class PartitionManagerContext {
private PartitionManagerContext(Map<String,String> config, PartitionManager manager) {
diff --git a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/RealmIdentityManagerProvider.java b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/RealmIdentityManagerProvider.java
index 3c00489..849aa98 100644
--- a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/RealmIdentityManagerProvider.java
+++ b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/RealmIdentityManagerProvider.java
@@ -1,6 +1,7 @@
package org.keycloak.picketlink.realm;
import org.keycloak.models.RealmModel;
+import org.keycloak.picketlink.AbstractIdentityManagerProvider;
import org.keycloak.picketlink.IdentityManagerProvider;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
@@ -8,7 +9,7 @@ import org.picketlink.idm.PartitionManager;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
-public class RealmIdentityManagerProvider implements IdentityManagerProvider {
+public class RealmIdentityManagerProvider extends AbstractIdentityManagerProvider {
private final PartitionManagerRegistry partitionManagerRegistry;
@@ -17,12 +18,7 @@ public class RealmIdentityManagerProvider implements IdentityManagerProvider {
}
@Override
- public IdentityManager getIdentityManager(RealmModel realm) {
- PartitionManager partitionManager = partitionManagerRegistry.getPartitionManager(realm);
- return partitionManager.createIdentityManager();
- }
-
- @Override
- public void close() {
+ protected PartitionManager getPartitionManager(RealmModel realm) {
+ return partitionManagerRegistry.getPartitionManager(realm);
}
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
index cd367c9..839813c 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/AuthProvidersIntegrationTest.java
@@ -62,7 +62,7 @@ public class AuthProvidersIntegrationTest {
// Configure LDAP
ldapRule.getEmbeddedServer().setupLdapInRealm(appRealm);
- LdapTestUtils.setLdapPassword(providerSession, appRealm, "john", "password");
+ LdapTestUtils.setLdapPassword(providerSession, appRealm, "johnkeycloak", "password");
}
});
@@ -135,7 +135,7 @@ public class AuthProvidersIntegrationTest {
@Test
public void loginLdap() {
loginPage.open();
- loginPage.login("john", "password");
+ loginPage.login("johnkeycloak", "password");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
@@ -186,20 +186,19 @@ public class AuthProvidersIntegrationTest {
@Test
public void passwordChangeLdap() throws Exception {
changePasswordPage.open();
- loginPage.login("john", "password");
+ loginPage.login("johnkeycloak", "password");
changePasswordPage.changePassword("password", "new-password", "new-password");
Assert.assertEquals("Your password has been updated", profilePage.getSuccess());
changePasswordPage.logout();
-// TODO: Disabled until https://issues.jboss.org/browse/PLINK-384 is released and updated
-// loginPage.open();
-// loginPage.login("john", "password");
-// Assert.assertEquals("Invalid username or password.", loginPage.getError());
+ loginPage.open();
+ loginPage.login("johnkeycloak", "password");
+ Assert.assertEquals("Invalid username or password.", loginPage.getError());
loginPage.open();
- loginPage.login("john", "new-password");
+ loginPage.login("johnkeycloak", "new-password");
Assert.assertEquals(AppPage.RequestType.AUTH_RESPONSE, appPage.getRequestType());
}
diff --git a/testsuite/integration/src/test/resources/ldap/users.ldif b/testsuite/integration/src/test/resources/ldap/users.ldif
index 0debe0b..386a9d6 100644
--- a/testsuite/integration/src/test/resources/ldap/users.ldif
+++ b/testsuite/integration/src/test/resources/ldap/users.ldif
@@ -19,12 +19,12 @@ objectclass: top
objectclass: organizationalUnit
ou: Groups
-dn: uid=john,ou=People,dc=keycloak,dc=org
+dn: uid=johnkeycloak,ou=People,dc=keycloak,dc=org
objectclass: top
objectclass: uidObject
objectclass: person
objectclass: inetOrgPerson
-uid: john
+uid: johnkeycloak
cn: John
sn: Doe
mail: john@email.org