Merge pull request #458 from mposolda/master
Support for Active Directory and RHDS
This commit is contained in:
commit
9a3abf6a6a
16 changed files with 226 additions and 70 deletions
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -13,6 +13,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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
package org.keycloak.picketlink.idm;
|
||||
|
||||
import org.picketlink.idm.IdentityManager;
|
||||
import org.picketlink.idm.ldap.internal.LDAPPlainTextPasswordCredentialHandler;
|
||||
import org.picketlink.idm.model.Account;
|
||||
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 LDAPAgentIgnoreCredentialHandler extends LDAPPlainTextPasswordCredentialHandler {
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue