From 56dce4af5a9b46866ad130ecf91742b5e1ca698d Mon Sep 17 00:00:00 2001 From: srosse <none@none> Date: Tue, 25 Nov 2014 15:06:17 +0100 Subject: [PATCH] OO-1259: implements synchronization of groups and roles via LDAP --- .../basesecurity/BaseSecurityManager.java | 4 +- src/main/java/org/olat/ldap/LDAPHelper.java | 249 ----- .../java/org/olat/ldap/LDAPLoginManager.java | 37 +- .../java/org/olat/ldap/LDAPLoginModule.java | 536 ++++------ .../org/olat/ldap/LDAPSyncConfiguration.java | 524 ++++++++++ .../org/olat/ldap/_spring/ldapContext.xml | 170 ++-- .../java/org/olat/ldap/manager/LDAPDAO.java | 360 +++++++ .../olat/ldap/manager/LDAPGroupVisitor.java | 73 ++ .../{ => manager}/LDAPLoginManagerImpl.java | 922 ++++++++++-------- .../olat/ldap/manager/LDAPUserVisitor.java | 119 +++ .../org/olat/ldap/manager/LDAPVisitor.java | 35 + .../java/org/olat/ldap/model/LDAPGroup.java | 82 ++ .../java/org/olat/ldap/model/LDAPUser.java | 148 +++ .../java/org/olat/ldap/ui/DeletStep01.java | 8 +- .../org/olat/ldap/ui/LDAPAdminController.java | 6 +- .../ldap/ui/LDAPAuthenticationController.java | 12 +- .../org/olat/login/auth/OLATAuthManager.java | 6 +- .../olat/user/ChangePasswordController.java | 26 +- .../resources/serviceconfig/olat.properties | 36 + .../java/org/olat/ldap/LDAPLoginTest.java | 44 +- 20 files changed, 2219 insertions(+), 1178 deletions(-) delete mode 100644 src/main/java/org/olat/ldap/LDAPHelper.java create mode 100644 src/main/java/org/olat/ldap/LDAPSyncConfiguration.java create mode 100644 src/main/java/org/olat/ldap/manager/LDAPDAO.java create mode 100644 src/main/java/org/olat/ldap/manager/LDAPGroupVisitor.java rename src/main/java/org/olat/ldap/{ => manager}/LDAPLoginManagerImpl.java (50%) create mode 100644 src/main/java/org/olat/ldap/manager/LDAPUserVisitor.java create mode 100644 src/main/java/org/olat/ldap/manager/LDAPVisitor.java create mode 100644 src/main/java/org/olat/ldap/model/LDAPGroup.java create mode 100644 src/main/java/org/olat/ldap/model/LDAPUser.java diff --git a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java index 7d5dc96cbff..66030631a2c 100644 --- a/src/main/java/org/olat/basesecurity/BaseSecurityManager.java +++ b/src/main/java/org/olat/basesecurity/BaseSecurityManager.java @@ -447,11 +447,11 @@ public class BaseSecurityManager extends BasicManager implements BaseSecurity { if (!hasBeenInGroup && isNowInGroup) { // user not yet in security group, add him addIdentityToSecurityGroup(updatedIdentity, securityGroup); - logAudit("User::" + actingIdentity.getName() + " added system role::" + groupName + " to user::" + updatedIdentity.getName(), null); + logAudit("User::" + (actingIdentity == null ? "unkown" : actingIdentity.getName()) + " added system role::" + groupName + " to user::" + updatedIdentity.getName(), null); } else if (hasBeenInGroup && !isNowInGroup) { // user not anymore in security group, remove him removeIdentityFromSecurityGroup(updatedIdentity, securityGroup); - logAudit("User::" + actingIdentity.getName() + " removed system role::" + groupName + " from user::" + updatedIdentity.getName(), null); + logAudit("User::" + (actingIdentity == null ? "unkown" : actingIdentity.getName()) + " removed system role::" + groupName + " from user::" + updatedIdentity.getName(), null); } } diff --git a/src/main/java/org/olat/ldap/LDAPHelper.java b/src/main/java/org/olat/ldap/LDAPHelper.java deleted file mode 100644 index 20e2846e9c7..00000000000 --- a/src/main/java/org/olat/ldap/LDAPHelper.java +++ /dev/null @@ -1,249 +0,0 @@ -/** - * <a href="http://www.openolat.org"> - * OpenOLAT - Online Learning and Training</a><br> - * <p> - * Licensed under the Apache License, Version 2.0 (the "License"); <br> - * you may not use this file except in compliance with the License.<br> - * You may obtain a copy of the License at the - * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> - * <p> - * Unless required by applicable law or agreed to in writing,<br> - * software distributed under the License is distributed on an "AS IS" BASIS, <br> - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> - * See the License for the specific language governing permissions and <br> - * limitations under the License. - * <p> - * Initial code contributed and copyrighted by<br> - * frentix GmbH, http://www.frentix.com - * <p> - */ -package org.olat.ldap; - -import java.io.FileInputStream; -import java.security.KeyStore; -import java.security.cert.Certificate; -import java.security.cert.X509Certificate; -import java.util.Date; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import javax.naming.NamingException; -import javax.naming.directory.Attribute; -import javax.naming.directory.Attributes; - -import org.olat.core.logging.OLog; -import org.olat.core.logging.Tracing; -import org.olat.user.UserManager; -import org.olat.user.propertyhandlers.UserPropertyHandler; - -/** - * Description: Helper methods for the LDAP package - * <p> - * LDAPHelper - * <ul> - * </ul> - * <p> - * - * @author Maurus Rohrer - */ - -public class LDAPHelper { - private static OLog log = Tracing.createLoggerFor(LDAPHelper.class); - - private LDAPHelper() { - // do nothing - } - - /** - * Maps LDAP Attributes to the OLAT Property - * - * Configuration: LDAP Attributes Map = olatextconfig.xml (property=userAttrs) - * - * @param attrID LDAP Attribute - * @return OLAT Property - */ - public static String mapLdapAttributeToOlatProperty(String attrID) { - Map<String, String> userAttrMapper = LDAPLoginModule.getUserAttributeMapper(); - String olatProperty = userAttrMapper.get(attrID); - return olatProperty; - } - - - /** - * Maps OLAT Property to the LDAP Attributes - * - * Configuration: LDAP Attributes Map = olatextconfig.xml (property=userAttrs) - * - * @param olatProperty OLAT PropertyattrID - * @return LDAP Attribute - */ - public static String mapOlatPropertyToLdapAttribute(String olatProperty) { - Map<String, String> userAttrMapper = LDAPLoginModule.getReqAttrs(); - if (userAttrMapper.containsValue(olatProperty)) { - Iterator<String> itr = userAttrMapper.keySet().iterator(); - while (itr.hasNext()) { - String key = itr.next(); - if (userAttrMapper.get(key).compareTo(olatProperty) == 0) return key; - } - } - return null; - } - - /** - * Extracts Value out of LDAP Attribute - * - * - * @param attribute LDAP Naming Attribute - * @return String value of Attribute, null on Exception - * - * @throws NamingException - */ - public static String getAttributeValue(Attribute attribute) { - try { - String attrValue = (String) attribute.get(); - return attrValue; - } catch (NamingException e) { - log.error("NamingException when trying to get attribute value for attribute::" + attribute, e); - return null; - } - } - - - /** - * Checks if Collection of naming Attributes contain defined required properties for OLAT - * - * * Configuration: LDAP Required Map = olatextconfig.xml (property=reqAttrs) - * - * @param attributes Collection of LDAP Naming Attribute - * @return null If all required Attributes are found, otherwise String[] of missing Attributes - * - */ - public static String[] checkReqAttr(Attributes attrs) { - Map<String, String> reqAttr = LDAPLoginModule.getReqAttrs(); - String[] missingAttr = new String[reqAttr.size()]; - int y = 0; - Iterator<String> reqItr = reqAttr.keySet().iterator(); - while (reqItr.hasNext()) { - String attKey = reqItr.next().trim(); - if (attrs.get(attKey) == null) { - missingAttr[y++] = attKey; - } - } - if (y == 0) return null; - else return missingAttr; - } - - /** - * Checks if defined OLAT Properties in olatextconfig.xml exist in OLAT. - * - * Configuration: LDAP Attributes Map = olatextconfig.xml (property=reqAttrs, property=userAttributeMapper) - * - * @param attrs Map of OLAT Properties from of the LDAP configuration - * @return true All exist OK, false Error - * - */ - public static boolean checkIfOlatPropertiesExists(Map<String, String> attrs) { - List<UserPropertyHandler> upHandler = UserManager.getInstance().getAllUserPropertyHandlers(); - for (String ldapAttribute : attrs.keySet()) { - boolean propertyExists = false; - String olatProperty = attrs.get(ldapAttribute); - if (olatProperty.equals(LDAPConstants.LDAP_USER_IDENTIFYER)) { - // LDAP user identifyer is not a user propery, it's the username - continue; - } - for (UserPropertyHandler userPropItr : upHandler) { - if (olatProperty.equals(userPropItr.getName())) { - // ok, this property exist, continue with next one - propertyExists = true; - break; - } - } - if ( ! propertyExists ) { - log - .error("Error in checkIfOlatPropertiesExists(): configured LDAP attribute::" - + ldapAttribute - + " configured to map to OLAT user property::" - + olatProperty - + " but no such user property configured in olat_userconfig.xml"); - return false; - } - } - return true; - } - - /** - * Checks if defined Static OLAT Property in olatextconfig.xml exist in OLAT. - * - * Configuration: olatextconfig.xml (property=staticUserProperties) - * - * @param olatProperties Set of OLAT Properties from of the LDAP configuration - * @return true All exist OK, false Error - * - */ - public static boolean checkIfStaticOlatPropertiesExists(Set<String> olatProperties) { - List<UserPropertyHandler> upHandler = UserManager.getInstance().getAllUserPropertyHandlers(); - for (String olatProperty : olatProperties) { - boolean propertyExists = false; - for (UserPropertyHandler userPropItr : upHandler) { - if (olatProperty.equals(userPropItr.getName())) { - // ok, this property exist, continue with next one - propertyExists = true; - break; - } - } - if ( ! propertyExists ) { - log - .error("Error in checkIfStaticOlatPropertiesExists(): configured static OLAT user property::" - + olatProperty - + " is not configured in olat_userconfig.xml"); - return false; - } - } - return true; - } - - /** - * Checks if SSL certification is know and accepted by Java JRE. - * - * - * @param dayFromNow Checks expiration - * @return true Certification accepted, false No valid certification - * - * @throws Exception - * - */ - public static boolean checkServerCertValidity(int daysFromNow) { - KeyStore keyStore; - try { - keyStore = KeyStore.getInstance(LDAPLoginModule.getTrustStoreType()); - keyStore.load(new FileInputStream(LDAPLoginModule.getTrustStoreLocation()), (LDAPLoginModule.getTrustStorePwd() != null) ? LDAPLoginModule.getTrustStorePwd().toCharArray() : null); - Enumeration<String> aliases = keyStore.aliases(); - while (aliases.hasMoreElements()) { - String alias = aliases.nextElement(); - Certificate cert = keyStore.getCertificate(alias); - if (cert instanceof X509Certificate) { - return isCertificateValid((X509Certificate)cert, daysFromNow); - } - } - } catch (Exception e) { - return false; - } - return false; - } - - private static boolean isCertificateValid(X509Certificate x509Cert, int daysFromNow) { - try { - x509Cert.checkValidity(); - if (daysFromNow > 0) { - Date nowPlusDays = new Date(System.currentTimeMillis() + (new Long(daysFromNow).longValue() * 24l * 60l * 60l * 1000l)); - x509Cert.checkValidity(nowPlusDays); - } - } catch (Exception e) { - return false; - } - return true; - } -} diff --git a/src/main/java/org/olat/ldap/LDAPLoginManager.java b/src/main/java/org/olat/ldap/LDAPLoginManager.java index 984e2ae0b39..444e91d3dfd 100644 --- a/src/main/java/org/olat/ldap/LDAPLoginManager.java +++ b/src/main/java/org/olat/ldap/LDAPLoginManager.java @@ -29,44 +29,41 @@ import javax.naming.ldap.LdapContext; import org.olat.core.id.Identity; import org.olat.core.id.OLATResourceable; -import org.olat.core.manager.BasicManager; import org.olat.core.util.resource.OresHelper; -public abstract class LDAPLoginManager extends BasicManager { +public interface LDAPLoginManager { public static final OLATResourceable ldapSyncLockOres = OresHelper.createOLATResourceableInstance(LDAPLoginManager.class, 0l); - public abstract LdapContext bindSystem(); + public LdapContext bindSystem(); - public abstract Attributes bindUser(String uid, String pwd, LDAPError errors); + public Attributes bindUser(String uid, String pwd, LDAPError errors); - public abstract void changePassword(Identity identity, String pwd, LDAPError errors); - - public abstract List<Attributes> getUserAttributesModifiedSince(Date syncTime, LdapContext ctx); + public void changePassword(Identity identity, String pwd, LDAPError errors); - public abstract void createAndPersistUser(Attributes userAttributes); + public Identity createAndPersistUser(Attributes userAttributes); - public abstract Map<String,String> prepareUserPropertyForSync(Attributes attributes, Identity identity); + public Map<String,String> prepareUserPropertyForSync(Attributes attributes, Identity identity); - public abstract List<Identity> getIdentitysDeletedInLdap(LdapContext ctx); + public List<Identity> getIdentitysDeletedInLdap(LdapContext ctx); - public abstract Identity findIdentyByLdapAuthentication(String uid, LDAPError errors); + public Identity findIdentyByLdapAuthentication(String uid, LDAPError errors); - public abstract void syncUser(Map<String,String> olatPropertyMap, Identity identity); + public void syncUser(Map<String,String> olatPropertyMap, Identity identity); - public abstract void deletIdentities(List<Identity> identityList); + public void deletIdentities(List<Identity> identityList); - public abstract boolean doBatchSync(LDAPError errors, boolean full); + public boolean doBatchSync(LDAPError errors, boolean full); - public abstract Date getLastSyncDate(); + public Date getLastSyncDate(); - public abstract boolean acquireSyncLock(); + public boolean acquireSyncLock(); - public abstract void freeSyncLock(); + public void freeSyncLock(); - public abstract void doSyncSingleUser(Identity ident); + public void doSyncSingleUser(Identity ident); - public abstract void removeFallBackAuthentications(); + public void removeFallBackAuthentications(); /** * returns true, if the given identity is member of the LDAP-securitygroup @@ -74,5 +71,5 @@ public abstract class LDAPLoginManager extends BasicManager { * @param ident * @return */ - public abstract boolean isIdentityInLDAPSecGroup(Identity ident); + public boolean isIdentityInLDAPSecGroup(Identity ident); } diff --git a/src/main/java/org/olat/ldap/LDAPLoginModule.java b/src/main/java/org/olat/ldap/LDAPLoginModule.java index 7bf78562656..dd3fd0157cb 100644 --- a/src/main/java/org/olat/ldap/LDAPLoginModule.java +++ b/src/main/java/org/olat/ldap/LDAPLoginModule.java @@ -25,33 +25,24 @@ import java.security.KeyStore; import java.security.cert.Certificate; import java.security.cert.X509Certificate; import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Date; import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import javax.naming.directory.Attributes; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.SecurityGroup; -import org.olat.core.configuration.Initializable; +import org.olat.core.configuration.AbstractSpringModule; import org.olat.core.logging.OLog; -import org.olat.core.logging.StartupException; import org.olat.core.logging.Tracing; import org.olat.core.util.StringHelper; +import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.user.UserManager; -import org.olat.user.propertyhandlers.UserPropertyHandler; import org.quartz.CronTrigger; import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; /** * Description: @@ -63,81 +54,116 @@ import org.quartz.SchedulerException; * * @author maurus.rohrer@gmail.com */ -public class LDAPLoginModule implements Initializable { +@Service("org.olat.ldap.LDAPLoginModule") +public class LDAPLoginModule extends AbstractSpringModule { // Connection configuration - private static String ldapUrl; - private static boolean ldapEnabled; - private static boolean activeDirectory; - private static String ldapDateFormat; + + @Value("${ldap.ldapUrl}") + private String ldapUrl; + @Value("${ldap.enable:false}") + private boolean ldapEnabled; + @Value("${ldap.activeDirectory:false}") + private boolean activeDirectory; + @Value("${ldap.dateFormat}") + private String ldapDateFormat; + //SSL configuration - private static boolean sslEnabled; - private static String trustStoreLoc; - private static String trustStorePass; - private static String trustStoreTyp; + @Value("${ldap.sslEnabled}") + private boolean sslEnabled; + @Value("${ldap.trustStoreLocation}") + private String trustStoreLoc; + @Value("${ldap.trustStorePwd}") + private String trustStorePass; + @Value("${ldap.trustStoreType}") + private String trustStoreTyp; + // System user: used for getting all users and connection testing - private static String systemDN; - private static String systemPW; - // List of bases where to find users - private static List<String> ldapBases; - private static Integer connectionTimeout; + @Value("${ldap.ldapSystemDN}") + private String systemDN; + @Value("${ldap.ldapSystemPW}") + private String systemPW; + @Value("${ldap.connectionTimeout}") + private Integer connectionTimeout; /** * Create LDAP users on the fly when authenticated successfully */ + @Value("${ldap.ldapCreateUsersOnLogin}") private boolean createUsersOnLogin; - // Use a valid ldap password and save it as olat password to reduce dependency - // to LDAP server availability and allow WeDAV access - private static boolean cacheLDAPPwdAsOLATPwdOnLogin; - // When the system detects an LDAP user that does already exist in OLAT but is not marked - // as LDAP user, the OLAT user can be converted to an LDAP managed user. - // When enabling this feature you should make sure that you don't have a user 'administrator' - // in your ldapBases (not a problem but not recommended) - private static boolean convertExistingLocalUsersToLDAPUsers; - // Users that have been created vial LDAP sync but now can't be found on the LDAP anymore - // can be deleted automatically. If unsure, set to false and delete those users manually - // in the user management. - private static boolean deleteRemovedLDAPUsersOnSync; - // LDAP sync will not delete users if more than deleteRemovedLDAPUserPercentage are found to be deleted. - private static int deleteRemovedLDAPUsersPercentage; + /** + * When users log in via LDAP, the system can keep a copy of the password as encrypted + * hash in the database. This makes OLAT more independent from an offline LDAP server + * and users can use their LDAP password to use the WebDAV functionality. + * When setting to true (recommended), make sure you configured pwdchange=false in the + * org.olat.user.UserModule olat.propertes. + */ + @Value("${ldap.cacheLDAPPwdAsOLATPwdOnLogin}") + private boolean cacheLDAPPwdAsOLATPwdOnLogin; + /** + * When the system detects an LDAP user that does already exist in OLAT but is not marked + * as LDAP user, the OLAT user can be converted to an LDAP managed user. + * When enabling this feature you should make sure that you don't have a user 'administrator' + * in your ldapBases (not a problem but not recommended) + */ + @Value("${ldap.convertExistingLocalUsersToLDAPUsers}") + private boolean convertExistingLocalUsersToLDAPUsers; + // + /** + * Users that have been created via LDAP sync but now can't be found on the LDAP anymore + * can be deleted automatically. If unsure, set to false and delete those users manually + * in the user management. + */ + @Value("${ldap.deleteRemovedLDAPUsersOnSync}") + private boolean deleteRemovedLDAPUsersOnSync; + /** + * Sanity check when deleteRemovedLDAPUsersOnSync is set to 'true': if more than the defined + * percentages of user accounts are not found on the LDAP server and thus recognized as to be + * deleted, the LDAP sync will not happen and require a manual triggering of the delete job + * from the admin interface. This should prevent accidential deletion of OLAT user because of + * temporary LDAP problems or user relocation on the LDAP side. + * Value= 0 (never delete) to 100 (always delete). + */ + @Value("${ldap.deleteRemovedLDAPUsersPercentage}") + private int deleteRemovedLDAPUsersPercentage; // Propagate the password changes onto the LDAP server - private static boolean propagatePasswordChangedOnLdapServer; + @Value("${ldap.propagatePasswordChangedOnLdapServer}") + private boolean propagatePasswordChangedOnLdapServer; // Configuration for syncing user attributes - private static String ldapUserFilter; - private static String ldapUserCreatedTimestampAttribute; - private static String ldapUserLastModifiedTimestampAttribute; - private static String ldapUserPasswordAttribute; + + // Should users be created and synchronized automatically? If you set this // configuration to false, the users will be generated on-the-fly when they // log in - private static boolean ldapSyncOnStartup; - private static boolean ldapSyncCronSync; - private static String ldapSyncCronSyncExpression; + @Value("${ldap.ldapSyncOnStartup}") + private boolean ldapSyncOnStartup; + @Value("${ldap.ldapSyncCronSync}") + private boolean ldapSyncCronSync; + @Value("${ldap.ldapSyncCronSyncExpression}") + private String ldapSyncCronSyncExpression; // User LDAP attributes to be synced and a map with the mandatory attributes - private static Map<String, String> userAttrMap; - private static Map<String, String> reqAttr; - private static Set<String> syncOnlyOnCreateProperties; - private static String[] userAttr; - // Static user properties that should be added to user when syncing - private static Map<String, String> staticUserProperties; - private static OLog log = Tracing.createLoggerFor(LDAPLoginModule.class); + + + private static final OLog log = Tracing.createLoggerFor(LDAPLoginModule.class); - private final Scheduler scheduler; - private final BaseSecurity securityManager; - private final LDAPLoginManager ldapManager; - private final UserManager userManager; + @Autowired + private LDAPSyncConfiguration syncConfiguration; + @Autowired + private Scheduler scheduler; + @Autowired + private BaseSecurity securityManager; + @Autowired + private LDAPLoginManager ldapManager; + @Autowired + private UserManager userManager; - /** - * [used by spring] - */ - private LDAPLoginModule(LDAPLoginManager ldapManager, BaseSecurity securityManager, UserManager userManager, Scheduler scheduler) { - this.ldapManager = ldapManager; - this.securityManager = securityManager; - this.userManager = userManager; - this.scheduler = scheduler; + @Autowired + public LDAPLoginModule(CoordinatorManager coordinatorManager) { + super(coordinatorManager); } /** * @see org.olat.core.configuration.Initializable#init() */ + @Override public void init() { // Check if LDAP is enabled if (!isLDAPEnabled()) { @@ -156,50 +182,54 @@ public class LDAPLoginModule implements Initializable { if (!checkConfigParameterIsNotEmpty(ldapUrl)) return; if (!checkConfigParameterIsNotEmpty(systemDN)) return; if (!checkConfigParameterIsNotEmpty(systemPW)) return; - if (ldapBases == null || ldapBases.size() == 0) { - log - .error("Missing configuration 'ldapBases'. Add at least one LDAP Base to the this configuration in olatextconfig.xml first. Disabling LDAP"); + if (syncConfiguration.getLdapBases() == null || syncConfiguration.getLdapBases().isEmpty()) { + log.error("Missing configuration 'ldapBases'. Add at least one LDAP Base to the this configuration in olatextconfig.xml first. Disabling LDAP"); setEnableLDAPLogins(false); return; } - if (ldapUserFilter != null) { - if (!ldapUserFilter.startsWith("(") || !ldapUserFilter.endsWith(")")) { + if (syncConfiguration.getLdapUserFilter() != null) { + if (!syncConfiguration.getLdapUserFilter().startsWith("(") || !syncConfiguration.getLdapUserFilter().endsWith(")")) { log.error("Wrong configuration 'ldapUserFilter'. Set filter to emtpy value or enclose filter in brackets like '(objectClass=person)'. Disabling LDAP"); setEnableLDAPLogins(false); return; } } - if (!checkConfigParameterIsNotEmpty(ldapUserCreatedTimestampAttribute)) return; - if (!checkConfigParameterIsNotEmpty(ldapUserLastModifiedTimestampAttribute)) return; - if (userAttrMap == null || userAttrMap.size() == 0) { - log - .error("Missing configuration 'userAttrMap'. Add at least the email propery to the this configuration in olatextconfig.xml first. Disabling LDAP"); + + if (!checkConfigParameterIsNotEmpty(syncConfiguration.getLdapUserCreatedTimestampAttribute())) { + return; + } + if (!checkConfigParameterIsNotEmpty(syncConfiguration.getLdapUserLastModifiedTimestampAttribute())) { + return; + } + if (syncConfiguration.getUserAttributeMap() == null || syncConfiguration.getUserAttributeMap().isEmpty()) { + log.error("Missing configuration 'userAttrMap'. Add at least the email propery to the this configuration in olatextconfig.xml first. Disabling LDAP"); setEnableLDAPLogins(false); return; } - if (reqAttr == null || reqAttr.size() == 0) { - log - .error("Missing configuration 'reqAttr'. Add at least the email propery to the this configuration in olatextconfig.xml first. Disabling LDAP"); + if (syncConfiguration.getRequestAttributes() == null || syncConfiguration.getRequestAttributes().isEmpty()) { + log.error("Missing configuration 'reqAttr'. Add at least the email propery to the this configuration in olatextconfig.xml first. Disabling LDAP"); setEnableLDAPLogins(false); return; } // check if OLAT user properties is defined in olat_userconfig.xml, if not disable the LDAP module - if(!checkIfOlatPropertiesExists(userAttrMap)){ + if(!syncConfiguration.checkIfOlatPropertiesExists(syncConfiguration.getUserAttributeMap())){ log.error("Invalid LDAP OLAT properties mapping configuration (userAttrMap). Disabling LDAP"); setEnableLDAPLogins(false); return; } - if(!checkIfOlatPropertiesExists(reqAttr)){ + if(!syncConfiguration.checkIfOlatPropertiesExists(syncConfiguration.getRequestAttributes())){ log.error("Invalid LDAP OLAT properties mapping configuration (reqAttr). Disabling LDAP"); setEnableLDAPLogins(false); return; } - if(syncOnlyOnCreateProperties != null && !checkIfStaticOlatPropertiesExists(syncOnlyOnCreateProperties)){ + if(syncConfiguration.getSyncOnlyOnCreateProperties() != null + && !syncConfiguration.checkIfStaticOlatPropertiesExists(syncConfiguration.getSyncOnlyOnCreateProperties())){ log.error("Invalid LDAP OLAT syncOnlyOnCreateProperties configuration. Disabling LDAP"); setEnableLDAPLogins(false); return; } - if( staticUserProperties != null && !checkIfStaticOlatPropertiesExists(staticUserProperties.keySet())){ + if(syncConfiguration.getStaticUserProperties() != null + && !syncConfiguration.checkIfStaticOlatPropertiesExists(syncConfiguration.getStaticUserProperties().keySet())){ log.error("Invalid static OLAT properties configuration (staticUserProperties). Disabling LDAP"); setEnableLDAPLogins(false); return; @@ -207,10 +237,11 @@ public class LDAPLoginModule implements Initializable { // check SSL certifications, throws Startup Exception if certificate is not found if(isSslEnabled()){ - if (!checkServerCertValidity(0)) - throw new StartupException("LDAP enabled but no valid server certificate found. Please fix!"); - if (!checkServerCertValidity(30)) + if (!checkServerCertValidity(0)) { + log.error("LDAP enabled but no valid server certificate found. Please fix!"); + } else if (!checkServerCertValidity(30)) { log.warn("Server Certificate will expire in less than 30 days."); + } } // Check ldap connection @@ -240,6 +271,11 @@ public class LDAPLoginModule implements Initializable { log.info("LDAP login is enabled"); } + @Override + protected void initFromChangedProperties() { + // + } + /** * Internal helper to sync users right away * @param ldapManager @@ -276,118 +312,7 @@ public class LDAPLoginModule implements Initializable { log.error("Error while scheduling LDAP cron sync job. Disabling LDAP cron syncing", e); } } - - /** - * Maps OLAT Property to the LDAP Attributes - * - * Configuration: LDAP Attributes Map = olatextconfig.xml (property=userAttrs) - * - * @param olatProperty OLAT PropertyattrID - * @return LDAP Attribute - */ - public static String mapOlatPropertyToLdapAttribute(String olatProperty) { - Map<String, String> userAttrMapper = getReqAttrs(); - if (userAttrMapper.containsValue(olatProperty)) { - Iterator<String> itr = userAttrMapper.keySet().iterator(); - while (itr.hasNext()) { - String key = itr.next(); - if (userAttrMapper.get(key).compareTo(olatProperty) == 0) return key; - } - } - return null; - } - - /** - * Checks if Collection of naming Attributes contain defined required properties for OLAT - * - * * Configuration: LDAP Required Map = olatextconfig.xml (property=reqAttrs) - * - * @param attributes Collection of LDAP Naming Attribute - * @return null If all required Attributes are found, otherwise String[] of missing Attributes - * - */ - public static String[] checkReqAttr(Attributes attrs) { - Map<String, String> reqAttrMap = getReqAttrs(); - String[] missingAttr = new String[reqAttrMap.size()]; - int y = 0; - for (String attKey : reqAttrMap.keySet()) { - attKey = attKey.trim(); - if (attrs.get(attKey) == null) { - missingAttr[y++] = attKey; - } - } - if (y == 0) return null; - else return missingAttr; - } - - /** - * Checks if defined OLAT Properties in olatextconfig.xml exist in OLAT. - * - * Configuration: LDAP Attributes Map = olatextconfig.xml (property=reqAttrs, property=userAttributeMapper) - * - * @param attrs Map of OLAT Properties from of the LDAP configuration - * @return true All exist OK, false Error - * - */ - private boolean checkIfOlatPropertiesExists(Map<String, String> attrs) { - List<UserPropertyHandler> upHandler = userManager.getAllUserPropertyHandlers(); - for (String ldapAttribute : attrs.keySet()) { - boolean propertyExists = false; - String olatProperty = attrs.get(ldapAttribute); - if (olatProperty.equals(LDAPConstants.LDAP_USER_IDENTIFYER)) { - // LDAP user identifyer is not a user propery, it's the username - continue; - } - for (UserPropertyHandler userPropItr : upHandler) { - if (olatProperty.equals(userPropItr.getName())) { - // ok, this property exist, continue with next one - propertyExists = true; - break; - } - } - if ( ! propertyExists ) { - log - .error("Error in checkIfOlatPropertiesExists(): configured LDAP attribute::" - + ldapAttribute - + " configured to map to OLAT user property::" - + olatProperty - + " but no such user property configured in olat_userconfig.xml"); - return false; - } - } - return true; - } - /** - * Checks if defined Static OLAT Property in olatextconfig.xml exist in OLAT. - * - * Configuration: olatextconfig.xml (property=staticUserProperties) - * - * @param olatProperties Set of OLAT Properties from of the LDAP configuration - * @return true All exist OK, false Error - * - */ - private boolean checkIfStaticOlatPropertiesExists(Set<String> olatProperties) { - List<UserPropertyHandler> upHandler = userManager.getAllUserPropertyHandlers(); - for (String olatProperty : olatProperties) { - boolean propertyExists = false; - for (UserPropertyHandler userPropItr : upHandler) { - if (olatProperty.equals(userPropItr.getName())) { - // ok, this property exist, continue with next one - propertyExists = true; - break; - } - } - if ( ! propertyExists ) { - log - .error("Error in checkIfStaticOlatPropertiesExists(): configured static OLAT user property::" - + olatProperty - + " is not configured in olat_userconfig.xml"); - return false; - } - } - return true; - } /** * Checks if SSL certification is know and accepted by Java JRE. @@ -399,7 +324,7 @@ public class LDAPLoginModule implements Initializable { * @throws Exception * */ - private static boolean checkServerCertValidity(int daysFromNow) { + private boolean checkServerCertValidity(int daysFromNow) { KeyStore keyStore; try { keyStore = KeyStore.getInstance(getTrustStoreType()); @@ -418,7 +343,7 @@ public class LDAPLoginModule implements Initializable { return false; } - private static boolean isCertificateValid(X509Certificate x509Cert, int daysFromNow) { + private boolean isCertificateValid(X509Certificate x509Cert, int daysFromNow) { try { x509Cert.checkValidity(); if (daysFromNow > 0) { @@ -481,118 +406,51 @@ public class LDAPLoginModule implements Initializable { ldapSyncOnStartup = ldapStartSyncs; } - public void setLdapUserFilter(String filter) { - if (StringHelper.containsNonWhitespace(filter)) { - ldapUserFilter = filter.trim(); - } else { - // set explicitly to null for no filter - ldapUserFilter = null; - } + public String getLdapSystemDN() { + return systemDN; } public void setLdapSystemDN(String ldapSystemDN) { systemDN = ldapSystemDN.trim(); } + + public String getLdapSystemPW() { + return systemPW; + } public void setLdapSystemPW(String ldapSystemPW) { systemPW = ldapSystemPW.trim(); } + + public String getLdapUrl() { + return ldapUrl; + } public void setLdapUrl(String ldapUrlConfig) { ldapUrl = ldapUrlConfig.trim(); } - - public void setLdapBases(List<String> ldapBasesConfig) { - // fxdiff: FXOLAT-141 allow setting in one line - ArrayList<String> listToUse = new ArrayList<String>(); - if (ldapBasesConfig != null) { - for (String baseEntry : ldapBasesConfig) { - if (StringHelper.containsNonWhitespace(baseEntry) && baseEntry.contains("!#")) { - String[] oneLineList = baseEntry.split("!#"); - List<String> oneLineListArr = Arrays.asList(oneLineList); - for (String oneLineEntry : oneLineListArr) { - if (StringHelper.containsNonWhitespace(oneLineEntry)) { - listToUse.add(oneLineEntry.trim()); - } - } - } else { - listToUse.add(baseEntry.trim()); - } - } - } - ldapBases = listToUse; - } - public void setLdapConnectionTimeout(Integer timeout) { - connectionTimeout = timeout; - } - - public void setUserAttributeMapper(Map<String, String> userAttributeMapper) { - // trim map - userAttrMap = new HashMap<String, String>(); - for (Entry<String, String> entry : userAttributeMapper.entrySet()) { - String ldapAttrib = entry.getKey().trim(); - String olatProp = entry.getValue().trim(); - if (StringHelper.containsNonWhitespace(ldapAttrib) && StringHelper.containsNonWhitespace(olatProp)){ - userAttrMap.put(ldapAttrib, olatProp); - } - } - // optimizes for later usage - userAttr = userAttrMap.keySet().toArray(new String[userAttrMap.size()]); - } - public void setReqAttrs(Map<String, String> reqAttrs) { - // trim map - reqAttr = new HashMap<String, String>(); - for (Entry<String, String> entry : reqAttrs.entrySet()) { - reqAttr.put(entry.getKey().trim(), entry.getValue().trim()); - } - } - public void setSyncOnlyOnCreateProperties(Set<String> syncOnlyOnCreatePropertiesConfig) { - // trim map - syncOnlyOnCreateProperties = new HashSet<String>(); - for (String value : syncOnlyOnCreatePropertiesConfig) { - if (StringHelper.containsNonWhitespace(value)){ - syncOnlyOnCreateProperties.add(value.trim()); - } - } - } - - public void setStaticUserProperties(Map<String, String> staticUserPropertiesMap) { - // trim map - staticUserProperties = new HashMap<String, String>(); - for (Entry<String, String> entry : staticUserPropertiesMap.entrySet()) { - String olatPropKey = entry.getKey().trim(); - String staticValue = entry.getValue().trim(); - if (StringHelper.containsNonWhitespace(olatPropKey) && StringHelper.containsNonWhitespace(staticValue)){ - staticUserProperties.put(olatPropKey, staticValue); - } - } - } - - public void setLdapUserLastModifiedTimestampAttribute(String ldapUserLastModifiedTimestampAttribute) { - LDAPLoginModule.ldapUserLastModifiedTimestampAttribute = ldapUserLastModifiedTimestampAttribute.trim(); - } - - public void setLdapUserCreatedTimestampAttribute(String ldapUserCreatedTimestampAttribute) { - LDAPLoginModule.ldapUserCreatedTimestampAttribute = ldapUserCreatedTimestampAttribute.trim(); + + public Integer getLdapConnectionTimeout() { + return connectionTimeout; } - public void setLdapUserPasswordAttribute(String userPasswordAttribute) { - LDAPLoginModule.ldapUserPasswordAttribute = userPasswordAttribute; + public void setLdapConnectionTimeout(Integer timeout) { + connectionTimeout = timeout; } public void setLdapSyncCronSync(boolean ldapSyncCronSync) { - LDAPLoginModule.ldapSyncCronSync = ldapSyncCronSync; + this.ldapSyncCronSync = ldapSyncCronSync; } public void setLdapSyncCronSyncExpression(String ldapSyncCronSyncExpression) { - LDAPLoginModule.ldapSyncCronSyncExpression = ldapSyncCronSyncExpression.trim(); + this.ldapSyncCronSyncExpression = ldapSyncCronSyncExpression.trim(); } public void setCacheLDAPPwdAsOLATPwdOnLogin(boolean cacheLDAPPwdAsOLATPwdOnLogin) { - LDAPLoginModule.cacheLDAPPwdAsOLATPwdOnLogin = cacheLDAPPwdAsOLATPwdOnLogin; + this.cacheLDAPPwdAsOLATPwdOnLogin = cacheLDAPPwdAsOLATPwdOnLogin; } public void setCreateUsersOnLogin(boolean createUsersOnLogin) { @@ -600,127 +458,61 @@ public class LDAPLoginModule implements Initializable { } public void setConvertExistingLocalUsersToLDAPUsers(boolean convertExistingLocalUsersToLDAPUsers) { - LDAPLoginModule.convertExistingLocalUsersToLDAPUsers = convertExistingLocalUsersToLDAPUsers; + this.convertExistingLocalUsersToLDAPUsers = convertExistingLocalUsersToLDAPUsers; } public void setDeleteRemovedLDAPUsersOnSync(boolean deleteRemovedLDAPUsersOnSync) { - LDAPLoginModule.deleteRemovedLDAPUsersOnSync = deleteRemovedLDAPUsersOnSync; + this.deleteRemovedLDAPUsersOnSync = deleteRemovedLDAPUsersOnSync; } public void setDeleteRemovedLDAPUsersPercentage(int deleteRemovedLDAPUsersPercentage){ - LDAPLoginModule.deleteRemovedLDAPUsersPercentage = deleteRemovedLDAPUsersPercentage; + this.deleteRemovedLDAPUsersPercentage = deleteRemovedLDAPUsersPercentage; } public void setPropagatePasswordChangedOnLdapServer(boolean propagatePasswordChangedOnServer) { - LDAPLoginModule.propagatePasswordChangedOnLdapServer = propagatePasswordChangedOnServer; - } - - /* - * Getters - */ - public static String getLdapSystemDN() { - return systemDN; - } - - public static String getLdapSystemPW() { - return systemPW; - } - - public static String getLdapUrl() { - return ldapUrl; - } - - public static List<String> getLdapBases() { - return ldapBases; - } - - public static Integer getLdapConnectionTimeout() { - return connectionTimeout; - } - - /** - * @return A filter expression enclosed in () brackets to filter for valid users or NULL for no filtering - */ - public static String getLdapUserFilter() { - return ldapUserFilter; - } - - public static String getLdapUserLastModifiedTimestampAttribute() { - return ldapUserLastModifiedTimestampAttribute; - } - - public static String getLdapUserCreatedTimestampAttribute() { - return ldapUserCreatedTimestampAttribute; - } - - public static String getLdapUserPasswordAttribute() { - return ldapUserPasswordAttribute; - } - - /** - * @return a map of user properties to set for each LDAP user or NULL if no - * such properties have to be set - */ - public static Map<String, String> getStaticUserProperties() { - return staticUserProperties; - } - - public static Map<String, String> getUserAttributeMapper() { - return userAttrMap; - } - - public static String[] getUserAttrs() { - return userAttr; - } - - public static Map<String, String> getReqAttrs() { - return reqAttr; - } - - public static Set<String> getSyncOnlyOnCreateProperties() { - return syncOnlyOnCreateProperties; + this.propagatePasswordChangedOnLdapServer = propagatePasswordChangedOnServer; } - public static boolean isLDAPEnabled() { + public boolean isLDAPEnabled() { return ldapEnabled; } - public static boolean isSslEnabled() { + public boolean isSslEnabled() { return sslEnabled; } - public static boolean isActiveDirectory() { + public boolean isActiveDirectory() { return activeDirectory; } - public static String getLdapDateFormat() { + public String getLdapDateFormat() { if(StringHelper.containsNonWhitespace(ldapDateFormat)) { return ldapDateFormat; } return "yyyyMMddHHmmss'Z'";//default } - public static String getTrustStoreLocation(){ + public String getTrustStoreLocation(){ return trustStoreLoc; } - public static String getTrustStorePwd(){ + public String getTrustStorePwd(){ return trustStorePass; } - public static String getTrustStoreType(){ + public String getTrustStoreType(){ return trustStoreTyp; } - public static boolean isLdapSyncOnStartup() { + public boolean isLdapSyncOnStartup() { return ldapSyncOnStartup; } - public static boolean isLdapSyncCronSync() { + public boolean isLdapSyncCronSync() { return ldapSyncCronSync; } - public static String getLdapSyncCronSyncExpression() { + public String getLdapSyncCronSyncExpression() { return ldapSyncCronSyncExpression; } @@ -728,23 +520,23 @@ public class LDAPLoginModule implements Initializable { return createUsersOnLogin; } - public static boolean isCacheLDAPPwdAsOLATPwdOnLogin() { + public boolean isCacheLDAPPwdAsOLATPwdOnLogin() { return cacheLDAPPwdAsOLATPwdOnLogin; } - public static boolean isConvertExistingLocalUsersToLDAPUsers() { + public boolean isConvertExistingLocalUsersToLDAPUsers() { return convertExistingLocalUsersToLDAPUsers; } - public static boolean isDeleteRemovedLDAPUsersOnSync() { + public boolean isDeleteRemovedLDAPUsersOnSync() { return deleteRemovedLDAPUsersOnSync; } - public static int getDeleteRemovedLDAPUsersPercentage(){ + public int getDeleteRemovedLDAPUsersPercentage(){ return deleteRemovedLDAPUsersPercentage; } - public static boolean isPropagatePasswordChangedOnLdapServer(){ + public boolean isPropagatePasswordChangedOnLdapServer(){ return propagatePasswordChangedOnLdapServer; } } diff --git a/src/main/java/org/olat/ldap/LDAPSyncConfiguration.java b/src/main/java/org/olat/ldap/LDAPSyncConfiguration.java new file mode 100644 index 00000000000..2543aae64c3 --- /dev/null +++ b/src/main/java/org/olat/ldap/LDAPSyncConfiguration.java @@ -0,0 +1,524 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.ldap; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import javax.naming.directory.Attributes; + +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.util.StringHelper; +import org.olat.user.UserManager; +import org.olat.user.propertyhandlers.UserPropertyHandler; + +/** + * + * Hold all configurations to use a LDAP server to sync + * users with OpenOLAT.<br /> + * + * Initial date: 24.11.2014<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class LDAPSyncConfiguration { + + private static final OLog log = Tracing.createLoggerFor(LDAPSyncConfiguration.class); + + private String ldapUserFilter; + private String ldapGroupFilter; + + private List<String> ldapBases; + private List<String> ldapGroupBases; + + private String ldapUserCreatedTimestampAttribute; + private String ldapUserLastModifiedTimestampAttribute; + private String ldapUserPasswordAttribute; + + private String[] userAttributes; + + private String coachRoleAttribute; + private String coachRoleValue; + + private String groupAttribute; + private String groupAttributeSeparator; + + private Map<String, String> requestAttributes; + private Map<String, String> userAttributeMap; + private Set<String> syncOnlyOnCreateProperties; + + private List<String> authorsGroupBase; + private String authorRoleAttribute; + private String authorRoleValue; + + private List<String> userManagersGroupBase; + private String userManagerRoleAttribute; + private String userManagerRoleValue; + + private List<String> groupManagersGroupBase; + private String groupManagerRoleAttribute; + private String groupManagerRoleValue; + + private List<String> qpoolManagersGroupBase; + private String qpoolManagerRoleAttribute; + private String qpoolManagerRoleValue; + + private List<String> learningResourceManagersGroupBase; + private String learningResourceManagerRoleAttribute; + private String learningResourceManagerRoleValue; + + /** + * Static user properties that should be added to user when syncing + */ + private Map<String, String> staticUserProperties; + + private UserManager userManager; + + /** + * [used by Spring] + * @param userManager + */ + public void setUserManager(UserManager userManager) { + this.userManager = userManager; + } + + public String getLdapUserCreatedTimestampAttribute() { + return ldapUserCreatedTimestampAttribute; + } + + public List<String> getLdapBases() { + return ldapBases; + } + + public void setLdapBases(List<String> bases) { + ldapBases = toList(bases); + } + + public List<String> getLdapGroupBases() { + return ldapGroupBases; + } + + public void setLdapGroupBases(List<String> bases) { + ldapGroupBases = toList(bases); + } + + private List<String> toList(List<String> list) { + List<String> listToUse = new ArrayList<String>(); + if (list != null) { + for (String entry : list) { + if (StringHelper.containsNonWhitespace(entry) && entry.contains("!#")) { + String[] oneLineList = entry.split("!#"); + for (String oneLineEntry : oneLineList) { + if (StringHelper.containsNonWhitespace(oneLineEntry)) { + listToUse.add(oneLineEntry.trim()); + } + } + } else if (StringHelper.containsNonWhitespace(entry)) { + listToUse.add(entry.trim()); + } + } + } + return listToUse; + } + + /** + * @return A filter expression enclosed in () brackets to filter for valid users or NULL for no filtering + */ + public String getLdapUserFilter() { + return ldapUserFilter; + } + + public void setLdapUserFilter(String filter) { + if (StringHelper.containsNonWhitespace(filter)) { + ldapUserFilter = filter.trim(); + } else { + // set explicitly to null for no filter + ldapUserFilter = null; + } + } + + public String getLdapGroupFilter() { + return ldapGroupFilter; + } + + public void setLdapGroupFilter(String filter) { + if (StringHelper.containsNonWhitespace(filter)) { + ldapGroupFilter = filter.trim(); + } else { + ldapGroupFilter = null; + } + } + + public boolean syncGroupWithLDAPGroup() { + return ldapGroupBases != null && ldapGroupBases.size() > 0; + } + + public boolean syncGroupWithAttribute() { + return StringHelper.containsNonWhitespace(groupAttribute); + } + + public String getCoachRoleAttribute() { + return coachRoleAttribute; + } + + public void setCoachRoleAttribute(String attribute) { + this.coachRoleAttribute = attribute; + } + + public String getCoachRoleValue() { + return coachRoleValue; + } + + public void setCoachRoleValue(String coachRoleValue) { + this.coachRoleValue = coachRoleValue; + } + + public String getGroupAttribute() { + return groupAttribute; + } + + public void setGroupAttribute(String attribute) { + this.groupAttribute = attribute; + } + + public String getGroupAttributeSeparator() { + return groupAttributeSeparator; + } + + public void setGroupAttributeSeparator(String groupAttributeSeparator) { + this.groupAttributeSeparator = groupAttributeSeparator; + } + + public List<String> getAuthorsGroupBase() { + return authorsGroupBase; + } + + public void setAuthorsGroupBase(List<String> bases) { + authorsGroupBase = toList(bases); + } + + public String getAuthorRoleAttribute() { + return authorRoleAttribute; + } + + public void setAuthorRoleAttribute(String attribute) { + this.authorRoleAttribute = attribute; + } + + public String getAuthorRoleValue() { + return authorRoleValue; + } + + public void setAuthorRoleValue(String value) { + this.authorRoleValue = value; + } + + public List<String> getUserManagersGroupBase() { + return userManagersGroupBase; + } + + public void setUserManagersGroupBase(List<String> bases) { + userManagersGroupBase = toList(bases); + } + + public String getUserManagerRoleAttribute() { + return userManagerRoleAttribute; + } + + public void setUserManagerRoleAttribute(String attribute) { + userManagerRoleAttribute = attribute; + } + + public String getUserManagerRoleValue() { + return userManagerRoleValue; + } + + public void setUserManagerRoleValue(String value) { + userManagerRoleValue = value; + } + + public List<String> getGroupManagersGroupBase() { + return groupManagersGroupBase; + } + + public void setGroupManagersGroupBase(List<String> bases) { + this.groupManagersGroupBase = toList(bases); + } + + public String getGroupManagerRoleAttribute() { + return groupManagerRoleAttribute; + } + + public void setGroupManagerRoleAttribute(String attribute) { + this.groupManagerRoleAttribute = attribute; + } + + public String getGroupManagerRoleValue() { + return groupManagerRoleValue; + } + + public void setGroupManagerRoleValue(String value) { + this.groupManagerRoleValue = value; + } + + public List<String> getQpoolManagersGroupBase() { + return qpoolManagersGroupBase; + } + + public void setQpoolManagersGroupBase(List<String> bases) { + this.qpoolManagersGroupBase = toList(bases); + } + + public String getQpoolManagerRoleAttribute() { + return qpoolManagerRoleAttribute; + } + + public void setQpoolManagerRoleAttribute(String attribute) { + this.qpoolManagerRoleAttribute = attribute; + } + + public String getQpoolManagerRoleValue() { + return qpoolManagerRoleValue; + } + + public void setQpoolManagerRoleValue(String value) { + this.qpoolManagerRoleValue = value; + } + + public List<String> getLearningResourceManagersGroupBase() { + return learningResourceManagersGroupBase; + } + + public void setLearningResourceManagersGroupBase(List<String> bases) { + this.learningResourceManagersGroupBase = toList(bases); + } + + public String getLearningResourceManagerRoleAttribute() { + return learningResourceManagerRoleAttribute; + } + + public void setLearningResourceManagerRoleAttribute(String attribute) { + this.learningResourceManagerRoleAttribute = attribute; + } + + public String getLearningResourceManagerRoleValue() { + return learningResourceManagerRoleValue; + } + + public void setLearningResourceManagerRoleValue(String attribute) { + this.learningResourceManagerRoleValue = attribute; + } + + public void setLdapUserCreatedTimestampAttribute(String attribute) { + this.ldapUserCreatedTimestampAttribute = attribute; + } + + public String getLdapUserLastModifiedTimestampAttribute() { + return ldapUserLastModifiedTimestampAttribute; + } + + public void setLdapUserLastModifiedTimestampAttribute(String attribute) { + this.ldapUserLastModifiedTimestampAttribute = attribute; + } + + public String getLdapUserPasswordAttribute() { + return ldapUserPasswordAttribute; + } + + public void setLdapUserPasswordAttribute(String attribute) { + this.ldapUserPasswordAttribute = attribute; + } + + public Map<String, String> getRequestAttributes() { + return requestAttributes; + } + + public void setRequestAttributes(Map<String, String> mapping) { + requestAttributes = new HashMap<String, String>(); + for (Map.Entry<String, String> entry : mapping.entrySet()) { + requestAttributes.put(entry.getKey().trim(), entry.getValue().trim()); + } + } + + public String[] getUserAttributes() { + return userAttributes; + } + + public Map<String, String> getUserAttributeMap() { + return userAttributeMap; + } + + public void setUserAttributeMap(Map<String, String> mapping) { + userAttributeMap = new HashMap<String, String>(); + for (Entry<String, String> entry : mapping.entrySet()) { + String ldapAttrib = entry.getKey(); + String olatProp = entry.getValue(); + if (StringHelper.containsNonWhitespace(ldapAttrib) && StringHelper.containsNonWhitespace(olatProp)){ + userAttributeMap.put(ldapAttrib.trim(), olatProp.trim()); + } + } + // optimizes for later usage + userAttributes = userAttributeMap.keySet().toArray(new String[userAttributeMap.size()]); + } + + public Map<String, String> getStaticUserProperties() { + return staticUserProperties; + } + + public void setStaticUserProperties(Map<String, String> mapping) { + staticUserProperties = new HashMap<String, String>(); + for (Map.Entry<String, String> entry : mapping.entrySet()) { + String olatPropKey = entry.getKey(); + String staticValue = entry.getValue(); + if (StringHelper.containsNonWhitespace(olatPropKey) && StringHelper.containsNonWhitespace(staticValue)){ + staticUserProperties.put(olatPropKey.trim(), staticValue.trim()); + } + } + } + + public Set<String> getSyncOnlyOnCreateProperties() { + return syncOnlyOnCreateProperties; + } + + public void setSyncOnlyOnCreateProperties(Set<String> properties) { + syncOnlyOnCreateProperties = new HashSet<String>(); + for (String property : properties) { + if (StringHelper.containsNonWhitespace(property)){ + syncOnlyOnCreateProperties.add(property.trim()); + } + } + } + + /** + * Checks if defined OLAT Properties in olatextconfig.xml exist in OLAT. + * + * Configuration: LDAP Attributes Map = olatextconfig.xml (property=reqAttrs, property=userAttributeMapper) + * + * @param attrs Map of OLAT Properties from of the LDAP configuration + * @return true All exist OK, false Error + * + */ + protected boolean checkIfOlatPropertiesExists(Map<String, String> attrs) { + List<UserPropertyHandler> upHandler = userManager.getAllUserPropertyHandlers(); + for (String ldapAttribute : attrs.keySet()) { + boolean propertyExists = false; + String olatProperty = attrs.get(ldapAttribute); + if (olatProperty.equals(LDAPConstants.LDAP_USER_IDENTIFYER)) { + // LDAP user identifyer is not a user propery, it's the username + continue; + } + for (UserPropertyHandler userPropItr : upHandler) { + if (olatProperty.equals(userPropItr.getName())) { + // ok, this property exist, continue with next one + propertyExists = true; + break; + } + } + if ( ! propertyExists ) { + log.error("Error in checkIfOlatPropertiesExists(): configured LDAP attribute::" + + ldapAttribute + + " configured to map to OLAT user property::" + + olatProperty + + " but no such user property configured in olat_userconfig.xml"); + return false; + } + } + return true; + } + + /** + * Checks if defined Static OLAT Property in olatextconfig.xml exist in OLAT. + * + * Configuration: olatextconfig.xml (property=staticUserProperties) + * + * @param olatProperties Set of OLAT Properties from of the LDAP configuration + * @return true All exist OK, false Error + * + */ + protected boolean checkIfStaticOlatPropertiesExists(Set<String> olatProperties) { + List<UserPropertyHandler> upHandler = userManager.getAllUserPropertyHandlers(); + for (String olatProperty : olatProperties) { + boolean propertyExists = false; + for (UserPropertyHandler userPropItr : upHandler) { + if (olatProperty.equals(userPropItr.getName())) { + // ok, this property exist, continue with next one + propertyExists = true; + break; + } + } + if ( ! propertyExists ) { + log.error("Error in checkIfStaticOlatPropertiesExists(): configured static OLAT user property::" + + olatProperty + + " is not configured in olat_userconfig.xml"); + return false; + } + } + return true; + } + + /** + * Checks if Collection of naming Attributes contain defined required properties for OLAT + * + * * Configuration: LDAP Required Map = olatextconfig.xml (property=reqAttrs) + * + * @param attributes Collection of LDAP Naming Attribute + * @return null If all required Attributes are found, otherwise String[] of missing Attributes + * + */ + public String[] checkRequestAttributes(Attributes attrs) { + Map<String, String> reqAttrMap = getRequestAttributes(); + String[] missingAttr = new String[reqAttrMap.size()]; + int y = 0; + for (String attKey : reqAttrMap.keySet()) { + attKey = attKey.trim(); + if (attrs.get(attKey) == null) { + missingAttr[y++] = attKey; + } + } + return (y == 0) ? null : missingAttr; + } + + /** + * Maps OLAT Property to the LDAP Attributes + * + * Configuration: LDAP Attributes Map = ldapContext.xml (property=userAttrs) + * + * @param olatProperty OLAT Property attribute ID + * @return LDAP Attribute + */ + public String getOlatPropertyToLdapAttribute(String olatProperty) { + Map<String, String> userAttrMapper = getRequestAttributes(); + if (userAttrMapper.containsValue(olatProperty)) { + Iterator<String> itr = userAttrMapper.keySet().iterator(); + while (itr.hasNext()) { + String key = itr.next(); + if (userAttrMapper.get(key).compareTo(olatProperty) == 0) return key; + } + } + return null; + } +} diff --git a/src/main/java/org/olat/ldap/_spring/ldapContext.xml b/src/main/java/org/olat/ldap/_spring/ldapContext.xml index eec448cd314..6822bc11503 100644 --- a/src/main/java/org/olat/ldap/_spring/ldapContext.xml +++ b/src/main/java/org/olat/ldap/_spring/ldapContext.xml @@ -1,90 +1,87 @@ <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" + xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans.xsd"> + http://www.springframework.org/schema/beans/spring-beans.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context.xsd"> - <!-- - ***************************************** - *** Define the LDAP Module *** - ***************************************** - --> - <bean id="org.olat.ldap.LDAPLoginModule" class="org.olat.ldap.LDAPLoginModule" init-method="init"> - <constructor-arg index="0" ref="org.olat.ldap.LDAPLoginManager"/> - <constructor-arg index="1" ref="baseSecurityManager"/> - <constructor-arg index="2" ref="userManager"/> - <constructor-arg index="3" ref="schedulerFactoryBean"/> + <context:component-scan base-package="org.olat.ldap" /> + + <!-- Configuration for syncing user attributes during login or cron and batch sync --> + <bean id="ldapSyncConfiguration" class="org.olat.ldap.LDAPSyncConfiguration"> + <property name="userManager" ref="userManager" /> + + <property name="ldapUserCreatedTimestampAttribute" value="${ldap.ldapUserCreatedTimestampAttribute}"/> + <property name="ldapUserLastModifiedTimestampAttribute" value="${ldap.ldapUserLastModifiedTimestampAttribute}"/> + <property name="ldapUserPasswordAttribute" value="${ldap.ldapUserPassordAttribute}"/> - <!-- Basic LDAP connection configuration --> - <property name="enableLDAPLogins" value="${ldap.enable}" /> - <!-- Active directory has some special format, especially for password changes --> - <property name="activeDirectory" value="${ldap.activeDirectory}" /> - <property name="ldapDateFormat" value="${ldap.dateFormat}" /> - <property name="ldapUrl" value="${ldap.ldapUrl}" /> - <property name="ldapConnectionTimeout" value="${ldap.connectionTimeout}" /> - <!-- System user: used for getting all users and connection testing --> - <property name="ldapSystemDN" value="${ldap.ldapSystemDN}" /> - <property name="ldapSystemPW" value="${ldap.ldapSystemPW}" /> - <!-- List of bases where to find users. You can add multiple bases --> - <property name="ldapBases" > + <property name="ldapUserFilter" value="${ldap.ldapUserFilter}"/> + <property name="ldapGroupFilter" value="${ldap.ldapGroupFilter}"/> + + <property name="ldapBases" > <list> <value>${ldap.ldapBases}</value> </list> </property> - <!-- SSL Options - Add server certificate to JDK Keystore whit keytool --> - <property name="sslEnabled" value="${ldap.sslEnabled}"/> - <property name="trustStoreLocation" value="${ldap.trustStoreLocation}"/> - <property name="trustStorePwd" value="${ldap.trustStorePwd}"/> - <property name="trustStoreType" value="${ldap.trustStoreType}"/> - <property name="createUsersOnLogin" value="${ldap.ldapCreateUsersOnLogin}" /> - <!-- - When users log in via LDAP, the system can keep a copy of the password as encrypted - hash in the database. This makes OLAT more independent from an offline LDAP server - and users can use their LDAP password to use the WebDAV functionality. - When setting to true (recommended), make sure you configured pwdchange=false in the - org.olat.user.UserModule olat.propertes. - --> - <property name="cacheLDAPPwdAsOLATPwdOnLogin" value="${ldap.cacheLDAPPwdAsOLATPwdOnLogin}" /> - <!-- - Change on password are propagate to the LDAP server - --> - <property name="propagatePasswordChangedOnLdapServer" value="${ldap.propagatePasswordChangedOnLdapServer}" /> - <!-- - When the system detects an LDAP user that does already exist in OLAT but is not marked - as LDAP user, the OLAT user can be converted to an LDAP managed user. - When enabling this feature you should make sure that you don't have a user 'administrator' - in your ldapBases (not a problem but not recommended) - --> - <property name="convertExistingLocalUsersToLDAPUsers" value="${ldap.convertExistingLocalUsersToLDAPUsers}" /> - <!-- - Users that have been created vial LDAP sync but now can't be found on the LDAP anymore - can be deleted automatically. If unsure, set to false and delete those users manually - in the user management. - --> - <property name="deleteRemovedLDAPUsersOnSync" value="${ldap.deleteRemovedLDAPUsersOnSync}" /> - <!-- - Sanity check when deleteRemovedLDAPUsersOnSync is set to 'true': if more than the defined - percentages of user accounts are not found on the LDAP server and thus recognized as to be - deleted, the LDAP sync will not happen and require a manual triggering of the delete job - from the admin interface. This should prevent accidential deletion of OLAT user because of - temporary LDAP problems or user relocation on the LDAP side. - Value= 0 (never delete) to 100 (always delete). - --> - <property name="deleteRemovedLDAPUsersPercentage" value="${ldap.deleteRemovedLDAPUsersPercentage}" /> - <!-- - Should users be created and synchronized automatically? If you set this configuration - to false, the users will be generated on-the-fly when they log in - --> - <property name="ldapSyncOnStartup" value="${ldap.ldapSyncOnStartup}" /> - <property name="ldapSyncCronSync" value="${ldap.ldapSyncCronSync}" /> - <!-- if ldapSyncCronSync=true, specify cron expression: http://quartz.sourceforge.net/javadoc/org/quartz/CronTrigger.html --> - <property name="ldapSyncCronSyncExpression" value="${ldap.ldapSyncCronSyncExpression}" /> <!-- run every hour --> - <!-- Configuration for syncing user attributes during login or cron and batch sync --> - <property name="ldapUserFilter" value="${ldap.ldapUserFilter}"/> - <property name="ldapUserCreatedTimestampAttribute" value="${ldap.ldapUserCreatedTimestampAttribute}"/> - <property name="ldapUserLastModifiedTimestampAttribute" value="${ldap.ldapUserLastModifiedTimestampAttribute}"/> - <property name="ldapUserPasswordAttribute" value="${ldap.ldapUserPassordAttribute}"/> + <property name="ldapGroupBases" > + <list> + <value>${ldap.ldapGroupBases}</value> + </list> + </property> + + <property name="groupAttribute" value="${ldap.user.groupAttribute}"/> + <property name="groupAttributeSeparator" value="${ldap.user.groupAttributeSeparator}"/> + <property name="coachRoleAttribute" value="${ldap.coachRoleAttribute}"/> + <property name="coachRoleValue" value="${ldap.coachRoleValue}"/> + + <!-- sync authors --> + <property name="authorsGroupBase" > + <list> + <value>${ldap.authorsGroupBases}</value> + </list> + </property> + <property name="authorRoleAttribute" value="${ldap.authorRoleAttribute}"/> + <property name="authorRoleValue" value="${ldap.authorRoleValue}"/> + + <!-- user managers --> + <property name="userManagersGroupBase" > + <list> + <value>${ldap.userManagersGroupBases}</value> + </list> + </property> + <property name="userManagerRoleAttribute" value="${ldap.userManagerRoleAttribute}"/> + <property name="userManagerRoleValue" value="${ldap.userManagerRoleValue}"/> + + <!-- group managers --> + <property name="groupManagersGroupBase" > + <list> + <value>${ldap.groupManagersGroupBases}</value> + </list> + </property> + <property name="groupManagerRoleAttribute" value="${ldap.groupManagerRoleAttribute}"/> + <property name="groupManagerRoleValue" value="${ldap.groupManagerRoleValue}"/> + + <!-- question pool managers --> + <property name="qpoolManagersGroupBase" > + <list> + <value>${ldap.qpoolManagersGroupBases}</value> + </list> + </property> + <property name="qpoolManagerRoleAttribute" value="${ldap.qpoolManagerRoleAttribute}"/> + <property name="qpoolManagerRoleValue" value="${ldap.qpoolManagerRoleValue}"/> + + <!-- learning resource managers --> + <property name="learningResourceManagersGroupBase" > + <list> + <value>${ldap.learningResourceManagersGroupBase}</value> + </list> + </property> + <property name="learningResourceManagerRoleAttribute" value="${ldap.learningResourceManagerRoleAttribute}"/> + <property name="learningResourceManagerRoleValue" value="${ldap.learningResourceManagerRoleValue}"/> + <!-- Define which user attributes are mandatory and how they are mapped to OLAT user properties. Note that OLAT requires at least the user properties userID and email. Those must be listed @@ -98,13 +95,14 @@ key: the LDAP attribute name value: the OLAT user property name --> - <property name="reqAttrs"> + <property name="requestAttributes"> <map> <entry key='${ldap.attributename.useridentifyer}' value='userID' /> <entry key='${ldap.attributename.email}' value='email' /> </map> </property> - <property name="userAttributeMapper"> + + <property name="userAttributeMap"> <map> <!-- The name that should be used as OLAT loginname / username --> <entry key='${ldap.attributename.useridentifyer}' value='userID' /> @@ -127,10 +125,10 @@ <entry key='${ldap.attrib.gen.map.ldapkey8}' value='${ldap.attrib.gen.map.olatkey8}' /> <entry key='${ldap.attrib.gen.map.ldapkey9}' value='${ldap.attrib.gen.map.olatkey9}' /> <entry key='${ldap.attrib.gen.map.ldapkey10}' value='${ldap.attrib.gen.map.olatkey10}' /> - </map> </property> - <!-- + + <!-- Specify static OLAT user properties that should be populated with a predefined value for each user. This is an optional feature, leave the property empty if you don't need it. It can be use e.g. to distinguish LDAP users from local OLAT users in a course using @@ -144,6 +142,7 @@ <entry key='${ldap.attrib.static.olatkey3}' value='${ldap.attrib.static.value3}' /> </map> </property> + <!-- Specify optional OLAT user properties that are mapped and synced only when the user is created and not in any subsequent sync process. This can be used e.g when a dummy mail @@ -181,16 +180,5 @@ </list> </property> <property name="parentTreeNodeIdentifier" value="sysconfigParent" /> - </bean> - - - <!-- The LDAP manager. If you provide your own, you must implement the org.olat.ldap.LDAPLoginManager interface --> - <bean id="org.olat.ldap.LDAPLoginManager" class="org.olat.ldap.LDAPLoginManagerImpl"> - <constructor-arg index="0" ref="coordinatorManager"/> - <constructor-arg index="1" ref="taskExecutorManager"/> - <property name="userManager" ref="userManager"/> - <property name="securityManager" ref="baseSecurityManager"/> - <property name="userDeletionManager" ref="userDeletionManager"/> </bean> - </beans> \ No newline at end of file diff --git a/src/main/java/org/olat/ldap/manager/LDAPDAO.java b/src/main/java/org/olat/ldap/manager/LDAPDAO.java new file mode 100644 index 00000000000..b9029618676 --- /dev/null +++ b/src/main/java/org/olat/ldap/manager/LDAPDAO.java @@ -0,0 +1,360 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.ldap.manager; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.TimeZone; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.SizeLimitExceededException; +import javax.naming.directory.Attribute; +import javax.naming.directory.DirContext; +import javax.naming.directory.SearchControls; +import javax.naming.directory.SearchResult; +import javax.naming.ldap.Control; +import javax.naming.ldap.LdapContext; +import javax.naming.ldap.PagedResultsControl; +import javax.naming.ldap.PagedResultsResponseControl; + +import net.fortuna.ical4j.util.TimeZones; + +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.util.StringHelper; +import org.olat.ldap.LDAPConstants; +import org.olat.ldap.LDAPLoginModule; +import org.olat.ldap.LDAPSyncConfiguration; +import org.olat.ldap.model.LDAPGroup; +import org.olat.ldap.model.LDAPUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +/** + * + * Initial date: 24.11.2014<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +@Service +public class LDAPDAO { + + private static final OLog log = Tracing.createLoggerFor(LDAPDAO.class); + + private static final int PAGE_SIZE = 50; + private static final TimeZone UTC_TIME_ZONE; + private static final String PAGED_RESULT_CONTROL_OID = "1.2.840.113556.1.4.319"; + + static { + UTC_TIME_ZONE = TimeZone.getTimeZone(TimeZones.UTC_ID); + } + + private boolean pagingSupportedAlreadyFound = false; + + @Autowired + private LDAPLoginModule ldapLoginModule; + @Autowired + private LDAPSyncConfiguration syncConfiguration; + + + public List<LDAPGroup> searchGroups(LdapContext ctx, List<String> groupDNs) { + String filter = syncConfiguration.getLdapGroupFilter(); + List<LDAPGroup> ldapGroups = new ArrayList<>(); + String[] groupAttributes = new String[]{"cn", "member"}; + for(String groupDN:groupDNs) { + LDAPGroupVisitor visitor = new LDAPGroupVisitor(); + search(visitor, groupDN, filter, groupAttributes, ctx); + ldapGroups.addAll(visitor.getGroups()); + } + return ldapGroups; + } + + public void search(LDAPVisitor visitor, String ldapBase, String filter, String[] returningAttrs, LdapContext ctx) { + SearchControls ctls = new SearchControls(); + ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); + ctls.setReturningAttributes(returningAttrs); + ctls.setCountLimit(0); // set no limits + + boolean paging = isPagedResultControlSupported(ctx); + + int counter = 0; + try { + if(paging) { + byte[] cookie = null; + ctx.setRequestControls(new Control[] { new PagedResultsControl(PAGE_SIZE, Control.NONCRITICAL) }); + do { + NamingEnumeration<SearchResult> enm = ctx.search(ldapBase, filter, ctls); + while (enm.hasMore()) { + visitor.visit(enm.next()); + } + cookie = getCookie(ctx); + } while (cookie != null); + } else { + ctx.setRequestControls(null); // reset on failure, see FXOLAT-299 + NamingEnumeration<SearchResult> enm = ctx.search(ldapBase, filter, ctls); + while (enm.hasMore()) { + visitor.visit(enm.next()); + } + counter++; + } + } catch (SizeLimitExceededException e) { + log.error("SizeLimitExceededException after " + + counter + + " records when getting all users from LDAP, reconfigure your LDAP server, hints: http://www.ldapbrowser.com/forum/viewtopic.php?t=14", null); + } catch (NamingException e) { + log.error("NamingException when trying to search from LDAP using ldapBase::" + ldapBase + " on row::" + counter, e); + } catch (Exception e) { + log.error("Exception when trying to search from LDAP using ldapBase::" + ldapBase + " on row::" + counter, e); + } + log.debug("finished search for ldapBase:: " + ldapBase); + } + + + public void searchInLdap(LDAPVisitor visitor, String filter, String[] returningAttrs, LdapContext ctx) { + SearchControls ctls = new SearchControls(); + ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); + ctls.setReturningAttributes(returningAttrs); + ctls.setCountLimit(0); // set no limits + + boolean paging = isPagedResultControlSupported(ctx); + for (String ldapBase : syncConfiguration.getLdapBases()) { + int counter = 0; + try { + if(paging) { + byte[] cookie = null; + ctx.setRequestControls(new Control[] { new PagedResultsControl(PAGE_SIZE, Control.NONCRITICAL) }); + do { + NamingEnumeration<SearchResult> enm = ctx.search(ldapBase, filter, ctls); + while (enm.hasMore()) { + visitor.visit(enm.next()); + } + cookie = getCookie(ctx); + } while (cookie != null); + } else { + ctx.setRequestControls(null); // reset on failure, see FXOLAT-299 + NamingEnumeration<SearchResult> enm = ctx.search(ldapBase, filter, ctls); + while (enm.hasMore()) { + visitor.visit(enm.next()); + } + counter++; + } + } catch (SizeLimitExceededException e) { + log.error("SizeLimitExceededException after " + + counter + + " records when getting all users from LDAP, reconfigure your LDAP server, hints: http://www.ldapbrowser.com/forum/viewtopic.php?t=14", null); + } catch (NamingException e) { + log.error("NamingException when trying to search users from LDAP using ldapBase::" + ldapBase + " on row::" + counter, e); + } catch (Exception e) { + log.error("Exception when trying to search users from LDAP using ldapBase::" + ldapBase + " on row::" + counter, e); + } + log.debug("finished search for ldapBase:: " + ldapBase); + } + } + + public String searchUserDN(String uid, DirContext ctx) { + if(ctx == null) + return null; + + List<String> ldapBases = syncConfiguration.getLdapBases(); + String[] serachAttr = { "dn" }; + + String filter = buildSearchUserFilter(uid); + SearchControls ctls = new SearchControls(); + ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); + ctls.setReturningAttributes(serachAttr); + + String userDN = null; + for (String ldapBase : ldapBases) { + try { + NamingEnumeration<SearchResult> enm = ctx.search(ldapBase, filter, ctls); + while (enm.hasMore()) { + SearchResult result = enm.next(); + userDN = result.getNameInNamespace(); + } + if (userDN != null) { + break; + } + } catch (NamingException e) { + log.error("NamingException when trying to bind user with username::" + uid + " on ldapBase::" + ldapBase, e); + } + } + + return userDN; + } + + public String buildSearchUserFilter(String uid) { + String ldapUserIDAttribute = syncConfiguration.getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER); + String ldapUserFilter = syncConfiguration.getLdapUserFilter(); + StringBuilder filter = new StringBuilder(); + if (ldapUserFilter != null) { + // merge preconfigured filter (e.g. object class, group filters) with username using AND rule + filter.append("(&").append(ldapUserFilter); + } + filter.append("(").append(ldapUserIDAttribute).append("=").append(uid).append(")"); + if (ldapUserFilter != null) { + filter.append(")"); + } + return filter.toString(); + } + + /** + * + * Creates list of all LDAP Users or changed Users since syncTime + * + * Configuration: userAttr = ldapContext.xml (property=userAttrs) LDAP Base = + * ldapContext.xml (property=ldapBase) + * + * + * + * @param syncTime The time to search in LDAP for changes since this time. + * SyncTime has to formatted: JJJJMMddHHmm + * @param ctx The LDAP system connection, if NULL or closed NamingExecpiton is + * thrown + * + * @return Returns list of Arguments of found users or empty list if search + * fails or nothing is changed + * + * @throws NamingException + */ + + public List<LDAPUser> getUserAttributesModifiedSince(Date syncTime, LdapContext ctx) { + final boolean debug = log.isDebug(); + String userFilter = syncConfiguration.getLdapUserFilter(); + StringBuilder filter = new StringBuilder(); + if (syncTime == null) { + if(debug) log.debug("LDAP get user attribs since never -> full sync!"); + if (filter != null) { + filter.append(userFilter); + } + } else { + String dateFormat = ldapLoginModule.getLdapDateFormat(); + SimpleDateFormat generalizedTimeFormatter = new SimpleDateFormat(dateFormat); + generalizedTimeFormatter.setTimeZone(UTC_TIME_ZONE); + String syncTimeForm = generalizedTimeFormatter.format(syncTime); + if(debug) log.debug("LDAP get user attribs since " + syncTime + " -> means search with date restriction-filter: " + syncTimeForm); + if (userFilter != null) { + // merge user filter with time fileter using and rule + filter.append("(&").append(userFilter); + } + filter.append("(|("); + filter.append(syncConfiguration.getLdapUserLastModifiedTimestampAttribute()).append(">=").append(syncTimeForm); + filter.append(")("); + filter.append(syncConfiguration.getLdapUserCreatedTimestampAttribute()).append(">=").append(syncTimeForm); + filter.append("))"); + if (userFilter != null) { + filter.append(")"); + } + } + + String[] userAttrs = getEnhancedUserAttributes(); + LDAPUserVisitor userVisitor = new LDAPUserVisitor(syncConfiguration); + searchInLdap(userVisitor, filter.toString(), userAttrs, ctx); + List<LDAPUser> ldapUserList = userVisitor.getLdapUserList(); + if(debug) { + log.debug("attrib search returned " + ldapUserList.size() + " results"); + } + return ldapUserList; + } + + private String[] getEnhancedUserAttributes() { + String[] userAttrs = syncConfiguration.getUserAttributes(); + + List<String> userAttrList = new ArrayList<>(userAttrs.length + 7); + for(String userAttr:userAttrs) { + userAttrList.add(userAttr); + } + if(StringHelper.containsNonWhitespace(syncConfiguration.getCoachRoleAttribute())) { + userAttrList.add(syncConfiguration.getCoachRoleAttribute()); + } + if(StringHelper.containsNonWhitespace(syncConfiguration.getAuthorRoleAttribute())) { + userAttrList.add(syncConfiguration.getAuthorRoleAttribute()); + } + if(StringHelper.containsNonWhitespace(syncConfiguration.getUserManagerRoleAttribute())) { + userAttrList.add(syncConfiguration.getUserManagerRoleAttribute()); + } + if(StringHelper.containsNonWhitespace(syncConfiguration.getGroupManagerRoleAttribute())) { + userAttrList.add(syncConfiguration.getGroupManagerRoleAttribute()); + } + if(StringHelper.containsNonWhitespace(syncConfiguration.getQpoolManagerRoleAttribute())) { + userAttrList.add(syncConfiguration.getQpoolManagerRoleAttribute()); + } + if(StringHelper.containsNonWhitespace(syncConfiguration.getLearningResourceManagerRoleAttribute())) { + userAttrList.add(syncConfiguration.getLearningResourceManagerRoleAttribute()); + } + if(StringHelper.containsNonWhitespace(syncConfiguration.getGroupAttribute())) { + userAttrList.add(syncConfiguration.getGroupAttribute()); + } + return userAttrList.toArray(new String[userAttrList.size()]); + } + + private byte[] getCookie(LdapContext ctx) throws NamingException, IOException { + byte[] cookie = null; + // Examine the paged results control response + Control[] controls = ctx.getResponseControls(); + if (controls != null) { + for (int i = 0; i < controls.length; i++) { + if (controls[i] instanceof PagedResultsResponseControl) { + PagedResultsResponseControl prrc = (PagedResultsResponseControl) controls[i]; + cookie = prrc.getCookie(); + } + } + } + // Re-activate paged results + ctx.setRequestControls(new Control[] { new PagedResultsControl(PAGE_SIZE, cookie, Control.NONCRITICAL) }); + return cookie; + } + + private boolean isPagedResultControlSupported(LdapContext ctx) { + // FXOLAT-299, might return false on 2nd execution + if (pagingSupportedAlreadyFound == true) return true; + try { + SearchControls ctl = new SearchControls(); + ctl.setReturningAttributes(new String[]{"supportedControl"}); + ctl.setSearchScope(SearchControls.OBJECT_SCOPE); + + /* search for the rootDSE object */ + NamingEnumeration<SearchResult> results = ctx.search("", "(objectClass=*)", ctl); + + while(results.hasMore()){ + SearchResult entry = results.next(); + NamingEnumeration<? extends Attribute> attrs = entry.getAttributes().getAll(); + while (attrs.hasMore()){ + Attribute attr = attrs.next(); + NamingEnumeration<?> vals = attr.getAll(); + while (vals.hasMore()){ + String value = (String) vals.next(); + if(value.equals(PAGED_RESULT_CONTROL_OID)) + pagingSupportedAlreadyFound = true; + return true; + } + } + } + return false; + } catch (Exception e) { + log.error("Exception when trying to know if the server support paged results.", e); + return false; + } + } + +} diff --git a/src/main/java/org/olat/ldap/manager/LDAPGroupVisitor.java b/src/main/java/org/olat/ldap/manager/LDAPGroupVisitor.java new file mode 100644 index 00000000000..5eed214d8cb --- /dev/null +++ b/src/main/java/org/olat/ldap/manager/LDAPGroupVisitor.java @@ -0,0 +1,73 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.ldap.manager; + +import java.util.ArrayList; +import java.util.List; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.SearchResult; + +import org.olat.ldap.model.LDAPGroup; + +/** + * + * Initial date: 24.11.2014<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class LDAPGroupVisitor implements LDAPVisitor { + + private final List<LDAPGroup> groups = new ArrayList<LDAPGroup>(); + + public List<LDAPGroup> getGroups() { + return groups; + } + + @Override + public void visit(SearchResult searchResult) throws NamingException { + Attributes resAttributes = searchResult.getAttributes(); + Attribute memberAttr = resAttributes.get("member"); + Attribute cnAttr = resAttributes.get("cn"); + + LDAPGroup group = new LDAPGroup(); + Object cn = cnAttr.get(); + if(cn instanceof String) { + group.setCommonName((String)cn); + } + + List<String> members = new ArrayList<String>(); + try { + for(NamingEnumeration<?> memberEn = memberAttr.getAll(); memberEn.hasMoreElements(); ) { + Object member = memberEn.next(); + if(member instanceof String) { + members.add((String)member); + } + } + } catch (NamingException e) { + e.printStackTrace(); + } + group.setMembers(members); + groups.add(group); + } +} diff --git a/src/main/java/org/olat/ldap/LDAPLoginManagerImpl.java b/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java similarity index 50% rename from src/main/java/org/olat/ldap/LDAPLoginManagerImpl.java rename to src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java index 2003fca458d..840e4b1f1d1 100644 --- a/src/main/java/org/olat/ldap/LDAPLoginManagerImpl.java +++ b/src/main/java/org/olat/ldap/manager/LDAPLoginManagerImpl.java @@ -18,10 +18,8 @@ * <p> */ -package org.olat.ldap; +package org.olat.ldap.manager; -import java.io.IOException; -import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; @@ -30,49 +28,61 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; -import java.util.TimeZone; import javax.naming.AuthenticationException; import javax.naming.Context; import javax.naming.NamingEnumeration; import javax.naming.NamingException; -import javax.naming.SizeLimitExceededException; import javax.naming.directory.Attribute; import javax.naming.directory.Attributes; import javax.naming.directory.BasicAttribute; import javax.naming.directory.DirContext; import javax.naming.directory.ModificationItem; -import javax.naming.directory.SearchControls; import javax.naming.directory.SearchResult; import javax.naming.ldap.Control; import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.LdapContext; -import javax.naming.ldap.PagedResultsControl; -import javax.naming.ldap.PagedResultsResponseControl; - -import net.fortuna.ical4j.util.TimeZones; import org.apache.commons.lang.ArrayUtils; import org.olat.admin.user.delete.service.UserDeletionManager; import org.olat.basesecurity.Authentication; import org.olat.basesecurity.BaseSecurity; -import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.BaseSecurityModule; import org.olat.basesecurity.Constants; +import org.olat.basesecurity.GroupRoles; import org.olat.basesecurity.SecurityGroup; -import org.olat.core.commons.persistence.DBFactory; +import org.olat.core.commons.persistence.DB; import org.olat.core.commons.services.taskexecutor.TaskExecutorManager; import org.olat.core.gui.control.Event; import org.olat.core.id.Identity; +import org.olat.core.id.Roles; import org.olat.core.id.User; import org.olat.core.id.UserConstants; +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.util.StringHelper; import org.olat.core.util.WorkThreadInformations; import org.olat.core.util.coordinate.Coordinator; import org.olat.core.util.coordinate.CoordinatorManager; import org.olat.core.util.event.GenericEventListener; import org.olat.core.util.mail.MailHelper; +import org.olat.group.BusinessGroup; +import org.olat.group.BusinessGroupManagedFlag; +import org.olat.group.BusinessGroupService; +import org.olat.group.manager.BusinessGroupRelationDAO; +import org.olat.group.model.SearchBusinessGroupParams; +import org.olat.ldap.LDAPConstants; +import org.olat.ldap.LDAPError; +import org.olat.ldap.LDAPEvent; +import org.olat.ldap.LDAPLoginManager; +import org.olat.ldap.LDAPLoginModule; +import org.olat.ldap.LDAPSyncConfiguration; +import org.olat.ldap.model.LDAPGroup; +import org.olat.ldap.model.LDAPUser; import org.olat.ldap.ui.LDAPAuthenticationController; import org.olat.user.UserManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; /** * Description: This manager handles communication between LDAP and OLAT. LDAP access is done by JNDI. @@ -83,62 +93,43 @@ import org.olat.user.UserManager; * * @author Maurus Rohrer */ -public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEventListener { +@Service("org.olat.ldap.LDAPLoginManager") +public class LDAPLoginManagerImpl implements LDAPLoginManager, GenericEventListener { + + private static final OLog log = Tracing.createLoggerFor(LDAPLoginManagerImpl.class); private static final String TIMEOUT_KEY = "com.sun.jndi.ldap.connect.timeout"; - private static final TimeZone UTC_TIME_ZONE; private static boolean batchSyncIsRunning = false; private static Date lastSyncDate = null; // first sync is always a full sync - private static final int PAGE_SIZE = 50; - private static final String PAGED_RESULT_CONTROL_OID = "1.2.840.113556.1.4.319"; - private Coordinator coordinator; private TaskExecutorManager taskExecutorManager; - private BaseSecurity securityManager; + @Autowired + private DB dbInstance; + @Autowired + private LDAPDAO ldapDao; + @Autowired private UserManager userManager; + @Autowired + private BaseSecurity securityManager; + @Autowired + private LDAPLoginModule ldapLoginModule; + @Autowired + private LDAPSyncConfiguration syncConfiguration; + @Autowired private UserDeletionManager userDeletionManager; - private boolean pagingSupportedAlreadyFound; - - static { - UTC_TIME_ZONE = TimeZone.getTimeZone(TimeZones.UTC_ID); - } + @Autowired + private BusinessGroupService businessGroupService; + @Autowired + private BusinessGroupRelationDAO businessGroupRelationDao; - /** - * Private constructor. Use LDAPLoginManager.getInstance() method instead - */ - private LDAPLoginManagerImpl(CoordinatorManager coordinatorManager, TaskExecutorManager taskExecutorManager) { - super(); + @Autowired + public LDAPLoginManagerImpl(CoordinatorManager coordinatorManager, TaskExecutorManager taskExecutorManager) { this.coordinator = coordinatorManager.getCoordinator(); this.taskExecutorManager = taskExecutorManager; - coordinator.getEventBus().registerFor(this, null, ldapSyncLockOres); } - - /** - * [used by Spring] - * @param securityManager - */ - public void setSecurityManager(BaseSecurity securityManager) { - this.securityManager = securityManager; - } - - /** - * [used by Spring] - * @param userManager - */ - public void setUserManager(UserManager userManager) { - this.userManager = userManager; - } - - /** - * [used by Spring] - * @param userDeletionManager - */ - public void setUserDeletionManager(UserDeletionManager userDeletionManager) { - this.userDeletionManager = userDeletionManager; - } @Override public void event(Event event) { @@ -172,8 +163,8 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve /** * Connect to the LDAP server with System DN and Password * - * Configuration: LDAP URL = olatextconfig.xml (property=ldapURL) System DN = - * olatextconfig.xml (property=ldapSystemDN) System PW = olatextconfig.xml + * Configuration: LDAP URL = ldapContext.xml (property=ldapURL) System DN = + * ldapContext.xml (property=ldapSystemDN) System PW = ldapContext.xml * (property=ldapSystemPW) * * @return The LDAP connection (LdapContext) or NULL if connect fails @@ -184,16 +175,16 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve // set LDAP connection attributes Hashtable<String, String> env = new Hashtable<String, String>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); - env.put(Context.PROVIDER_URL, LDAPLoginModule.getLdapUrl()); + env.put(Context.PROVIDER_URL, ldapLoginModule.getLdapUrl()); env.put(Context.SECURITY_AUTHENTICATION, "simple"); - env.put(Context.SECURITY_PRINCIPAL, LDAPLoginModule.getLdapSystemDN()); - env.put(Context.SECURITY_CREDENTIALS, LDAPLoginModule.getLdapSystemPW()); - if(LDAPLoginModule.getLdapConnectionTimeout() != null) { - env.put(TIMEOUT_KEY, LDAPLoginModule.getLdapConnectionTimeout().toString()); + env.put(Context.SECURITY_PRINCIPAL, ldapLoginModule.getLdapSystemDN()); + env.put(Context.SECURITY_CREDENTIALS, ldapLoginModule.getLdapSystemPW()); + if(ldapLoginModule.getLdapConnectionTimeout() != null) { + env.put(TIMEOUT_KEY, ldapLoginModule.getLdapConnectionTimeout().toString()); } // check ssl - if (LDAPLoginModule.isSslEnabled()) { + if (ldapLoginModule.isSslEnabled()) { enableSSL(env); } @@ -202,12 +193,12 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve ctx.getConnectControls(); return ctx; } catch (NamingException e) { - logError("NamingException when trying to bind system with DN::" + LDAPLoginModule.getLdapSystemDN() + " and PW::" - + LDAPLoginModule.getLdapSystemPW() + " on URL::" + LDAPLoginModule.getLdapUrl(), e); + log.error("NamingException when trying to bind system with DN::" + ldapLoginModule.getLdapSystemDN() + " and PW::" + + ldapLoginModule.getLdapSystemPW() + " on URL::" + ldapLoginModule.getLdapUrl(), e); return null; } catch (Exception e) { - logError("Exception when trying to bind system with DN::" + LDAPLoginModule.getLdapSystemDN() + " and PW::" - + LDAPLoginModule.getLdapSystemPW() + " on URL::" + LDAPLoginModule.getLdapUrl(), e); + log.error("Exception when trying to bind system with DN::" + ldapLoginModule.getLdapSystemDN() + " and PW::" + + ldapLoginModule.getLdapSystemPW() + " on URL::" + ldapLoginModule.getLdapUrl(), e); return null; } @@ -217,9 +208,9 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve * * Connect to LDAP with the User-Name and Password given as parameters * - * Configuration: LDAP URL = olatextconfig.xml (property=ldapURL) LDAP Base = - * olatextconfig.xml (property=ldapBase) LDAP Attributes Map = - * olatextconfig.xml (property=userAttrs) + * Configuration: LDAP URL = ldapContext.xml (property=ldapURL) LDAP Base = + * ldapContext.xml (property=ldapBase) LDAP Attributes Map = + * ldapContext.xml (property=userAttrs) * * * @param uid The users LDAP login name (can't be null) @@ -229,13 +220,14 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve * * @throws NamingException */ + @Override public Attributes bindUser(String uid, String pwd, LDAPError errors) { // get user name, password and attributes - String ldapUrl = LDAPLoginModule.getLdapUrl(); - String[] userAttr = LDAPLoginModule.getUserAttrs(); + String ldapUrl = ldapLoginModule.getLdapUrl(); + String[] userAttr = syncConfiguration.getUserAttributes(); if (uid == null || pwd == null) { - if (isLogDebugEnabled()) logDebug("Error when trying to bind user, missing username or password. Username::" + uid + " pwd::" + pwd); + if (log.isDebug()) log.debug("Error when trying to bind user, missing username or password. Username::" + uid + " pwd::" + pwd); errors.insert("Username and password must be selected"); return null; } @@ -245,10 +237,10 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve errors.insert("LDAP connection error"); return null; } - String userDN = searchUserDN(uid, ctx); + String userDN = ldapDao.searchUserDN(uid, ctx); if (userDN == null) { - logInfo("Error when trying to bind user with username::" + uid + " - user not found on LDAP server" - + (LDAPLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin() ? ", trying with OLAT login provider" : "")); + log.info("Error when trying to bind user with username::" + uid + " - user not found on LDAP server" + + (ldapLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin() ? ", trying with OLAT login provider" : "")); errors.insert("Username or password incorrect"); return null; } @@ -261,10 +253,10 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve env.put(Context.SECURITY_AUTHENTICATION, "simple"); env.put(Context.SECURITY_PRINCIPAL, userDN); env.put(Context.SECURITY_CREDENTIALS, pwd); - if(LDAPLoginModule.getLdapConnectionTimeout() != null) { - env.put(TIMEOUT_KEY, LDAPLoginModule.getLdapConnectionTimeout().toString()); + if(ldapLoginModule.getLdapConnectionTimeout() != null) { + env.put(TIMEOUT_KEY, ldapLoginModule.getLdapConnectionTimeout().toString()); } - if (LDAPLoginModule.isSslEnabled()) { + if(ldapLoginModule.isSslEnabled()) { enableSSL(env); } @@ -275,11 +267,11 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve userBind.close(); return attributes; } catch (AuthenticationException e) { - logInfo("Error when trying to bind user with username::" + uid + " - invalid LDAP password"); + log.info("Error when trying to bind user with username::" + uid + " - invalid LDAP password"); errors.insert("Username or password incorrect"); return null; } catch (NamingException e) { - logError("NamingException when trying to get attributes after binding user with username::" + uid, e); + log.error("NamingException when trying to get attributes after binding user with username::" + uid, e); errors.insert("Username or password incorrect"); return null; } @@ -293,23 +285,23 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve @Override public void changePassword(Identity identity, String pwd, LDAPError errors) { String uid = identity.getName(); - String ldapUserPasswordAttribute = LDAPLoginModule.getLdapUserPasswordAttribute(); + String ldapUserPasswordAttribute = syncConfiguration.getLdapUserPasswordAttribute(); try { DirContext ctx = bindSystem(); - String dn = searchUserDN(uid, ctx); + String dn = ldapDao.searchUserDN(uid, ctx); ModificationItem [] modificationItems = new ModificationItem [ 1 ]; Attribute userPasswordAttribute; - if(LDAPLoginModule.isActiveDirectory()) { + if(ldapLoginModule.isActiveDirectory()) { //active directory need the password enquoted and unicoded (but little-endian) String quotedPassword = "\"" + pwd + "\""; - char unicodePwd[] = quotedPassword.toCharArray(); + char unicodePwd[] = quotedPassword.toCharArray(); byte pwdArray[] = new byte[unicodePwd.length * 2]; - for (int i=0; i<unicodePwd.length; i++) { - pwdArray[i*2 + 1] = (byte) (unicodePwd[i] >>> 8); - pwdArray[i*2 + 0] = (byte) (unicodePwd[i] & 0xff); - } + for (int i=0; i<unicodePwd.length; i++) { + pwdArray[i*2 + 1] = (byte) (unicodePwd[i] >>> 8); + pwdArray[i*2 + 0] = (byte) (unicodePwd[i] & 0xff); + } userPasswordAttribute = new BasicAttribute ( ldapUserPasswordAttribute, pwdArray ); } else { userPasswordAttribute = new BasicAttribute ( ldapUserPasswordAttribute, pwd ); @@ -319,128 +311,12 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve ctx.modifyAttributes ( dn, modificationItems ); ctx.close(); } catch (NamingException e) { - logError("NamingException when trying to change password with username::" + uid, e); + log.error("NamingException when trying to change password with username::" + uid, e); errors.insert("Cannot change the password"); } } - - /** - * Find the user dn with its uid - * @param uid - * @param ctx - * @return user's dn - */ - private String searchUserDN(String uid, DirContext ctx) { - if(ctx == null) - return null; - - List<String> ldapBases = LDAPLoginModule.getLdapBases(); - String[] serachAttr = { "dn" }; - - String filter = buildSearchUserFilter(uid); - SearchControls ctls = new SearchControls(); - ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); - ctls.setReturningAttributes(serachAttr); - - String userDN = null; - for (String ldapBase : ldapBases) { - try { - NamingEnumeration<SearchResult> enm = ctx.search(ldapBase, filter, ctls); - while (enm.hasMore()) { - SearchResult result = enm.next(); - userDN = result.getNameInNamespace(); - } - if (userDN != null) { - break; - } - } catch (NamingException e) { - logError("NamingException when trying to bind user with username::" + uid + " on ldapBase::" + ldapBase, e); - } - } - - return userDN; - } - /** - * Build an LDAP search filter for the given user ID using the preconfigured filters - * @param uid the user ID - * @return the filter String - */ - private String buildSearchUserFilter(String uid) { - String ldapUserIDAttribute = LDAPLoginModule.mapOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER); - String ldapUserFilter = LDAPLoginModule.getLdapUserFilter(); - StringBuilder filter = new StringBuilder(); - if (ldapUserFilter != null) { - // merge preconfigured filter (e.g. object class, group filters) with username using AND rule - filter.append("(&").append(ldapUserFilter); - } - filter.append("(").append(ldapUserIDAttribute).append("=").append(uid).append(")"); - if (ldapUserFilter != null) { - filter.append(")"); - } - return filter.toString(); - } - - /** - * - * Creates list of all LDAP Users or changed Users since syncTime - * - * Configuration: userAttr = olatextconfig.xml (property=userAttrs) LDAP Base = - * olatextconfig.xml (property=ldapBase) - * - * - * - * @param syncTime The time to search in LDAP for changes since this time. - * SyncTime has to formatted: JJJJMMddHHmm - * @param ctx The LDAP system connection, if NULL or closed NamingExecpiton is - * thrown - * - * @return Returns list of Arguments of found users or empty list if search - * fails or nothing is changed - * - * @throws NamingException - */ - public List<Attributes> getUserAttributesModifiedSince(Date syncTime, LdapContext ctx) { - String userFilter = LDAPLoginModule.getLdapUserFilter(); - StringBuilder filter = new StringBuilder(); - if (syncTime == null) { - logDebug("LDAP get user attribs since never -> full sync!"); - if (filter != null) { - filter.append(userFilter); - } - } else { - String dateFormat = LDAPLoginModule.getLdapDateFormat(); - SimpleDateFormat generalizedTimeFormatter = new SimpleDateFormat(dateFormat); - generalizedTimeFormatter.setTimeZone(UTC_TIME_ZONE); - String syncTimeForm = generalizedTimeFormatter.format(syncTime); - logDebug("LDAP get user attribs since " + syncTime + " -> means search with date restriction-filter: " + syncTimeForm); - if (userFilter != null) { - // merge user filter with time fileter using and rule - filter.append("(&").append(userFilter); - } - filter.append("(|("); - filter.append(LDAPLoginModule.getLdapUserLastModifiedTimestampAttribute()).append(">=").append(syncTimeForm); - filter.append(")("); - filter.append(LDAPLoginModule.getLdapUserCreatedTimestampAttribute()).append(">=").append(syncTimeForm); - filter.append("))"); - if (userFilter != null) { - filter.append(")"); - } - } - final List<Attributes> ldapUserList = new ArrayList<Attributes>(); - searchInLdap(new LdapVisitor() { - public void visit(SearchResult result) { - Attributes resAttribs = result.getAttributes(); - logDebug(" found : " + resAttribs.size() + " attributes in result " + result.getName()); - ldapUserList.add(resAttribs); - } - }, filter.toString(), LDAPLoginModule.getUserAttrs(), ctx); - - logDebug("attrib search returned " + ldapUserList.size() + " results"); - - return ldapUserList; - } /** @@ -448,13 +324,14 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve * * @param identityList List of Identities to delete */ + @Override public void deletIdentities(List<Identity> identityList) { SecurityGroup secGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); for (Identity identity: identityList) { securityManager.removeIdentityFromSecurityGroup(identity, secGroup); userDeletionManager.deleteIdentity(identity); - DBFactory.getInstance().intermediateCommit(); + dbInstance.intermediateCommit(); } } @@ -465,16 +342,17 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve * (OLATProperty,LDAPValue) * @param identity Identity to sync */ + @Override public void syncUser(Map<String, String> olatPropertyMap, Identity identity) { if (identity == null) { - logWarn("Identiy is null - should not happen", null); + log.warn("Identiy is null - should not happen", null); return; } User user = identity.getUser(); // remove user identifyer - can not be changed later olatPropertyMap.remove(LDAPConstants.LDAP_USER_IDENTIFYER); // remove attributes that are defined as sync-only-on-create - Set<String> syncOnlyOnCreateProperties = LDAPLoginModule.getSyncOnlyOnCreateProperties(); + Set<String> syncOnlyOnCreateProperties = syncConfiguration.getSyncOnlyOnCreateProperties(); if (syncOnlyOnCreateProperties != null) { for (String syncOnlyOnCreateKey : syncOnlyOnCreateProperties) { olatPropertyMap.remove(syncOnlyOnCreateKey); @@ -486,7 +364,7 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve String value = keyValuePair.getValue(); if(value == null) { if(user.getProperty(propName, null) != null) { - logDebug("removed property " + propName + " for identity " + identity); + log.debug("removed property " + propName + " for identity " + identity); user.setProperty(propName, value); } } else { @@ -495,13 +373,12 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve } // Add static user properties from the configuration - Map<String, String> staticProperties = LDAPLoginModule.getStaticUserProperties(); + Map<String, String> staticProperties = syncConfiguration.getStaticUserProperties(); if (staticProperties != null && staticProperties.size() > 0) { for (Map.Entry<String, String> staticProperty : staticProperties.entrySet()) { user.setProperty(staticProperty.getKey(), staticProperty.getValue()); } } - //fxdiff: FXOLAT-228: update user userManager.updateUser(user); } @@ -511,62 +388,62 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve * * @param userAttributes Set of LDAP Attribute of User to be created */ - @SuppressWarnings("unchecked") - public void createAndPersistUser(Attributes userAttributes) { + @Override + public Identity createAndPersistUser(Attributes userAttributes) { // Get and Check Config - String[] reqAttrs = LDAPLoginModule.checkReqAttr(userAttributes); + String[] reqAttrs = syncConfiguration.checkRequestAttributes(userAttributes); if (reqAttrs != null) { - logWarn("Can not create and persist user, the following attributes are missing::" + ArrayUtils.toString(reqAttrs), null); - return; + log.warn("Can not create and persist user, the following attributes are missing::" + ArrayUtils.toString(reqAttrs), null); + return null; } - String uid = getAttributeValue(userAttributes.get(LDAPLoginModule - .mapOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER))); - String email = getAttributeValue(userAttributes.get(LDAPLoginModule.mapOlatPropertyToLdapAttribute(UserConstants.EMAIL))); + String uid = getAttributeValue(userAttributes.get(syncConfiguration + .getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER))); + String email = getAttributeValue(userAttributes.get(syncConfiguration.getOlatPropertyToLdapAttribute(UserConstants.EMAIL))); // Lookup user if (securityManager.findIdentityByName(uid) != null) { - logError("Can't create user with username='" + uid + "', this username does already exist in OLAT database", null); - return; + log.error("Can't create user with username='" + uid + "', this username does already exist in OLAT database", null); + return null; } if (!MailHelper.isValidEmailAddress(email)) { // needed to prevent possibly an AssertException in findIdentityByEmail breaking the sync! - logError("Cannot try to lookup user " + uid + " by email with an invalid email::" + email, null); - return; + log.error("Cannot try to lookup user " + uid + " by email with an invalid email::" + email, null); + return null; } if (userManager.userExist(email) ) { - logError("Can't create user with email='" + email + "', a user with that email does already exist in OLAT database", null); - return; + log.error("Can't create user with email='" + email + "', a user with that email does already exist in OLAT database", null); + return null; } // Create User (first and lastname is added in next step) User user = userManager.createUser(null, null, email); // Set User Property's (Iterates over Attributes and gets OLAT Property out // of olatexconfig.xml) - NamingEnumeration<Attribute> neAttr = (NamingEnumeration<Attribute>) userAttributes.getAll(); + NamingEnumeration<? extends Attribute> neAttr = userAttributes.getAll(); try { while (neAttr.hasMore()) { Attribute attr = neAttr.next(); String olatProperty = mapLdapAttributeToOlatProperty(attr.getID()); - if (!attr.getID().equalsIgnoreCase(LDAPLoginModule.mapOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER)) ) { + if (!attr.getID().equalsIgnoreCase(syncConfiguration.getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER)) ) { String ldapValue = getAttributeValue(attr); if (olatProperty == null || ldapValue == null) continue; user.setProperty(olatProperty, ldapValue); } } // Add static user properties from the configuration - Map<String, String> staticProperties = LDAPLoginModule.getStaticUserProperties(); + Map<String, String> staticProperties = syncConfiguration.getStaticUserProperties(); if (staticProperties != null && staticProperties.size() > 0) { for (Entry<String, String> staticProperty : staticProperties.entrySet()) { user.setProperty(staticProperty.getKey(), staticProperty.getValue()); } } } catch (NamingException e) { - logError("NamingException when trying to create and persist LDAP user with username::" + uid, e); - return; + log.error("NamingException when trying to create and persist LDAP user with username::" + uid, e); + return null; } catch (Exception e) { // catch any exception here to properly log error - logError("Unknown exception when trying to create and persist LDAP user with username::" + uid, e); - return; + log.error("Unknown exception when trying to create and persist LDAP user with username::" + uid, e); + return null; } // Create Identity @@ -577,8 +454,8 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve // Add to SecurityGroup OLATUSERS secGroup = securityManager.findSecurityGroupByName(Constants.GROUP_OLATUSERS); securityManager.addIdentityToSecurityGroup(identity, secGroup); - logInfo("Created LDAP user username::" + uid); - + log.info("Created LDAP user username::" + uid); + return identity; } @@ -617,15 +494,15 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve } } if (olatPropertyMap.size() == 1 && olatPropertyMap.get(LDAPConstants.LDAP_USER_IDENTIFYER) != null) { - logDebug("propertymap for identity " + identity.getName() + " contains only userID, NOTHING TO SYNC!"); + log.debug("propertymap for identity " + identity.getName() + " contains only userID, NOTHING TO SYNC!"); return null; } else { - logDebug("propertymap for identity " + identity.getName() + " contains " + olatPropertyMap.size() + " items (" + olatPropertyMap.keySet() + ") to be synced later on"); + log.debug("propertymap for identity " + identity.getName() + " contains " + olatPropertyMap.size() + " items (" + olatPropertyMap.keySet() + ") to be synced later on"); return olatPropertyMap; } } catch (NamingException e) { - logError("NamingException when trying to prepare user properties for LDAP sync", e); + log.error("NamingException when trying to prepare user properties for LDAP sync", e); return null; } } @@ -633,13 +510,13 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve /** * Maps LDAP Attributes to the OLAT Property * - * Configuration: LDAP Attributes Map = olatextconfig.xml (property=userAttrs) + * Configuration: LDAP Attributes Map = ldapContext.xml (property=userAttrs) * * @param attrID LDAP Attribute * @return OLAT Property */ private String mapLdapAttributeToOlatProperty(String attrID) { - Map<String, String> userAttrMapper = LDAPLoginModule.getUserAttributeMapper(); + Map<String, String> userAttrMapper = syncConfiguration.getUserAttributeMap(); String olatProperty = userAttrMapper.get(attrID); return olatProperty; } @@ -658,7 +535,7 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve String attrValue = (String)attribute.get(); return attrValue; } catch (NamingException e) { - logError("NamingException when trying to get attribute value for attribute::" + attribute, e); + log.error("NamingException when trying to get attribute value for attribute::" + attribute, e); return null; } } @@ -681,7 +558,7 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve } else { SecurityGroup ldapGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); if (ldapGroup == null) { - logError("Error getting user from OLAT security group '" + LDAPConstants.SECURITY_GROUP_LDAP + "' : group does not exist", null); + log.error("Error getting user from OLAT security group '" + LDAPConstants.SECURITY_GROUP_LDAP + "' : group does not exist", null); return null; } if (securityManager.isIdentityInSecurityGroup(identity, ldapGroup)) { @@ -693,18 +570,17 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve return identity; } else { - if (LDAPLoginModule.isConvertExistingLocalUsersToLDAPUsers()) { + if (ldapLoginModule.isConvertExistingLocalUsersToLDAPUsers()) { // Add user to LDAP security group and add the ldap provider securityManager.createAndPersistAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP, identity.getName(), null, null); securityManager.addIdentityToSecurityGroup(identity, ldapGroup); - logInfo("Found identity by LDAP username that was not yet in LDAP security group. Converted user::" + uid + log.info("Found identity by LDAP username that was not yet in LDAP security group. Converted user::" + uid + " to be an LDAP managed user"); return identity; } else { errors.insert("findIdentyByLdapAuthentication: User with username::" + uid + " exist but not Managed by LDAP"); return null; } - } } } @@ -714,8 +590,8 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve * Creates list of all OLAT Users which have been deleted out of the LDAP * directory but still exits in OLAT * - * Configuration: Required Attributes = olatextconfig.xml (property=reqAttrs) - * LDAP Base = olatextconfig.xml (property=ldapBase) + * Configuration: Required Attributes = ldapContext.xml (property=reqAttrs) + * LDAP Base = ldapContext.xml (property=ldapBase) * * @param syncTime The time to search in LDAP for changes since this time. * SyncTime has to formatted: JJJJMMddHHmm @@ -730,11 +606,12 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve public List<Identity> getIdentitysDeletedInLdap(LdapContext ctx) { if (ctx == null) return null; // Find all LDAP Users - String userID = LDAPLoginModule.mapOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER); - String userFilter = LDAPLoginModule.getLdapUserFilter(); + String userID = syncConfiguration.getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER); + String userFilter = syncConfiguration.getLdapUserFilter(); final List<String> ldapList = new ArrayList<String>(); - searchInLdap(new LdapVisitor() { + ldapDao.searchInLdap(new LDAPVisitor() { + @Override public void visit(SearchResult result) throws NamingException { Attributes attrs = result.getAttributes(); NamingEnumeration<? extends Attribute> aEnum = attrs.getAll(); @@ -747,14 +624,14 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve }, (userFilter == null ? "" : userFilter), new String[] { userID }, ctx); if (ldapList.isEmpty()) { - logWarn("No users in LDAP found, can't create deletionList!!", null); + log.warn("No users in LDAP found, can't create deletionList!!", null); return null; } // Find all User in OLAT, members of LDAPSecurityGroup SecurityGroup ldapGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); if (ldapGroup == null) { - logError("Error getting users from OLAT security group '" + LDAPConstants.SECURITY_GROUP_LDAP + "' : group does not exist", null); + log.error("Error getting users from OLAT security group '" + LDAPConstants.SECURITY_GROUP_LDAP + "' : group does not exist", null); return null; } @@ -768,100 +645,10 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve } return identityListToDelete; } - - private void searchInLdap(LdapVisitor visitor, String filter, String[] returningAttrs, LdapContext ctx) { - SearchControls ctls = new SearchControls(); - ctls.setSearchScope(SearchControls.SUBTREE_SCOPE); - ctls.setReturningAttributes(returningAttrs); - ctls.setCountLimit(0); // set no limits - - boolean paging = isPagedResultControlSupported(ctx); - for (String ldapBase : LDAPLoginModule.getLdapBases()) { - int counter = 0; - try { - if(paging) { - byte[] cookie = null; - ctx.setRequestControls(new Control[] { new PagedResultsControl(PAGE_SIZE, Control.NONCRITICAL) }); - do { - NamingEnumeration<SearchResult> enm = ctx.search(ldapBase, filter, ctls); - while (enm.hasMore()) { - visitor.visit(enm.next()); - } - cookie = getCookie(ctx); - } while (cookie != null); - } else { - ctx.setRequestControls(null); // reset on failure, see FXOLAT-299 - NamingEnumeration<SearchResult> enm = ctx.search(ldapBase, filter, ctls); - while (enm.hasMore()) { - visitor.visit(enm.next()); - } - counter++; - } - } catch (SizeLimitExceededException e) { - logError("SizeLimitExceededException after " - + counter - + " records when getting all users from LDAP, reconfigure your LDAP server, hints: http://www.ldapbrowser.com/forum/viewtopic.php?t=14", null); - } catch (NamingException e) { - logError("NamingException when trying to fetch deleted users from LDAP using ldapBase::" + ldapBase + " on row::" + counter, e); - } catch (Exception e) { - logError("Exception when trying to fetch deleted users from LDAP using ldapBase::" + ldapBase + " on row::" + counter, e); - } - logDebug("finished search for ldapBase:: " + ldapBase); - } - } - - private byte[] getCookie(LdapContext ctx) throws NamingException, IOException { - byte[] cookie = null; - // Examine the paged results control response - Control[] controls = ctx.getResponseControls(); - if (controls != null) { - for (int i = 0; i < controls.length; i++) { - if (controls[i] instanceof PagedResultsResponseControl) { - PagedResultsResponseControl prrc = (PagedResultsResponseControl) controls[i]; - cookie = prrc.getCookie(); - } - } - } - // Re-activate paged results - ctx.setRequestControls(new Control[] { new PagedResultsControl(PAGE_SIZE, cookie, Control.NONCRITICAL) }); - return cookie; - } - - private boolean isPagedResultControlSupported(LdapContext ctx) { - // FXOLAT-299, might return false on 2nd execution - if (pagingSupportedAlreadyFound == true) return true; - try { - SearchControls ctl = new SearchControls(); - ctl.setReturningAttributes(new String[]{"supportedControl"}); - ctl.setSearchScope(SearchControls.OBJECT_SCOPE); - - /* search for the rootDSE object */ - NamingEnumeration<SearchResult> results = ctx.search("", "(objectClass=*)", ctl); - - while(results.hasMore()){ - SearchResult entry = results.next(); - NamingEnumeration<? extends Attribute> attrs = entry.getAttributes().getAll(); - while (attrs.hasMore()){ - Attribute attr = attrs.next(); - NamingEnumeration<?> vals = attr.getAll(); - while (vals.hasMore()){ - String value = (String) vals.next(); - if(value.equals(PAGED_RESULT_CONTROL_OID)) - pagingSupportedAlreadyFound = true; - return true; - } - } - } - return false; - } catch (Exception e) { - logError("Exception when trying to know if the server support paged results.", e); - return false; - } - } /** * Execute Batch Sync. Will update all Attributes of LDAP users in OLAt, create new users and delete users in OLAT. - * Can be configured in olatextconfig.xml + * Can be configured in ldapContext.xml * * @param LDAPError * @@ -870,7 +657,7 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve public boolean doBatchSync(LDAPError errors, boolean full) { //fxdiff: also run on nodes != 1 as nodeid = tomcat-id in fx-environment // if(WebappHelper.getNodeId() != 1) { -// logWarn("Sync happens only on node 1", null); +// log.warn("Sync happens only on node 1", null); // return false; // } @@ -885,7 +672,7 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve synchronized (LDAPLoginManagerImpl.class) { if (batchSyncIsRunning) { // don't run twice, skip this execution - logInfo("LDAP user doBatchSync started, but another job is still running - skipping this sync"); + log.info("LDAP user doBatchSync started, but another job is still running - skipping this sync"); errors.insert("BatchSync already running by concurrent process"); return false; } @@ -906,7 +693,7 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve ctx = bindSystem(); if (ctx == null) { errors.insert("LDAP connection ERROR"); - logError("Error in LDAP batch sync: LDAP connection empty", null); + log.error("Error in LDAP batch sync: LDAP connection empty", null); freeSyncLock(); success = false; return success; @@ -917,10 +704,17 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve // Get time before sync to have a save sync time when sync is successful String sinceSentence = (lastSyncDate == null ? " (full sync)" : " since last sync from " + lastSyncDate); doBatchSyncDeletedUsers(ctx, sinceSentence); - // fxdiff: see FXOLAT-299 // bind again to use an initial unmodified context. lookup of server-properties might fail otherwise! + ctx.close(); + ctx = bindSystem(); + Map<String,LDAPUser> dnToIdentityKeyMap = new HashMap<>(); + List<LDAPUser> ldapUsers = doBatchSyncNewAndModifiedUsers(ctx, sinceSentence, dnToIdentityKeyMap, errors); + ctx.close(); ctx = bindSystem(); - doBatchSyncNewAndModifiedUsers(ctx, sinceSentence, errors); + //sync groups by LDAP groups or attributes + doBatchSyncGroups(ctx, ldapUsers, dnToIdentityKeyMap, errors); + //sync roles + doBatchSyncRoles(ctx, ldapUsers, dnToIdentityKeyMap, errors); // update sync time and set running flag lastSyncDate = timeBeforeSync; @@ -931,7 +725,7 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve } catch (Exception e) { errors.insert("Unknown error"); - logError("Error in LDAP batch sync, unknown reason", e); + log.error("Error in LDAP batch sync, unknown reason", e); success = false; return success; } finally { @@ -952,14 +746,154 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve } } + private void doBatchSyncGroups(LdapContext ctx, List<LDAPUser> ldapUsers, Map<String,LDAPUser> dnToIdentityKeyMap, LDAPError errors) + throws NamingException { + ctx.close(); + ctx = bindSystem(); + //sync groups by LDAP groups or attributes + if(syncConfiguration.syncGroupWithLDAPGroup()) { + doSyncLDAPGroups(ctx, dnToIdentityKeyMap, errors); + } + if(syncConfiguration.syncGroupWithAttribute()) { + doSyncGroupAttribute(ldapUsers); + } + } + + private void doBatchSyncRoles(LdapContext ctx, List<LDAPUser> ldapUsers, Map<String,LDAPUser> dnToIdentityKeyMap, LDAPError errors) + throws NamingException { + ctx.close(); + ctx = bindSystem(); + + //authors + if(syncConfiguration.getAuthorsGroupBase() != null && syncConfiguration.getAuthorsGroupBase().size() > 0) { + List<LDAPGroup> authorGroups = ldapDao.searchGroups(ctx, syncConfiguration.getAuthorsGroupBase()); + syncRole(ctx, authorGroups, Constants.GROUP_AUTHORS, dnToIdentityKeyMap, errors); + } + //user managers + if(syncConfiguration.getUserManagersGroupBase() != null && syncConfiguration.getUserManagersGroupBase().size() > 0) { + List<LDAPGroup> userManagerGroups = ldapDao.searchGroups(ctx, syncConfiguration.getUserManagersGroupBase()); + syncRole(ctx, userManagerGroups, Constants.GROUP_USERMANAGERS, dnToIdentityKeyMap, errors); + } + //group managers + if(syncConfiguration.getGroupManagersGroupBase() != null && syncConfiguration.getGroupManagersGroupBase().size() > 0) { + List<LDAPGroup> groupManagerGroups = ldapDao.searchGroups(ctx, syncConfiguration.getGroupManagersGroupBase()); + syncRole(ctx, groupManagerGroups, Constants.GROUP_GROUPMANAGERS, dnToIdentityKeyMap, errors); + } + //question pool managers + if(syncConfiguration.getQpoolManagersGroupBase() != null && syncConfiguration.getQpoolManagersGroupBase().size() > 0) { + List<LDAPGroup> qpoolManagerGroups = ldapDao.searchGroups(ctx, syncConfiguration.getQpoolManagersGroupBase()); + syncRole(ctx, qpoolManagerGroups, Constants.GROUP_POOL_MANAGER, dnToIdentityKeyMap, errors); + } + //learning resource manager + if(syncConfiguration.getLearningResourceManagersGroupBase() != null && syncConfiguration.getLearningResourceManagersGroupBase().size() > 0) { + List<LDAPGroup> resourceManagerGroups = ldapDao.searchGroups(ctx, syncConfiguration.getLearningResourceManagersGroupBase()); + syncRole(ctx, resourceManagerGroups, Constants.GROUP_INST_ORES_MANAGER, dnToIdentityKeyMap, errors); + } + + + int count = 0; + boolean syncAuthor = StringHelper.containsNonWhitespace(syncConfiguration.getAuthorRoleAttribute()) + && StringHelper.containsNonWhitespace(syncConfiguration.getAuthorRoleValue()); + boolean syncUserManager = StringHelper.containsNonWhitespace(syncConfiguration.getUserManagerRoleAttribute()) + && StringHelper.containsNonWhitespace(syncConfiguration.getUserManagerRoleValue()); + boolean syncGroupManager = StringHelper.containsNonWhitespace(syncConfiguration.getGroupManagerRoleAttribute()) + && StringHelper.containsNonWhitespace(syncConfiguration.getGroupManagerRoleValue()); + boolean syncQpoolManager = StringHelper.containsNonWhitespace(syncConfiguration.getQpoolManagerRoleAttribute()) + && StringHelper.containsNonWhitespace(syncConfiguration.getQpoolManagerRoleValue()); + boolean syncLearningResourceManager = StringHelper.containsNonWhitespace(syncConfiguration.getLearningResourceManagerRoleAttribute()) + && StringHelper.containsNonWhitespace(syncConfiguration.getLearningResourceManagerRoleValue()); + + for(LDAPUser ldapUser:ldapUsers) { + if(syncAuthor && ldapUser.isAuthor()) { + syncRole(ldapUser, Constants.GROUP_AUTHORS); + count++; + } + if(syncUserManager && ldapUser.isUserManager()) { + syncRole(ldapUser, Constants.GROUP_USERMANAGERS); + count++; + } + if(syncGroupManager && ldapUser.isGroupManager()) { + syncRole(ldapUser, Constants.GROUP_GROUPMANAGERS); + count++; + } + if(syncQpoolManager && ldapUser.isQpoolManager()) { + syncRole(ldapUser, Constants.GROUP_POOL_MANAGER); + count++; + } + if(syncLearningResourceManager && ldapUser.isLearningResourceManager()) { + syncRole(ldapUser, Constants.GROUP_INST_ORES_MANAGER); + count++; + } + if(count > 20) { + dbInstance.commitAndCloseSession(); + count = 0; + } + } + + dbInstance.commitAndCloseSession(); + } + + private void syncRole(LdapContext ctx, List<LDAPGroup> groups, String role, + Map<String,LDAPUser> dnToIdentityKeyMap, LDAPError errors) { + if(groups == null || groups.isEmpty()) return; + + for(LDAPGroup group:groups) { + List<String> members = group.getMembers(); + if(members != null && members.size() > 0) { + for(String member:members) { + LDAPUser ldapUser = getLDAPUser(ctx, member, dnToIdentityKeyMap, errors); + if(ldapUser != null && ldapUser.getCachedIdentity() != null) { + syncRole(ldapUser, role); + } + } + } + dbInstance.commitAndCloseSession(); + } + } + + private void syncRole(LDAPUser ldapUser, String role) { + Identity identity = ldapUser.getCachedIdentity(); + List<String> roleList = securityManager.getRolesAsString(identity); + if(!roleList.contains(role)) { + Roles roles = securityManager.getRoles(identity); + switch(role) { + case Constants.GROUP_AUTHORS: + roles = new Roles(roles.isOLATAdmin(), roles.isUserManager(), roles.isGroupManager(), true, + false, roles.isInstitutionalResourceManager(), roles.isPoolAdmin(), false); + securityManager.updateRoles(null, identity, roles); + break; + case Constants.GROUP_USERMANAGERS: + roles = new Roles(roles.isOLATAdmin(), true, roles.isGroupManager(), roles.isAuthor(), + false, roles.isInstitutionalResourceManager(), roles.isPoolAdmin(), false); + securityManager.updateRoles(null, identity, roles); + break; + case Constants.GROUP_GROUPMANAGERS: + roles = new Roles(roles.isOLATAdmin(), roles.isUserManager(), true, roles.isAuthor(), + false, roles.isInstitutionalResourceManager(), roles.isPoolAdmin(), false); + securityManager.updateRoles(null, identity, roles); + break; + case Constants.GROUP_POOL_MANAGER: + roles = new Roles(roles.isOLATAdmin(), roles.isUserManager(), roles.isGroupManager(), roles.isAuthor(), + false, roles.isInstitutionalResourceManager(), true, false); + securityManager.updateRoles(null, identity, roles); + break; + case Constants.GROUP_INST_ORES_MANAGER: + roles = new Roles(roles.isOLATAdmin(), roles.isUserManager(), roles.isGroupManager(), roles.isAuthor(), + false, true, roles.isPoolAdmin(), false); + securityManager.updateRoles(null, identity, roles); + break; + } + } + } + private void doBatchSyncDeletedUsers(LdapContext ctx, String sinceSentence) { // create User to Delete List List<Identity> deletedUserList = getIdentitysDeletedInLdap(ctx); // delete old users if (deletedUserList == null || deletedUserList.size() == 0) { - logInfo("LDAP batch sync: no users to delete" + sinceSentence); + log.info("LDAP batch sync: no users to delete" + sinceSentence); } else { - if (LDAPLoginModule.isDeleteRemovedLDAPUsersOnSync()) { + if (ldapLoginModule.isDeleteRemovedLDAPUsersOnSync()) { // check if more not more than the defined percentages of // users managed in LDAP should be deleted // if they are over the percentage, they will not be deleted @@ -967,19 +901,19 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve SecurityGroup ldapGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); List<Identity> olatListIdentity = securityManager.getIdentitiesOfSecurityGroup(ldapGroup); if (olatListIdentity.isEmpty()) - logInfo("No users managed by LDAP, can't delete users"); + log.info("No users managed by LDAP, can't delete users"); else { int prozente = (int) (((float)deletedUserList.size() / (float) olatListIdentity.size())*100); - if (prozente >= LDAPLoginModule.getDeleteRemovedLDAPUsersPercentage()) { - logInfo("LDAP batch sync: more than " - + LDAPLoginModule.getDeleteRemovedLDAPUsersPercentage() + if (prozente >= ldapLoginModule.getDeleteRemovedLDAPUsersPercentage()) { + log.info("LDAP batch sync: more than " + + ldapLoginModule.getDeleteRemovedLDAPUsersPercentage() + "% of LDAP managed users should be deleted. Please use Admin Deletion Job. Or increase deleteRemovedLDAPUsersPercentage. " + prozente + "% tried to delete."); } else { // delete users deletIdentities(deletedUserList); - logInfo("LDAP batch sync: " + log.info("LDAP batch sync: " + deletedUserList.size() + " users deleted" + sinceSentence); } @@ -990,7 +924,7 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve for (Identity toBeDeleted : deletedUserList) { users.append(toBeDeleted.getName()).append(','); } - logInfo("LDAP batch sync: " + log.info("LDAP batch sync: " + deletedUserList.size() + " users detected as to be deleted" + sinceSentence @@ -1000,96 +934,273 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve } } - private void doBatchSyncNewAndModifiedUsers(LdapContext ctx, String sinceSentence, LDAPError errors) { + private List<LDAPUser> doBatchSyncNewAndModifiedUsers(LdapContext ctx, String sinceSentence, Map<String,LDAPUser> dnToIdentityKeyMap, LDAPError errors) { // Get new and modified users from LDAP int count = 0; - List<Attributes> ldapUserList = getUserAttributesModifiedSince(lastSyncDate, ctx); + List<LDAPUser> ldapUserList = ldapDao.getUserAttributesModifiedSince(lastSyncDate, ctx); // Check for new and modified users - List<Attributes> newLdapUserList = new ArrayList<Attributes>(); + List<LDAPUser> newLdapUserList = new ArrayList<LDAPUser>(); Map<Identity, Map<String, String>> changedMapIdentityMap = new HashMap<Identity, Map<String, String>>(); - for (Attributes userAttrs: ldapUserList) { + for (LDAPUser ldapUser: ldapUserList) { String user = null; - try { - user = getAttributeValue(userAttrs.get(LDAPLoginModule.mapOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER))); + try { + Attributes userAttrs = ldapUser.getAttributes(); + String uidProp = syncConfiguration.getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER); + user = getAttributeValue(userAttrs.get(uidProp)); Identity identity = findIdentyByLdapAuthentication(user, errors); if (identity != null) { Map<String, String> changedAttrMap = prepareUserPropertyForSync(userAttrs, identity); - if (changedAttrMap != null) changedMapIdentityMap.put(identity, changedAttrMap); + if (changedAttrMap != null) { + changedMapIdentityMap.put(identity, changedAttrMap); + } + if(StringHelper.containsNonWhitespace(ldapUser.getDn())) { + dnToIdentityKeyMap.put(ldapUser.getDn(), ldapUser); + ldapUser.setCachedIdentity(identity); + } } else if (errors.isEmpty()) { - String[] reqAttrs = LDAPLoginModule.checkReqAttr(userAttrs); + String[] reqAttrs = syncConfiguration.checkRequestAttributes(userAttrs); if (reqAttrs == null) { - newLdapUserList.add(userAttrs); - } - else logWarn("Error in LDAP batch sync: can't create user with username::" + user + " : missing required attributes::" + newLdapUserList.add(ldapUser); + } else { + log.warn("Error in LDAP batch sync: can't create user with username::" + user + " : missing required attributes::" + ArrayUtils.toString(reqAttrs), null); + } } else { - logWarn(errors.get(), null); + log.warn(errors.get(), null); } + if(++count % 20 == 0) { - DBFactory.getInstance().intermediateCommit(); + dbInstance.intermediateCommit(); } - } catch (Exception e) { + } catch (Exception e) { // catch here to go on with other users on exeptions! - logError("some error occured in looping over set of changed user-attributes, actual user " + user + ". Will still continue with others.", e); + log.error("some error occured in looping over set of changed user-attributes, actual user " + user + ". Will still continue with others.", e); } } // sync existing users if (changedMapIdentityMap == null || changedMapIdentityMap.isEmpty()) { - logInfo("LDAP batch sync: no users to sync" + sinceSentence); + log.info("LDAP batch sync: no users to sync" + sinceSentence); } else { for (Identity ident : changedMapIdentityMap.keySet()) { // sync user is exception save, no try/catch needed syncUser(changedMapIdentityMap.get(ident), ident); //REVIEW Identity are not saved??? if(++count % 20 == 0) { - DBFactory.getInstance().intermediateCommit(); + dbInstance.intermediateCommit(); } } - logInfo("LDAP batch sync: " + changedMapIdentityMap.size() + " users synced" + sinceSentence); + log.info("LDAP batch sync: " + changedMapIdentityMap.size() + " users synced" + sinceSentence); } // create new users if (newLdapUserList.isEmpty()) { - logInfo("LDAP batch sync: no users to create" + sinceSentence); + log.info("LDAP batch sync: no users to create" + sinceSentence); } else { - for (Attributes userAttrs: newLdapUserList) { + for (LDAPUser ldapUser: newLdapUserList) { + Attributes userAttrs = ldapUser.getAttributes(); try { - createAndPersistUser(userAttrs); + Identity identity = createAndPersistUser(userAttrs); if(++count % 20 == 0) { - DBFactory.getInstance().intermediateCommit(); + dbInstance.intermediateCommit(); + } + + if(StringHelper.containsNonWhitespace(ldapUser.getDn())) { + dnToIdentityKeyMap.put(ldapUser.getDn(), ldapUser); + ldapUser.setCachedIdentity(identity); } } catch (Exception e) { // catch here to go on with other users on exeptions! - logError("some error occured while creating new users, actual userAttribs " + userAttrs + ". Will still continue with others.", e); + log.error("some error occured while creating new users, actual userAttribs " + userAttrs + ". Will still continue with others.", e); } } - logInfo("LDAP batch sync: " + newLdapUserList.size() + " users created" + sinceSentence); + log.info("LDAP batch sync: " + newLdapUserList.size() + " users created" + sinceSentence); } - //fxdiff: FXOLAT-228: update user - DBFactory.getInstance().intermediateCommit(); + dbInstance.intermediateCommit(); + return ldapUserList; } + private void doSyncGroupAttribute(List<LDAPUser> ldapUsers) { + Map<String,List<LDAPUser>> externelIdToGroupMap = new HashMap<>(); + for(LDAPUser ldapUser:ldapUsers) { + List<String> groupIds = ldapUser.getGroupIds(); + if(groupIds != null && groupIds.size() > 0) { + Identity identity = ldapUser.getCachedIdentity(); + if(identity == null) { + log.error("Identity with dn=" + ldapUser.getDn() + " not found"); + } else { + for(String groupId:groupIds) { + if(!externelIdToGroupMap.containsKey(groupId)) { + externelIdToGroupMap.put(groupId, new ArrayList<LDAPUser>()); + } + externelIdToGroupMap.get(groupId).add(ldapUser); + } + } + } + } + + for(Map.Entry<String, List<LDAPUser>> groupEntry:externelIdToGroupMap.entrySet()) { + String externalId = groupEntry.getKey(); + List<LDAPUser> members = groupEntry.getValue(); + BusinessGroup managedGroup = getManagerBusinessGroup(externalId); + if(managedGroup != null) { + syncBusinessGroup(managedGroup, members); + } + dbInstance.commitAndCloseSession(); + } + } - // TODO: not finished! + private void syncBusinessGroup(BusinessGroup businessGroup, List<LDAPUser> members) { + List<Identity> currentMembers = businessGroupRelationDao + .getMembers(businessGroup, GroupRoles.coach.name(), GroupRoles.participant.name()); + + for(LDAPUser member:members) { + Identity memberIdentity = member.getCachedIdentity(); + syncMembership(businessGroup, memberIdentity, member.isCoach()); + currentMembers.remove(memberIdentity); + } + + for(Identity currentMember:currentMembers) { + List<String> roles = businessGroupRelationDao.getRoles(currentMember, businessGroup); + for(String role:roles) { + businessGroupRelationDao.removeRole(currentMember, businessGroup, role); + } + } + } + + private void syncMembership(BusinessGroup businessGroup, Identity identity, boolean coach) { + if(identity != null) { + List<String> roles = businessGroupRelationDao.getRoles(identity, businessGroup); + if(roles.isEmpty()) { + if(coach) { + businessGroupRelationDao.addRole(identity, businessGroup, GroupRoles.coach.name()); + } else { + businessGroupRelationDao.addRole(identity, businessGroup, GroupRoles.participant.name()); + } + } else if(coach && roles.size() == 1 && roles.contains(GroupRoles.coach.name())) { + //coach and only coach, do nothing + } else if(!coach && roles.size() == 1 && roles.contains(GroupRoles.participant.name())) { + //participant and only participant, do nothing + } else { + boolean already = false; + String mainRole = coach ? GroupRoles.coach.name() : GroupRoles.participant.name(); + for(String role:roles) { + if(mainRole.equals(role)) { + already = true; + } else { + businessGroupRelationDao.removeRole(identity, businessGroup, role); + } + } + + if(!already) { + businessGroupRelationDao.addRole(identity, businessGroup, mainRole); + } + } + } + } + + private void doSyncLDAPGroups(LdapContext ctx, Map<String,LDAPUser> dnToIdentityKeyMap, LDAPError errors) { + List<String> groupDNs = syncConfiguration.getLdapGroupBases(); + List<LDAPGroup> ldapGroups = ldapDao.searchGroups(ctx, groupDNs); + + for(LDAPGroup ldapGroup:ldapGroups) { + String externalId = ldapGroup.getCommonName(); + BusinessGroup managedGroup = getManagerBusinessGroup(externalId); + if(managedGroup != null) { + syncBusinessGroup(ctx, ldapGroup, managedGroup, dnToIdentityKeyMap, errors); + } + dbInstance.commitAndCloseSession(); + } + } + + private BusinessGroup getManagerBusinessGroup(String externalId) { + SearchBusinessGroupParams params = new SearchBusinessGroupParams(); + params.setExternalId(externalId); + List<BusinessGroup> businessGroups = businessGroupService.findBusinessGroups(params, null, 0, -1); + + BusinessGroup managedBusinessGroup; + if(businessGroups.size() == 0) { + String managedFlags = BusinessGroupManagedFlag.membersmanagement.name() + "," + BusinessGroupManagedFlag.delete.name(); + managedBusinessGroup = businessGroupService + .createBusinessGroup(null, externalId, externalId, externalId, managedFlags, null, null, false, false, null); + + } else if(businessGroups.size() == 1) { + managedBusinessGroup = businessGroups.get(0); + } else { + log.error(businessGroups.size() + " managed groups found with the following external id: " + externalId); + managedBusinessGroup = null; + } + return managedBusinessGroup; + } + + private void syncBusinessGroup(LdapContext ctx, LDAPGroup ldapGroup, BusinessGroup businessGroup, Map<String,LDAPUser> dnToIdentityKeyMap, LDAPError errors) { + List<String> members = ldapGroup.getMembers(); + List<Identity> currentMembers = businessGroupRelationDao + .getMembers(businessGroup, GroupRoles.coach.name(), GroupRoles.participant.name()); + + for(String member:members) { + LDAPUser ldapUser = getLDAPUser(ctx, member, dnToIdentityKeyMap, errors); + if(ldapUser != null) { + Identity identity = ldapUser.getCachedIdentity(); + syncMembership(businessGroup, identity, ldapUser.isCoach()); + currentMembers.remove(identity); + } + } + + for(Identity currentMember:currentMembers) { + List<String> roles = businessGroupRelationDao.getRoles(currentMember, businessGroup); + for(String role:roles) { + businessGroupRelationDao.removeRole(currentMember, businessGroup, role); + } + } + } + + private LDAPUser getLDAPUser(LdapContext ctx, String member, Map<String,LDAPUser> dnToIdentityKeyMap, LDAPError errors) { + LDAPUser ldapUser = dnToIdentityKeyMap.get(member); + + Identity identity = ldapUser == null ? null : ldapUser.getCachedIdentity(); + if(identity == null) { + String userFilter = syncConfiguration.getLdapUserFilter(); + String uidProp = syncConfiguration.getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER); + + String userDN = member; + LDAPUserVisitor visitor = new LDAPUserVisitor(syncConfiguration); + ldapDao.search(visitor, userDN, userFilter, syncConfiguration.getUserAttributes(), ctx); + + List<LDAPUser> ldapUserList = visitor.getLdapUserList(); + if(ldapUserList.size() == 1) { + ldapUser = ldapUserList.get(0); + Attributes userAttrs = ldapUser.getAttributes(); + String user = getAttributeValue(userAttrs.get(uidProp)); + identity = findIdentyByLdapAuthentication(user, errors); + if(identity != null) { + dnToIdentityKeyMap.put(userDN, ldapUser); + } + } + } + return ldapUser; + } + + @Override public void doSyncSingleUser(Identity ident){ LdapContext ctx = bindSystem(); if (ctx == null) { - logError("could not bind to ldap", null); + log.error("could not bind to ldap", null); } - String userDN = searchUserDN(ident.getName(), ctx); + String userDN = ldapDao.searchUserDN(ident.getName(), ctx); final List<Attributes> ldapUserList = new ArrayList<Attributes>(); // TODO: use userDN instead of filter to get users attribs - searchInLdap(new LdapVisitor() { + ldapDao.searchInLdap(new LDAPVisitor() { + @Override public void visit(SearchResult result) { Attributes resAttribs = result.getAttributes(); - logDebug(" found : " + resAttribs.size() + " attributes in result " + result.getName()); + log.debug(" found : " + resAttribs.size() + " attributes in result " + result.getName()); ldapUserList.add(resAttribs); } - }, userDN, LDAPLoginModule.getUserAttrs(), ctx); + }, userDN, syncConfiguration.getUserAttributes(), ctx); Attributes attrs = ldapUserList.get(0); Map<String, String> olatProToSync = prepareUserPropertyForSync(attrs, ident); @@ -1101,6 +1212,7 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve /** * @see org.olat.ldap.LDAPLoginManager#getLastSyncDate() */ + @Override public Date getLastSyncDate() { return lastSyncDate; } @@ -1112,13 +1224,16 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve */ private void enableSSL(Hashtable<String, String> env) { env.put(Context.SECURITY_PROTOCOL, "ssl"); - System.setProperty("javax.net.ssl.trustStore", LDAPLoginModule.getTrustStoreLocation()); + if(StringHelper.containsNonWhitespace(ldapLoginModule.getTrustStoreLocation())) { + System.setProperty("javax.net.ssl.trustStore", ldapLoginModule.getTrustStoreLocation()); + } } /** * Acquire lock for administration jobs * */ + @Override public synchronized boolean acquireSyncLock(){ if(batchSyncIsRunning){ return false; @@ -1131,12 +1246,9 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve * Release lock for administration jobs * */ + @Override public synchronized void freeSyncLock() { - batchSyncIsRunning=false; - } - - public interface LdapVisitor { - public void visit(SearchResult searchResult) throws NamingException ; + batchSyncIsRunning = false; } /** @@ -1146,33 +1258,31 @@ public class LDAPLoginManagerImpl extends LDAPLoginManager implements GenericEve */ @Override public void removeFallBackAuthentications() { - if (LDAPLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()){ - BaseSecurity secMgr = BaseSecurityManager.getInstance(); - SecurityGroup ldapGroup = secMgr.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); + if (ldapLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()){ + SecurityGroup ldapGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); if (ldapGroup == null) { - logError("Error getting user from OLAT security group '" + LDAPConstants.SECURITY_GROUP_LDAP + "' : group does not exist", null); + log.error("Error getting user from OLAT security group '" + LDAPConstants.SECURITY_GROUP_LDAP + "' : group does not exist", null); } - List<Identity> ldapIdents = secMgr.getIdentitiesOfSecurityGroup(ldapGroup); - logInfo("found " + ldapIdents.size() + " identies in ldap security group"); + List<Identity> ldapIdents = securityManager.getIdentitiesOfSecurityGroup(ldapGroup); + log.info("found " + ldapIdents.size() + " identies in ldap security group"); int count=0; for (Identity identity : ldapIdents) { - Authentication auth = secMgr.findAuthentication(identity, BaseSecurityModule.getDefaultAuthProviderIdentifier()); + Authentication auth = securityManager.findAuthentication(identity, BaseSecurityModule.getDefaultAuthProviderIdentifier()); if (auth!=null){ - secMgr.deleteAuthentication(auth); + securityManager.deleteAuthentication(auth); count++; } if (count % 20 == 0){ - DBFactory.getInstance().intermediateCommit(); + dbInstance.intermediateCommit(); } } - logInfo("removed cached authentications (fallback login provider: " + BaseSecurityModule.getDefaultAuthProviderIdentifier() + ") for " + count + " users."); + log.info("removed cached authentications (fallback login provider: " + BaseSecurityModule.getDefaultAuthProviderIdentifier() + ") for " + count + " users."); } } @Override public boolean isIdentityInLDAPSecGroup(Identity ident) { - BaseSecurity secMgr = BaseSecurityManager.getInstance(); - SecurityGroup ldapSecurityGroup = secMgr.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); - return ldapSecurityGroup != null && secMgr.isIdentityInSecurityGroup(ident, ldapSecurityGroup); + SecurityGroup ldapSecurityGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); + return ldapSecurityGroup != null && securityManager.isIdentityInSecurityGroup(ident, ldapSecurityGroup); } } \ No newline at end of file diff --git a/src/main/java/org/olat/ldap/manager/LDAPUserVisitor.java b/src/main/java/org/olat/ldap/manager/LDAPUserVisitor.java new file mode 100644 index 00000000000..c21cc0112df --- /dev/null +++ b/src/main/java/org/olat/ldap/manager/LDAPUserVisitor.java @@ -0,0 +1,119 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.ldap.manager; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import javax.naming.NamingEnumeration; +import javax.naming.NamingException; +import javax.naming.directory.Attribute; +import javax.naming.directory.Attributes; +import javax.naming.directory.SearchResult; + +import org.olat.core.logging.OLog; +import org.olat.core.logging.Tracing; +import org.olat.core.util.StringHelper; +import org.olat.ldap.LDAPSyncConfiguration; +import org.olat.ldap.model.LDAPUser; + +/** + * + * Initial date: 24.11.2014<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class LDAPUserVisitor implements LDAPVisitor { + + private static final OLog log = Tracing.createLoggerFor(LDAPUserVisitor.class); + + private final LDAPSyncConfiguration syncConfiguration; + private final List<LDAPUser> ldapUserList = new ArrayList<LDAPUser>(); + + public LDAPUserVisitor(LDAPSyncConfiguration syncConfiguration) { + this.syncConfiguration = syncConfiguration; + } + + public List<LDAPUser> getLdapUserList() { + return ldapUserList; + } + + @Override + public void visit(SearchResult searchResult) throws NamingException { + Attributes resAttribs = searchResult.getAttributes(); + String dn = searchResult.getNameInNamespace(); + + LDAPUser ldapUser = new LDAPUser(); + ldapUser.setDn(dn); + ldapUser.setAttributes(resAttribs); + ldapUser.setCoach(hasAttributeValue(resAttribs, syncConfiguration.getCoachRoleAttribute(), syncConfiguration.getCoachRoleValue())); + ldapUser.setAuthor(hasAttributeValue(resAttribs, syncConfiguration.getAuthorRoleAttribute(), syncConfiguration.getAuthorRoleValue())); + ldapUser.setUserManager(hasAttributeValue(resAttribs, syncConfiguration.getUserManagerRoleAttribute(), syncConfiguration.getUserManagerRoleValue())); + ldapUser.setGroupManager(hasAttributeValue(resAttribs, syncConfiguration.getGroupManagerRoleAttribute(), syncConfiguration.getGroupManagerRoleValue())); + ldapUser.setQpoolManager(hasAttributeValue(resAttribs, syncConfiguration.getQpoolManagerRoleAttribute(), syncConfiguration.getQpoolManagerRoleValue())); + ldapUser.setLearningResourceManager(hasAttributeValue(resAttribs, syncConfiguration.getLearningResourceManagerRoleAttribute(), syncConfiguration.getLearningResourceManagerRoleValue())); + + List<String> groupList = Collections.emptyList(); + if(StringHelper.containsNonWhitespace(syncConfiguration.getGroupAttribute())) { + try { + Attribute groupAttr = resAttribs.get(syncConfiguration.getGroupAttribute()); + if(groupAttr != null && groupAttr.get() instanceof String) { + String groupString = (String)groupAttr.get(); + String[] groupArr = groupString.split(syncConfiguration.getGroupAttributeSeparator()); + groupList = new ArrayList<>(groupArr.length); + for(String group:groupArr) { + groupList.add(group); + } + } + } catch (Exception e) { + log.error(""); + } + } + ldapUser.setGroupIds(groupList); + ldapUserList.add(ldapUser); + } + + private boolean hasAttributeValue(Attributes resAttribs, String attribute, String value) { + boolean hasAttributeValue = false; + if(StringHelper.containsNonWhitespace(attribute) && StringHelper.containsNonWhitespace(value)) { + try { + Attribute attr = resAttribs.get(attribute); + if(attr != null) { + if(attr.size() == 1) { + if(value.equals(attr.get())) { + hasAttributeValue = true; + } + } else if(attr.size() > 1) { + for(NamingEnumeration<?> valuEnum=attr.getAll(); valuEnum.hasMoreElements(); ) { + Object nextValue = valuEnum.next(); + if(value.equals(nextValue)) { + hasAttributeValue = true; + } + } + } + } + } catch (Exception e) { + log.error(""); + } + } + return hasAttributeValue; + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/ldap/manager/LDAPVisitor.java b/src/main/java/org/olat/ldap/manager/LDAPVisitor.java new file mode 100644 index 00000000000..289d0b7a3dd --- /dev/null +++ b/src/main/java/org/olat/ldap/manager/LDAPVisitor.java @@ -0,0 +1,35 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.ldap.manager; + +import javax.naming.NamingException; +import javax.naming.directory.SearchResult; + +/** + * + * Initial date: 24.11.2014<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public interface LDAPVisitor { + + public void visit(SearchResult searchResult) throws NamingException; + +} diff --git a/src/main/java/org/olat/ldap/model/LDAPGroup.java b/src/main/java/org/olat/ldap/model/LDAPGroup.java new file mode 100644 index 00000000000..038b58ccdd7 --- /dev/null +++ b/src/main/java/org/olat/ldap/model/LDAPGroup.java @@ -0,0 +1,82 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.ldap.model; + +import java.util.ArrayList; +import java.util.List; + +/** + * + * Initial date: 24.11.2014<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class LDAPGroup { + + private String commonName; + private List<String> members; + + public LDAPGroup() { + // + } + + public LDAPGroup(String commonName) { + this.commonName = commonName; + this.members = new ArrayList<>(); + } + + public String getCommonName() { + return commonName; + } + + public void setCommonName(String commonName) { + this.commonName = commonName; + } + + public List<String> getMembers() { + return members; + } + + public void setMembers(List<String> members) { + this.members = members; + } + + @Override + public int hashCode() { + return commonName == null ? 987234895 : commonName.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if(this == obj) { + return true; + } + if(obj instanceof LDAPGroup) { + LDAPGroup group = (LDAPGroup)obj; + return commonName != null && commonName.equals(group.getCommonName()); + } + return false; + } + + @Override + public String toString() { + return "ldapGroup[cn=" + commonName + "]" + super.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/org/olat/ldap/model/LDAPUser.java b/src/main/java/org/olat/ldap/model/LDAPUser.java new file mode 100644 index 00000000000..a227f5d6b16 --- /dev/null +++ b/src/main/java/org/olat/ldap/model/LDAPUser.java @@ -0,0 +1,148 @@ +/** + * <a href="http://www.openolat.org"> + * OpenOLAT - Online Learning and Training</a><br> + * <p> + * Licensed under the Apache License, Version 2.0 (the "License"); <br> + * you may not use this file except in compliance with the License.<br> + * You may obtain a copy of the License at the + * <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a> + * <p> + * Unless required by applicable law or agreed to in writing,<br> + * software distributed under the License is distributed on an "AS IS" BASIS, <br> + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> + * See the License for the specific language governing permissions and <br> + * limitations under the License. + * <p> + * Initial code contributed and copyrighted by<br> + * frentix GmbH, http://www.frentix.com + * <p> + */ +package org.olat.ldap.model; + +import java.util.List; + +import javax.naming.directory.Attributes; + +import org.olat.core.id.Identity; + +/** + * + * Initial date: 24.11.2014<br> + * @author srosse, stephane.rosse@frentix.com, http://www.frentix.com + * + */ +public class LDAPUser { + + private String dn; + private boolean coach; + private boolean author; + private boolean userManager; + private boolean groupManager; + private boolean qpoolManager; + private boolean learningResourceManager; + private List<String> groupIds; + private Attributes attributes; + private Identity cachedIdentity; + + public String getDn() { + return dn; + } + + public void setDn(String dn) { + this.dn = dn; + } + + public Attributes getAttributes() { + return attributes; + } + + public void setAttributes(Attributes attributes) { + this.attributes = attributes; + } + + public boolean isCoach() { + return coach; + } + + public void setCoach(boolean coach) { + this.coach = coach; + } + + public boolean isAuthor() { + return author; + } + + public void setAuthor(boolean author) { + this.author = author; + } + + public boolean isUserManager() { + return userManager; + } + + public void setUserManager(boolean userManager) { + this.userManager = userManager; + } + + public boolean isGroupManager() { + return groupManager; + } + + public void setGroupManager(boolean groupManager) { + this.groupManager = groupManager; + } + + public boolean isQpoolManager() { + return qpoolManager; + } + + public void setQpoolManager(boolean qpoolManager) { + this.qpoolManager = qpoolManager; + } + + public boolean isLearningResourceManager() { + return learningResourceManager; + } + + public void setLearningResourceManager(boolean learningResourceManager) { + this.learningResourceManager = learningResourceManager; + } + + public List<String> getGroupIds() { + return groupIds; + } + + public void setGroupIds(List<String> groupIds) { + this.groupIds = groupIds; + } + + public Identity getCachedIdentity() { + return cachedIdentity; + } + + public void setCachedIdentity(Identity cachedIdentity) { + this.cachedIdentity = cachedIdentity; + } + + @Override + public int hashCode() { + return dn == null ? 76789 : dn.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if(this == obj) { + return true; + } + if(obj instanceof LDAPUser) { + LDAPUser user = (LDAPUser)obj; + return dn != null && dn.equals(user.getDn()); + } + return false; + } + + @Override + public String toString() { + return "ldapUser[dn=" + dn + "]" + super.toString(); + } +} diff --git a/src/main/java/org/olat/ldap/ui/DeletStep01.java b/src/main/java/org/olat/ldap/ui/DeletStep01.java index 8f1d301125c..c8ce6d84cd2 100644 --- a/src/main/java/org/olat/ldap/ui/DeletStep01.java +++ b/src/main/java/org/olat/ldap/ui/DeletStep01.java @@ -44,9 +44,10 @@ import org.olat.core.gui.control.generic.wizard.StepsEvent; import org.olat.core.gui.control.generic.wizard.StepsRunContext; import org.olat.core.id.Identity; import org.olat.ldap.LDAPConstants; -import org.olat.ldap.LDAPLoginModule; +import org.olat.ldap.LDAPSyncConfiguration; import org.olat.user.UserManager; import org.olat.user.propertyhandlers.UserPropertyHandler; +import org.springframework.beans.factory.annotation.Autowired; /** * Description:<br> @@ -84,6 +85,9 @@ public class DeletStep01 extends BasicStep { } private final class DeletStepForm01 extends StepFormBasicController { + + @Autowired + private LDAPSyncConfiguration syncConfiguration; public DeletStepForm01(UserRequest ureq, WindowControl control, Form rootForm, StepsRunContext runContext) { super(ureq, control, rootForm, runContext, LAYOUT_VERTICAL, null); @@ -113,7 +117,7 @@ public class DeletStep01 extends BasicStep { return; } - Map<String, String> reqProbertyMap = new HashMap<String,String>(LDAPLoginModule.getUserAttributeMapper()); + Map<String, String> reqProbertyMap = new HashMap<String,String>(syncConfiguration.getUserAttributeMap()); Collection<String> reqProberty = reqProbertyMap.values(); reqProberty.remove(LDAPConstants.LDAP_USER_IDENTIFYER); diff --git a/src/main/java/org/olat/ldap/ui/LDAPAdminController.java b/src/main/java/org/olat/ldap/ui/LDAPAdminController.java index a9dfd260669..c23832276cd 100644 --- a/src/main/java/org/olat/ldap/ui/LDAPAdminController.java +++ b/src/main/java/org/olat/ldap/ui/LDAPAdminController.java @@ -52,6 +52,7 @@ import org.olat.ldap.LDAPError; import org.olat.ldap.LDAPEvent; import org.olat.ldap.LDAPLoginManager; import org.olat.ldap.LDAPLoginModule; +import org.springframework.beans.factory.annotation.Autowired; /** * Description:<br> @@ -79,6 +80,9 @@ public class LDAPAdminController extends BasicController implements GenericEvent private CloseableCalloutWindowController calloutCtr; private Link syncOneUserLink; private Link removeFallBackAuthsLink; + + @Autowired + private LDAPLoginModule ldapLoginModule; public LDAPAdminController(UserRequest ureq, WindowControl control) { super(ureq, control); @@ -94,7 +98,7 @@ public class LDAPAdminController extends BasicController implements GenericEvent // syncOneUserLink = LinkFactory.createButton("one.user.sync.button.start", ldapAdminVC, this); // remove olat-fallback authentications for ldap-users, see FXOLAT-284 - if (LDAPLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()){ + if (ldapLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()){ removeFallBackAuthsLink = LinkFactory.createButton("remove.fallback.auth", ldapAdminVC, this); } // Create start delete User link diff --git a/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java b/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java index 0cfde5a48bd..c6b8a11631e 100644 --- a/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java +++ b/src/main/java/org/olat/ldap/ui/LDAPAuthenticationController.java @@ -61,6 +61,7 @@ import org.olat.registration.DisclaimerController; import org.olat.registration.PwChangeController; import org.olat.registration.RegistrationManager; import org.olat.user.UserModule; +import org.springframework.beans.factory.annotation.Autowired; public class LDAPAuthenticationController extends AuthenticationController implements Activateable2 { public static final String PROVIDER_LDAP = "LDAP"; @@ -76,6 +77,9 @@ public class LDAPAuthenticationController extends AuthenticationController imple private CloseableModalController cmc; private final OLATAuthManager olatAuthenticationSpi; + + @Autowired + private LDAPLoginModule ldapLoginModule; public LDAPAuthenticationController(UserRequest ureq, WindowControl control) { // use fallback translator to login and registration package @@ -85,7 +89,7 @@ public class LDAPAuthenticationController extends AuthenticationController imple loginComp = createVelocityContainer("ldaplogin"); - if(UserModule.isPwdchangeallowed(null) && LDAPLoginModule.isPropagatePasswordChangedOnLdapServer()) { + if(UserModule.isPwdchangeallowed(null) && ldapLoginModule.isPropagatePasswordChangedOnLdapServer()) { pwLink = LinkFactory.createLink("_olat_login_change_pwd", "menu.pw", loginComp, this); pwLink.setElementCssClass("o_login_pwd"); } @@ -114,7 +118,7 @@ public class LDAPAuthenticationController extends AuthenticationController imple protected void openChangePassword(UserRequest ureq, String initialEmail) { // double-check if allowed first - if (!UserModule.isPwdchangeallowed(ureq.getIdentity()) || !LDAPLoginModule.isPropagatePasswordChangedOnLdapServer()) { + if (!UserModule.isPwdchangeallowed(ureq.getIdentity()) || !ldapLoginModule.isPropagatePasswordChangedOnLdapServer()) { throw new OLATSecurityException("chose password to be changed, but disallowed by config"); } @@ -163,7 +167,7 @@ public class LDAPAuthenticationController extends AuthenticationController imple provider = LDAPAuthenticationController.PROVIDER_LDAP; } else { // try fallback to OLAT provider if configured - if (LDAPLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()) { + if (ldapLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()) { authenticatedIdentity = olatAuthenticationSpi.authenticate(null, login, pass); } if (authenticatedIdentity != null) { @@ -276,7 +280,7 @@ public class LDAPAuthenticationController extends AuthenticationController imple } } // Add or update an OLAT authentication token for this user if configured in the module - if (identity != null && LDAPLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()) { + if (identity != null && ldapModule.isCacheLDAPPwdAsOLATPwdOnLogin()) { CoreSpringFactory.getImpl(OLATAuthManager.class).changeOlatPassword(identity, identity, pwd); } return identity; diff --git a/src/main/java/org/olat/login/auth/OLATAuthManager.java b/src/main/java/org/olat/login/auth/OLATAuthManager.java index 9551db0fa5b..a586bdf5549 100644 --- a/src/main/java/org/olat/login/auth/OLATAuthManager.java +++ b/src/main/java/org/olat/login/auth/OLATAuthManager.java @@ -87,6 +87,8 @@ public class OLATAuthManager extends BasicManager implements AuthenticationSPI { private MailManager mailManager; @Autowired private WebDAVAuthManager webDAVAuthManager; + @Autowired + private LDAPLoginModule ldapLoginModule; /** * @@ -190,14 +192,14 @@ public class OLATAuthManager extends BasicManager implements AuthenticationSPI { Authentication ldapAuth = securityManager.findAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP); if(ldapAuth != null) { - if(LDAPLoginModule.isPropagatePasswordChangedOnLdapServer()) { + if(ldapLoginModule.isPropagatePasswordChangedOnLdapServer()) { LDAPError ldapError = new LDAPError(); LDAPLoginManager ldapLoginManager = (LDAPLoginManager) CoreSpringFactory.getBean("org.olat.ldap.LDAPLoginManager"); ldapLoginManager.changePassword(identity, newPwd, ldapError); log.audit(doer.getName() + " change the password on the LDAP server for identity: " + identity.getName()); allOk = ldapError.isEmpty(); - if(allOk && LDAPLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()) { + if(allOk && ldapLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()) { allOk &= changeOlatPassword(doer, identity, newPwd); } } diff --git a/src/main/java/org/olat/user/ChangePasswordController.java b/src/main/java/org/olat/user/ChangePasswordController.java index 3d83f89cd72..efb9e31bc9a 100644 --- a/src/main/java/org/olat/user/ChangePasswordController.java +++ b/src/main/java/org/olat/user/ChangePasswordController.java @@ -30,10 +30,8 @@ import java.util.List; import org.olat.basesecurity.Authentication; import org.olat.basesecurity.BaseSecurity; -import org.olat.basesecurity.BaseSecurityManager; import org.olat.basesecurity.BaseSecurityModule; import org.olat.basesecurity.Constants; -import org.olat.core.CoreSpringFactory; import org.olat.core.gui.UserRequest; import org.olat.core.gui.components.Component; import org.olat.core.gui.components.velocity.VelocityContainer; @@ -50,6 +48,7 @@ import org.olat.ldap.LDAPLoginModule; import org.olat.ldap.ui.LDAPAuthenticationController; import org.olat.login.SupportsAfterLoginInterceptor; import org.olat.login.auth.OLATAuthManager; +import org.springframework.beans.factory.annotation.Autowired; /** @@ -73,7 +72,13 @@ public class ChangePasswordController extends BasicController implements Support private VelocityContainer myContent; private ChangePasswordForm chPwdForm; - private final OLATAuthManager olatAuthenticationSpi; + + @Autowired + private LDAPLoginModule ldapLoginModule; + @Autowired + private OLATAuthManager olatAuthenticationSpi; + @Autowired + private BaseSecurity securityManager; /** * @param ureq @@ -81,8 +86,6 @@ public class ChangePasswordController extends BasicController implements Support */ public ChangePasswordController(UserRequest ureq, WindowControl wControl) { super(ureq, wControl); - - olatAuthenticationSpi = CoreSpringFactory.getImpl(OLATAuthManager.class); // if a user is not allowed to change his/her own password, say it here if (!UserModule.isPwdchangeallowed(ureq.getIdentity())) { @@ -92,9 +95,8 @@ public class ChangePasswordController extends BasicController implements Support putInitialPanel(simpleMsg.getInitialComponent()); return; } - - BaseSecurity mgr = BaseSecurityManager.getInstance(); - if (!mgr.isIdentityPermittedOnResourceable( + + if (!securityManager.isIdentityPermittedOnResourceable( ureq.getIdentity(), Constants.PERMISSION_ACCESS, OresHelper.lookupType(this.getClass()))) { @@ -140,11 +142,11 @@ public class ChangePasswordController extends BasicController implements Support String oldPwd = chPwdForm.getOldPasswordValue(); Identity provenIdent = null; - if (BaseSecurityManager.getInstance().findAuthentication(ureq.getIdentity(), LDAPAuthenticationController.PROVIDER_LDAP) != null) { + if (securityManager.findAuthentication(ureq.getIdentity(), LDAPAuthenticationController.PROVIDER_LDAP) != null) { LDAPError ldapError = new LDAPError(); //fallback to OLAT if enabled happen automatically in LDAPAuthenticationController provenIdent = LDAPAuthenticationController.authenticate(ureq.getIdentity().getName(), oldPwd, ldapError); - } else if(BaseSecurityManager.getInstance().findAuthentication(ureq.getIdentity(), BaseSecurityModule.getDefaultAuthProviderIdentifier()) != null) { + } else if(securityManager.findAuthentication(ureq.getIdentity(), BaseSecurityModule.getDefaultAuthProviderIdentifier()) != null) { provenIdent = olatAuthenticationSpi.authenticate(ureq.getIdentity(), ureq.getIdentity().getName(), oldPwd); } @@ -173,14 +175,14 @@ public class ChangePasswordController extends BasicController implements Support private void exposePwdProviders(Identity identity) { // check if user has OLAT provider - List<Authentication> authentications = BaseSecurityManager.getInstance().getAuthentications(identity); + List<Authentication> authentications = securityManager.getAuthentications(identity); Iterator<Authentication> iter = authentications.iterator(); while (iter.hasNext()) { myContent.contextPut("provider_" + (iter.next()).getProvider(), Boolean.TRUE); } //LDAP Module propagate changes to password - if(LDAPLoginModule.isPropagatePasswordChangedOnLdapServer()) { + if(ldapLoginModule.isPropagatePasswordChangedOnLdapServer()) { myContent.contextPut("provider_LDAP_pwdchange", Boolean.TRUE); } } diff --git a/src/main/resources/serviceconfig/olat.properties b/src/main/resources/serviceconfig/olat.properties index bf830d37744..0dcbfb022ab 100644 --- a/src/main/resources/serviceconfig/olat.properties +++ b/src/main/resources/serviceconfig/olat.properties @@ -891,6 +891,42 @@ ldap.attrib.sync.once.olatkey1= ldap.attrib.sync.once.olatkey2= ldap.attrib.sync.once.olatkey3= +# sync group from a LDAP groups +ldap.ldapGroupBases= +ldap.ldapGroupBases.values=ou=groups,dc=openproject,dc=org +# the object class of groups +ldap.ldapGroupObjectClass=groupOfNames +ldap.ldapGroupFilter=(objectClass=${ldap.ldapGroupObjectClass}) +# the marker attribute for coaches +ldap.coachRoleAttribute=employeeType +ldap.coachRoleValue=coach +# sync group from a list of separated ids saved in an attribute of +# the DLAP user +ldap.user.groupAttribute= +ldap.user.groupAttribute.values=o +ldap.user.groupAttributeSeparator=, + +# sync authors +ldap.authorsGroupBases= +ldap.authorRoleAttribute= +ldap.authorRoleValue= +# sync user managers +ldap.userManagersGroupBases= +ldap.userManagerRoleAttribute= +ldap.userManagerRoleValue= +# sync group managers +ldap.groupManagersGroupBases= +ldap.groupManagerRoleAttribute= +ldap.groupManagerRoleValue= +# sync question pool managers +ldap.qpoolManagersGroupBases= +ldap.qpoolManagerRoleAttribute= +ldap.qpoolManagerRoleValue= +# sync learning resource managers +ldap.learningResourceManagersGroupBase= +ldap.learningResourceManagerRoleAttribute= +ldap.learningResourceManagerRoleValue= + ##### # Build properties ##### diff --git a/src/test/java/org/olat/ldap/LDAPLoginTest.java b/src/test/java/org/olat/ldap/LDAPLoginTest.java index f9cd4b7e6f0..58396671309 100644 --- a/src/test/java/org/olat/ldap/LDAPLoginTest.java +++ b/src/test/java/org/olat/ldap/LDAPLoginTest.java @@ -45,8 +45,11 @@ import org.olat.core.CoreSpringFactory; import org.olat.core.commons.persistence.DBFactory; import org.olat.core.id.Identity; import org.olat.core.id.User; +import org.olat.ldap.manager.LDAPDAO; +import org.olat.ldap.model.LDAPUser; import org.olat.test.OlatTestCase; import org.olat.user.UserManager; +import org.springframework.beans.factory.annotation.Autowired; /** @@ -61,10 +64,16 @@ import org.olat.user.UserManager; */ public class LDAPLoginTest extends OlatTestCase { + @Autowired + private LDAPDAO ldapDao; + @Autowired + private LDAPLoginModule ldapLoginModule; + @Autowired + private LDAPSyncConfiguration syncConfiguration; @Test public void testSystemBind() { - Assume.assumeTrue(LDAPLoginModule.isLDAPEnabled()); + Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); //edit olatextconfig.xml for testing LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); @@ -74,7 +83,7 @@ public class LDAPLoginTest extends OlatTestCase { @Test public void testCreateUser() { - Assume.assumeTrue(LDAPLoginModule.isLDAPEnabled()); + Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); BaseSecurity securityManager = BaseSecurityManager.getInstance(); @@ -82,7 +91,7 @@ public class LDAPLoginTest extends OlatTestCase { String userPW = "olat"; LDAPError errors = new LDAPError(); - boolean usersSyncedAtStartup = LDAPLoginModule.isLdapSyncOnStartup(); + boolean usersSyncedAtStartup = ldapLoginModule.isLdapSyncOnStartup(); //user should not exits in OLAT when not synced during startup assertEquals(usersSyncedAtStartup, (securityManager.findIdentityByName(uid) != null)); // bind user @@ -99,7 +108,7 @@ public class LDAPLoginTest extends OlatTestCase { @Test public void testUserBind() throws NamingException { - Assume.assumeTrue(LDAPLoginModule.isLDAPEnabled()); + Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); LDAPError errors = new LDAPError(); @@ -131,7 +140,7 @@ public class LDAPLoginTest extends OlatTestCase { @Test public void testCheckUser() { - Assume.assumeTrue(LDAPLoginModule.isLDAPEnabled()); + Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); LDAPError errors = new LDAPError(); @@ -156,7 +165,7 @@ public class LDAPLoginTest extends OlatTestCase { @Test public void testCreateChangedAttrMap() { - Assume.assumeTrue(LDAPLoginModule.isLDAPEnabled()); + Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); // simulate closed session (user adding from startup job) DBFactory.getInstance().intermediateCommit(); @@ -167,7 +176,7 @@ public class LDAPLoginTest extends OlatTestCase { String pwd = "olat"; LDAPError errors = new LDAPError(); - boolean usersSyncedAtStartup = LDAPLoginModule.isLdapSyncOnStartup(); + boolean usersSyncedAtStartup = ldapLoginModule.isLdapSyncOnStartup(); if (usersSyncedAtStartup) { try { //create user but with different attributes - must fail since user already exists @@ -215,7 +224,7 @@ public class LDAPLoginTest extends OlatTestCase { @Test public void testSyncUser(){ - Assume.assumeTrue(LDAPLoginModule.isLDAPEnabled()); + Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); BaseSecurity securityManager = BaseSecurityManager.getInstance(); @@ -239,7 +248,7 @@ public class LDAPLoginTest extends OlatTestCase { @Test public void testIdentityDeletedInLDAP(){ - Assume.assumeTrue(LDAPLoginModule.isLDAPEnabled()); + Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); LDAPLoginManager ldapManager = (LDAPLoginManager) CoreSpringFactory.getBean(LDAPLoginManager.class); BaseSecurity securityManager = BaseSecurityManager.getInstance(); @@ -286,10 +295,10 @@ public class LDAPLoginTest extends OlatTestCase { @Test public void testCronSync() throws Exception { - Assume.assumeTrue(LDAPLoginModule.isLDAPEnabled()); + Assume.assumeTrue(ldapLoginModule.isLDAPEnabled()); LdapContext ctx; - List<Attributes> ldapUserList; + List<LDAPUser> ldapUserList; List<Attributes> newLdapUserList; Map<Identity, Map<String, String>> changedMapIdenityMap; List<Identity> deletedUserList; @@ -301,12 +310,12 @@ public class LDAPLoginTest extends OlatTestCase { //find user changed after 2010,01,09,00,00 ctx = ldapMan.bindSystem(); Date syncDate = new Date(110,00,10,00,00); - ldapUserList = ldapMan.getUserAttributesModifiedSince(syncDate, ctx); + ldapUserList = ldapDao.getUserAttributesModifiedSince(syncDate, ctx); assertEquals(1, ldapUserList.size()); //find all users syncDate = null; - ldapUserList = ldapMan.getUserAttributesModifiedSince(syncDate, ctx); + ldapUserList = ldapDao.getUserAttributesModifiedSince(syncDate, ctx); assertEquals(6, ldapUserList.size()); @@ -316,15 +325,16 @@ public class LDAPLoginTest extends OlatTestCase { newLdapUserList = new LinkedList<Attributes>(); changedMapIdenityMap = new HashMap<Identity, Map<String, String>>(); for (int i = 0; i < ldapUserList.size(); i++) { - user = getAttributeValue(ldapUserList.get(i).get(LDAPLoginModule.mapOlatPropertyToLdapAttribute("userID"))); + Attributes userAttrs = ldapUserList.get(i).getAttributes(); + user = getAttributeValue(userAttrs.get(syncConfiguration.getOlatPropertyToLdapAttribute("userID"))); idenity = ldapMan.findIdentyByLdapAuthentication(user, errors); if (idenity != null) { - changedAttrMap = ldapMan.prepareUserPropertyForSync(ldapUserList.get(i), idenity); + changedAttrMap = ldapMan.prepareUserPropertyForSync(userAttrs, idenity); if(changedAttrMap!= null) changedMapIdenityMap.put(idenity, changedAttrMap); } else { if(errors.isEmpty()) { - String[] reqAttrs = LDAPLoginModule.checkReqAttr(ldapUserList.get(i)); - if(reqAttrs==null) newLdapUserList.add(ldapUserList.get(i)); + String[] reqAttrs = syncConfiguration.checkRequestAttributes(userAttrs); + if(reqAttrs==null) newLdapUserList.add(userAttrs); else System.out.println("Cannot create User " + user + " required Attributes are missing"); } else System.out.println(errors.get()); -- GitLab