From 0af68d28f187e9a3f84eb8717e81433bb945784c Mon Sep 17 00:00:00 2001 From: mposolda Date: Mon, 8 Jun 2015 12:28:59 +0200 Subject: [PATCH] KEYCLOAK-1357 LDAP migration --- .../keycloak/federation/ldap/LDAPConfig.java | 37 +++++++-------- .../ldap/LDAPFederationProviderFactory.java | 4 +- .../ldap/LDAPIdentityStoreRegistry.java | 17 ------- .../keycloak/migration/MigrationModel.java | 2 +- .../migration/MigrationModelManager.java | 6 ++- .../org/keycloak/migration/ModelVersion.java | 2 +- .../migrators/MigrateTo1_3_0_Beta1.java | 46 +++++++++++++++++++ .../org/keycloak/models/LDAPConstants.java | 19 ++++++++ ...erFederationEventAwareProviderFactory.java | 2 +- .../keycloak/models/MigrationVersionTest.java | 22 ++++----- 10 files changed, 101 insertions(+), 56 deletions(-) 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 4ebde77865..3317b9d1d0 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 @@ -48,7 +48,14 @@ public class LDAPConfig { } public String getUsersDn() { - return config.get(LDAPConstants.USERS_DN); + String usersDn = config.get(LDAPConstants.USERS_DN); + + if (usersDn == null) { + // Just for the backwards compatibility 1.2 -> 1.3 . Should be removed later. + usersDn = config.get("userDnSuffix"); + } + + return usersDn; } public Collection getUserObjectClasses() { @@ -101,31 +108,13 @@ public class LDAPConfig { if (uuidAttrName == null) { // Differences of unique attribute among various vendors String vendor = getVendor(); - if (vendor != null) { - switch (vendor) { - case LDAPConstants.VENDOR_RHDS: - uuidAttrName = "nsuniqueid"; - break; - case LDAPConstants.VENDOR_TIVOLI: - uuidAttrName = "uniqueidentifier"; - break; - case LDAPConstants.VENDOR_NOVELL_EDIRECTORY: - uuidAttrName = "guid"; - break; - case LDAPConstants.VENDOR_ACTIVE_DIRECTORY: - uuidAttrName = LDAPConstants.OBJECT_GUID; - } - } - - if (uuidAttrName == null) { - uuidAttrName = LDAPConstants.ENTRY_UUID; - } + uuidAttrName = LDAPConstants.getUuidAttributeName(vendor); } return uuidAttrName; } - // TODO: Remove and use mapper instead + // TODO: Remove and use mapper instead? public boolean isUserAccountControlsAfterPasswordUpdate() { String userAccountCtrls = config.get(LDAPConstants.USER_ACCOUNT_CONTROLS_AFTER_PASSWORD_UPDATE); return userAccountCtrls==null ? false : Boolean.parseBoolean(userAccountCtrls); @@ -148,6 +137,12 @@ public class LDAPConfig { String rdn = config.get(LDAPConstants.RDN_LDAP_ATTRIBUTE); if (rdn == null) { rdn = getUsernameLdapAttribute(); + + if (rdn.equalsIgnoreCase(LDAPConstants.SAM_ACCOUNT_NAME)) { + // Just for the backwards compatibility 1.2 -> 1.3 . Should be removed later. + rdn = LDAPConstants.CN; + } + } return rdn; } 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 5c1bf6e76f..876a96d5fc 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 @@ -41,7 +41,7 @@ import java.util.Set; */ public class LDAPFederationProviderFactory extends UserFederationEventAwareProviderFactory { private static final Logger logger = Logger.getLogger(LDAPFederationProviderFactory.class); - public static final String PROVIDER_NAME = "ldap"; + public static final String PROVIDER_NAME = LDAPConstants.LDAP_PROVIDER; private LDAPIdentityStoreRegistry ldapStoreRegistry; @@ -79,7 +79,7 @@ public class LDAPFederationProviderFactory extends UserFederationEventAwareProvi // Best effort to create appropriate mappers according to our LDAP config @Override - protected void onProviderModelCreated(RealmModel realm, UserFederationProviderModel newProviderModel) { + public void onProviderModelCreated(RealmModel realm, UserFederationProviderModel newProviderModel) { LDAPConfig ldapConfig = new LDAPConfig(newProviderModel.getConfig()); boolean activeDirectory = ldapConfig.isActiveDirectory(); diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java index c737266a03..c9d7bb25df 100644 --- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java +++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/LDAPIdentityStoreRegistry.java @@ -55,23 +55,6 @@ public class LDAPIdentityStoreRegistry { checkSystemProperty("com.sun.jndi.ldap.connect.pool.protocol", "plain"); checkSystemProperty("com.sun.jndi.ldap.connect.pool.debug", "off"); - /*String ldapLoginNameMapping = ldapConfig.get(LDAPConstants.USERNAME_LDAP_ATTRIBUTE); - if (ldapLoginNameMapping == null) { - ldapLoginNameMapping = activeDirectory ? LDAPConstants.CN : LDAPConstants.UID; - } - - String ldapFirstNameMapping = activeDirectory ? "givenName" : LDAPConstants.CN; - String createTimestampMapping = activeDirectory ? "whenCreated" : LDAPConstants.CREATE_TIMESTAMP; - String modifyTimestampMapping = activeDirectory ? "whenChanged" : LDAPConstants.MODIFY_TIMESTAMP; - String[] userObjectClasses = getUserObjectClasses(ldapConfig); */ - - -/* if (activeDirectory && ldapLoginNameMapping.equals("sAMAccountName")) { - ldapUserMappingConfig.setBindingDnPropertyName("fullName"); - ldapUserMappingConfig.addAttributeMapping("fullName", LDAPConstants.CN); - logger.infof("Using 'cn' attribute for DN of user and 'sAMAccountName' for username"); - } */ - return new LDAPIdentityStore(cfg); } diff --git a/model/api/src/main/java/org/keycloak/migration/MigrationModel.java b/model/api/src/main/java/org/keycloak/migration/MigrationModel.java index 936fbcf1da..df24b3d009 100755 --- a/model/api/src/main/java/org/keycloak/migration/MigrationModel.java +++ b/model/api/src/main/java/org/keycloak/migration/MigrationModel.java @@ -11,7 +11,7 @@ public interface MigrationModel { /** * Must have the form of major.minor.micro as the version is parsed and numbers are compared */ - public static final String LATEST_VERSION = "1.2.0.CR1"; + public static final String LATEST_VERSION = "1.3.0.Beta1"; String getStoredVersion(); void setStoredVersion(String version); diff --git a/model/api/src/main/java/org/keycloak/migration/MigrationModelManager.java b/model/api/src/main/java/org/keycloak/migration/MigrationModelManager.java index 7f52ab38e5..5d58fe1b1a 100755 --- a/model/api/src/main/java/org/keycloak/migration/MigrationModelManager.java +++ b/model/api/src/main/java/org/keycloak/migration/MigrationModelManager.java @@ -17,11 +17,13 @@ public class MigrationModelManager { String storedVersion = model.getStoredVersion(); if (MigrationModel.LATEST_VERSION.equals(storedVersion)) return; ModelVersion stored = null; - if (storedVersion != null) new ModelVersion(storedVersion); + if (storedVersion != null) { + stored = new ModelVersion(storedVersion); + } if (stored == null || stored.lessThan(MigrationTo1_2_0_CR1.VERSION)) { if (stored != null) { - logger.debug("Migrating older model to 1.2.0.RC1 updates"); + logger.debug("Migrating older model to 1.2.0.CR1 updates"); } new MigrationTo1_2_0_CR1().migrate(session); } diff --git a/model/api/src/main/java/org/keycloak/migration/ModelVersion.java b/model/api/src/main/java/org/keycloak/migration/ModelVersion.java index 1dfcd14c0d..095576a560 100755 --- a/model/api/src/main/java/org/keycloak/migration/ModelVersion.java +++ b/model/api/src/main/java/org/keycloak/migration/ModelVersion.java @@ -59,7 +59,7 @@ public class ModelVersion { if (major < version.major) return true; if (minor < version.minor) return true; if (micro < version.micro) return true; - if (qualifier == version.qualifier) return false; + if (qualifier != null && qualifier.equals(version.qualifier)) return false; if (qualifier == null) return false; if (version.qualifier == null) return true; int comp = qualifier.compareTo(version.qualifier); diff --git a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0_Beta1.java b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0_Beta1.java index ccf3c75373..ce013404cf 100755 --- a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0_Beta1.java +++ b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_3_0_Beta1.java @@ -2,10 +2,18 @@ package org.keycloak.migration.migrators; import org.keycloak.migration.ModelVersion; import org.keycloak.models.KeycloakSession; +import org.keycloak.models.LDAPConstants; import org.keycloak.models.RealmModel; +import org.keycloak.models.UserFederationEventAwareProviderFactory; +import org.keycloak.models.UserFederationProvider; +import org.keycloak.models.UserFederationProviderFactory; +import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.utils.DefaultAuthenticationFlows; import java.util.List; +import java.util.Map; + +import javax.naming.directory.SearchControls; /** * @author Bill Burke @@ -21,7 +29,45 @@ public class MigrateTo1_3_0_Beta1 { if (realm.getAuthenticationFlows().size() == 0) { DefaultAuthenticationFlows.addFlows(realm); } + + migrateLDAPProviders(session, realm); } } + + private void migrateLDAPProviders(KeycloakSession session, RealmModel realm) { + List federationProviders = realm.getUserFederationProviders(); + for (UserFederationProviderModel fedProvider : federationProviders) { + + if (fedProvider.getProviderName().equals(LDAPConstants.LDAP_PROVIDER)) { + Map config = fedProvider.getConfig(); + + // Update config properties for LDAP federation provider + config.put(LDAPConstants.SEARCH_SCOPE, String.valueOf(SearchControls.SUBTREE_SCOPE)); + + String usersDn = config.remove("userDnSuffix"); + config.put(LDAPConstants.USERS_DN, usersDn); + + String rdnLdapAttribute = config.get(LDAPConstants.USERNAME_LDAP_ATTRIBUTE); + if (rdnLdapAttribute != null) { + if (rdnLdapAttribute.equalsIgnoreCase(LDAPConstants.SAM_ACCOUNT_NAME)) { + config.put(LDAPConstants.RDN_LDAP_ATTRIBUTE, LDAPConstants.CN); + } else { + config.put(LDAPConstants.RDN_LDAP_ATTRIBUTE, rdnLdapAttribute); + } + } + + String uuidAttrName = LDAPConstants.getUuidAttributeName(config.get(LDAPConstants.VENDOR)); + config.put(LDAPConstants.UUID_LDAP_ATTRIBUTE, uuidAttrName); + + realm.updateUserFederationProvider(fedProvider); + + // Create default mappers for LDAP + UserFederationProviderFactory ldapFactory = (UserFederationProviderFactory) session.getKeycloakSessionFactory().getProviderFactory(UserFederationProvider.class, LDAPConstants.LDAP_PROVIDER); + if (ldapFactory != null) { + ((UserFederationEventAwareProviderFactory) ldapFactory).onProviderModelCreated(realm, fedProvider); + } + } + } + } } 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 22f8979021..0d97664029 100644 --- a/model/api/src/main/java/org/keycloak/models/LDAPConstants.java +++ b/model/api/src/main/java/org/keycloak/models/LDAPConstants.java @@ -5,6 +5,8 @@ package org.keycloak.models; */ public class LDAPConstants { + public static final String LDAP_PROVIDER = "ldap"; + public static final String VENDOR = "vendor"; public static final String VENDOR_RHDS = "rhds"; public static final String VENDOR_ACTIVE_DIRECTORY = "ad"; @@ -80,4 +82,21 @@ public class LDAPConstants { public static final String OBJECT_GUID = "objectGUID"; public static final String CREATE_TIMESTAMP = "createTimestamp"; public static final String MODIFY_TIMESTAMP = "modifyTimestamp"; + + public static String getUuidAttributeName(String vendor) { + if (vendor != null) { + switch (vendor) { + case VENDOR_RHDS: + return "nsuniqueid"; + case VENDOR_TIVOLI: + return "uniqueidentifier"; + case VENDOR_NOVELL_EDIRECTORY: + return "guid"; + case VENDOR_ACTIVE_DIRECTORY: + return OBJECT_GUID; + } + } + + return ENTRY_UUID; + } } diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationEventAwareProviderFactory.java b/model/api/src/main/java/org/keycloak/models/UserFederationEventAwareProviderFactory.java index 866bf79881..b409d8784d 100644 --- a/model/api/src/main/java/org/keycloak/models/UserFederationEventAwareProviderFactory.java +++ b/model/api/src/main/java/org/keycloak/models/UserFederationEventAwareProviderFactory.java @@ -29,5 +29,5 @@ public abstract class UserFederationEventAwareProviderFactory implements UserFed }); } - protected abstract void onProviderModelCreated(RealmModel realm, UserFederationProviderModel createdProviderModel); + public abstract void onProviderModelCreated(RealmModel realm, UserFederationProviderModel createdProviderModel); } diff --git a/model/api/src/test/java/org/keycloak/models/MigrationVersionTest.java b/model/api/src/test/java/org/keycloak/models/MigrationVersionTest.java index c706d8a07c..9bdd231867 100755 --- a/model/api/src/test/java/org/keycloak/models/MigrationVersionTest.java +++ b/model/api/src/test/java/org/keycloak/models/MigrationVersionTest.java @@ -16,30 +16,30 @@ public class MigrationVersionTest { Assert.assertEquals(version_100Beta1.getMajor(), 1); Assert.assertEquals(version_100Beta1.getMinor(), 0); Assert.assertEquals(version_100Beta1.getMicro(), 0); - ModelVersion version_100RC1 = new ModelVersion("1.0.0.RC1"); + ModelVersion version_100CR1 = new ModelVersion("1.0.0.CR1"); ModelVersion version_100 = new ModelVersion("1.0.0"); ModelVersion version_110Beta1 = new ModelVersion("1.1.0.Beta1"); - ModelVersion version_110RC1 = new ModelVersion("1.1.0.RC1"); + ModelVersion version_110CR1 = new ModelVersion("1.1.0.CR1"); ModelVersion version_110 = new ModelVersion("1.1.0"); ModelVersion version_111Beta1 = new ModelVersion("1.1.1.Beta1"); - ModelVersion version_111RC1 = new ModelVersion("1.1.1.RC1"); + ModelVersion version_111CR1 = new ModelVersion("1.1.1.CR1"); ModelVersion version_111 = new ModelVersion("1.1.1"); ModelVersion version_211Beta1 = new ModelVersion("2.1.1.Beta1"); - ModelVersion version_211RC1 = new ModelVersion("2.1.1.RC1"); - Assert.assertEquals(version_211RC1.getMajor(), 2); - Assert.assertEquals(version_211RC1.getMinor(), 1); - Assert.assertEquals(version_211RC1.getMicro(), 1); - Assert.assertEquals(version_211RC1.getQualifier(), "RC1"); + ModelVersion version_211CR1 = new ModelVersion("2.1.1.CR1"); + Assert.assertEquals(version_211CR1.getMajor(), 2); + Assert.assertEquals(version_211CR1.getMinor(), 1); + Assert.assertEquals(version_211CR1.getMicro(), 1); + Assert.assertEquals(version_211CR1.getQualifier(), "CR1"); ModelVersion version_211 = new ModelVersion("2.1.1"); Assert.assertFalse(version_100Beta1.lessThan(version_100Beta1)); - Assert.assertTrue(version_100Beta1.lessThan(version_100RC1)); + Assert.assertTrue(version_100Beta1.lessThan(version_100CR1)); Assert.assertTrue(version_100Beta1.lessThan(version_100)); Assert.assertTrue(version_100Beta1.lessThan(version_110Beta1)); - Assert.assertTrue(version_100Beta1.lessThan(version_110RC1)); + Assert.assertTrue(version_100Beta1.lessThan(version_110CR1)); Assert.assertTrue(version_100Beta1.lessThan(version_110)); - Assert.assertFalse(version_211.lessThan(version_110RC1)); + Assert.assertFalse(version_211.lessThan(version_110CR1)); } }