/** * <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.Arrays; import java.util.Date; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import javax.naming.AuthenticationException; import javax.naming.Context; import javax.naming.NamingEnumeration; import javax.naming.NamingException; 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.SearchResult; import javax.naming.ldap.Control; import javax.naming.ldap.InitialLdapContext; import javax.naming.ldap.LdapContext; 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.BaseSecurityModule; import org.olat.basesecurity.Constants; import org.olat.basesecurity.GroupRoles; import org.olat.basesecurity.SecurityGroup; import org.olat.core.CoreSpringFactory; 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.FrameworkStartedEvent; import org.olat.core.util.event.FrameworkStartupEventChannel; 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.login.auth.OLATAuthManager; 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. * The synching is done only on node 1 of a cluster. * <p> * LDAPLoginMangerImpl * <p> * * @author Maurus Rohrer */ @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 boolean batchSyncIsRunning = false; private static Date lastSyncDate = null; // first sync is always a full sync private Coordinator coordinator; private TaskExecutorManager taskExecutorManager; @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; @Autowired private BusinessGroupService businessGroupService; @Autowired private BusinessGroupRelationDAO businessGroupRelationDao; @Autowired public LDAPLoginManagerImpl(CoordinatorManager coordinatorManager, TaskExecutorManager taskExecutorManager) { this.coordinator = coordinatorManager.getCoordinator(); this.taskExecutorManager = taskExecutorManager; coordinator.getEventBus().registerFor(this, null, ldapSyncLockOres); FrameworkStartupEventChannel.registerForStartupEvent(this); } @Override public void event(Event event) { if(event instanceof LDAPEvent) { if(LDAPEvent.SYNCHING.equals(event.getCommand())) { batchSyncIsRunning = true; } else if(LDAPEvent.SYNCHING_ENDED.equals(event.getCommand())) { batchSyncIsRunning = false; lastSyncDate = ((LDAPEvent)event).getTimestamp(); } else if(LDAPEvent.DO_SYNCHING.equals(event.getCommand())) { doHandleBatchSync(false); } else if(LDAPEvent.DO_FULL_SYNCHING.equals(event.getCommand())) { doHandleBatchSync(true); } } else if(event instanceof FrameworkStartedEvent) { try { init(); } catch (Exception e) { log.error("", e); } } } private void init() { if(ldapLoginModule.isLDAPEnabled()) { if (bindSystem() == null) { // don't disable ldap, maybe just a temporary problem, but still report // problem in logfile log.error("LDAP connection test failed during module initialization, edit config or contact network administrator"); } else { log.info("LDAP login is enabled"); } // Start LDAP cron sync job if (ldapLoginModule.isLdapSyncCronSync()) { LDAPError errors = new LDAPError(); if (doBatchSync(errors, true)) { log.info("LDAP start sync: users synced"); } else { log.warn("LDAP start sync error: " + errors.get()); } } else { log.info("LDAP cron sync is disabled"); } } } private void doHandleBatchSync(final boolean full) { //fxdiff: also run on nodes != 1 as nodeid = tomcat-id in fx-environment // if(WebappHelper.getNodeId() != 1) return; Runnable batchSyncTask = new Runnable() { public void run() { LDAPError errors = new LDAPError(); doBatchSync(errors, full); } }; taskExecutorManager.execute(batchSyncTask); } /** * Connect to the LDAP server with System DN and Password * * 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 * * @throws NamingException */ public LdapContext bindSystem() { // 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.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()); } // check ssl if (ldapLoginModule.isSslEnabled()) { enableSSL(env); } try { InitialLdapContext ctx = new InitialLdapContext(env, new Control[]{}); ctx.getConnectControls(); return ctx; } catch (NamingException 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) { log.error("Exception when trying to bind system with DN::" + ldapLoginModule.getLdapSystemDN() + " and PW::" + ldapLoginModule.getLdapSystemPW() + " on URL::" + ldapLoginModule.getLdapUrl(), e); return null; } } /** * * Connect to LDAP with the User-Name and Password given as parameters * * 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) * @param pwd The users LDAP password (can't be null) * * @return After successful bind Attributes otherwise NULL * * @throws NamingException */ @Override public Attributes bindUser(String login, String pwd, LDAPError errors) { // get user name, password and attributes String ldapUrl = ldapLoginModule.getLdapUrl(); String[] userAttr = syncConfiguration.getUserAttributes(); if (login == null || pwd == null) { if (log.isDebug()) log.debug("Error when trying to bind user, missing username or password. Username::" + login + " pwd::" + pwd); errors.insert("Username and password must be selected"); return null; } LdapContext ctx = bindSystem(); if (ctx == null) { errors.insert("LDAP connection error"); return null; } String userDN = ldapDao.searchUserForLogin(login, ctx); if (userDN == null) { log.info("Error when trying to bind user with username::" + login + " - user not found on LDAP server" + (ldapLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin() ? ", trying with OLAT login provider" : "")); errors.insert("Username or password incorrect"); return null; } // Ok, so far so good, user exists. Now try to fetch attributes using the // users credentials Hashtable<String, String> env = new Hashtable<String, String>(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); env.put(Context.PROVIDER_URL, ldapUrl); 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.isSslEnabled()) { enableSSL(env); } try { Control[] connectCtls = new Control[]{}; LdapContext userBind = new InitialLdapContext(env, connectCtls); Attributes attributes = userBind.getAttributes(userDN, userAttr); userBind.close(); return attributes; } catch (AuthenticationException e) { log.info("Error when trying to bind user with username::" + login + " - invalid LDAP password"); errors.insert("Username or password incorrect"); return null; } catch (NamingException e) { log.error("NamingException when trying to get attributes after binding user with username::" + login, e); errors.insert("Username or password incorrect"); return null; } } @Override public Identity authenticate(String username, String pwd, LDAPError ldapError) { long start = System.nanoTime(); //authenticate against LDAP server Attributes attrs = bindUser(username, pwd, ldapError); long takes = System.nanoTime() - start; if(takes > LDAPLoginModule.WARNING_LIMIT) { log.warn("LDAP Authentication takes (ms): " + (takes / 1000000)); } if (ldapError.isEmpty() && attrs != null) { Identity identity = findIdentityByLdapAuthentication(attrs, ldapError); if (!ldapError.isEmpty()) { return null; } if (identity == null) { if(ldapLoginModule.isCreateUsersOnLogin()) { // User authenticated but not yet existing - create as new OLAT user createAndPersistUser(attrs); identity = findIdentityByLdapAuthentication(attrs, ldapError); } else { ldapError.insert("login.notauthenticated"); } } else { // User does already exist - just sync attributes Map<String, String> olatProToSync = prepareUserPropertyForSync(attrs, identity); if (olatProToSync != null) { syncUser(olatProToSync, identity); } } // Add or update an OLAT authentication token for this user if configured in the module if (identity != null && ldapLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()) { // there is no WEBDAV token but an HA1, the HA1 is linked to the OLAT one. CoreSpringFactory.getImpl(OLATAuthManager.class) .synchronizeOlatPasswordAndUsername(identity, identity, username, pwd); } return identity; } return null; } /** * Change the password on the LDAP server. * @see org.olat.ldap.LDAPLoginManager#changePassword(org.olat.core.id.Identity, java.lang.String, org.olat.ldap.LDAPError) */ @Override public boolean changePassword(Identity identity, String pwd, LDAPError errors) { String uid = identity.getName(); String ldapUserPasswordAttribute = syncConfiguration.getLdapUserPasswordAttribute(); try { LdapContext ctx = bindSystem(); String dn = ldapDao.searchUserDNByUid(uid, ctx); List<ModificationItem> modificationItemList = new ArrayList<>(); if(ldapLoginModule.isActiveDirectory()) { boolean resetLockoutTime = false; if(ldapLoginModule.isResetLockTimoutOnPasswordChange()) { String[] attrs = syncConfiguration.getUserAttributes(); List<String> attrList = new ArrayList<>(Arrays.asList(attrs)); attrList.add("lockoutTime"); attrs = attrList.toArray(new String[attrList.size()]); Attributes attributes = ctx.getAttributes(dn, attrs); Attribute lockoutTimeAttr = attributes.get("lockoutTime"); if(lockoutTimeAttr != null && lockoutTimeAttr.size() > 0) { Object lockoutTime = lockoutTimeAttr.get(); if(lockoutTime != null && !lockoutTime.equals("0")) { resetLockoutTime = true; } } } //active directory need the password enquoted and unicoded (but little-endian) String quotedPassword = "\"" + pwd + "\""; 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); } BasicAttribute userPasswordAttribute = new BasicAttribute(ldapUserPasswordAttribute, pwdArray ); modificationItemList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute)); if(resetLockoutTime) { BasicAttribute lockTimeoutAttribute = new BasicAttribute("lockoutTime", "0"); modificationItemList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE, lockTimeoutAttribute)); } } else { BasicAttribute userPasswordAttribute = new BasicAttribute(ldapUserPasswordAttribute, pwd); modificationItemList.add(new ModificationItem(DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute)); } ModificationItem[] modificationItems = modificationItemList.toArray(new ModificationItem[modificationItemList.size()]); ctx.modifyAttributes(dn, modificationItems); ctx.close(); return true; } catch (NamingException e) { log.error("NamingException when trying to change password with username::" + uid, e); errors.insert("Cannot change the password"); return false; } catch(Exception e) { log.error("Unexpected exception when trying to change password with username::" + uid, e); errors.insert("Cannot change the password"); return false; } } /** * Delete all Identities in List and removes them from LDAPSecurityGroup * * @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); dbInstance.intermediateCommit(); } } /** * Sync all OLATPropertys in Map of Identity * * @param olatPropertyMap Map of changed OLAT properties * (OLATProperty,LDAPValue) * @param identity Identity to sync */ @Override public void syncUser(Map<String, String> olatPropertyMap, Identity identity) { if (identity == 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 = syncConfiguration.getSyncOnlyOnCreateProperties(); if (syncOnlyOnCreateProperties != null) { for (String syncOnlyOnCreateKey : syncOnlyOnCreateProperties) { olatPropertyMap.remove(syncOnlyOnCreateKey); } } for(Map.Entry<String, String> keyValuePair : olatPropertyMap.entrySet()) { String propName = keyValuePair.getKey(); String value = keyValuePair.getValue(); if(value == null) { if(user.getProperty(propName, null) != null) { log.debug("removed property " + propName + " for identity " + identity); user.setProperty(propName, value); } } else { user.setProperty(propName, value); } } // Add static user properties from the configuration 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()); } } userManager.updateUser(user); } /** * Creates User in OLAT and ads user to LDAP securityGroup Required Attributes * have to be checked before this method. * * @param userAttributes Set of LDAP Attribute of User to be created */ @Override public Identity createAndPersistUser(Attributes userAttributes) { // Get and Check Config String[] reqAttrs = syncConfiguration.checkRequestAttributes(userAttributes); if (reqAttrs != null) { log.warn("Can not create and persist user, the following attributes are missing::" + ArrayUtils.toString(reqAttrs), null); return null; } String uid = getAttributeValue(userAttributes.get(syncConfiguration .getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER))); String email = getAttributeValue(userAttributes.get(syncConfiguration.getOlatPropertyToLdapAttribute(UserConstants.EMAIL))); // Lookup user if (securityManager.findIdentityByNameCaseInsensitive(uid) != null) { 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! log.error("Cannot try to lookup user " + uid + " by email with an invalid email::" + email, null); return null; } if (userManager.userExist(email) ) { 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<? extends Attribute> neAttr = userAttributes.getAll(); try { while (neAttr.hasMore()) { Attribute attr = neAttr.next(); String olatProperty = mapLdapAttributeToOlatProperty(attr.getID()); 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 = syncConfiguration.getStaticUserProperties(); if (staticProperties != null && staticProperties.size() > 0) { for (Entry<String, String> staticProperty : staticProperties.entrySet()) { user.setProperty(staticProperty.getKey(), staticProperty.getValue()); } } } catch (NamingException e) { 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 log.error("Unknown exception when trying to create and persist LDAP user with username::" + uid, e); return null; } // Create Identity Identity identity = securityManager.createAndPersistIdentityAndUser(uid, null, user, LDAPAuthenticationController.PROVIDER_LDAP, uid); // Add to SecurityGroup LDAP SecurityGroup secGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); securityManager.addIdentityToSecurityGroup(identity, secGroup); // Add to SecurityGroup OLATUSERS secGroup = securityManager.findSecurityGroupByName(Constants.GROUP_OLATUSERS); securityManager.addIdentityToSecurityGroup(identity, secGroup); log.info("Created LDAP user username::" + uid); return identity; } /** * Checks if LDAP properties are different then OLAT properties of a User. If * they are different a Map (OlatPropertyName,LDAPValue) is returned. * * @param attributes Set of LDAP Attribute of Identity * @param identity Identity to compare * * @return Map(OlatPropertyName,LDAPValue) of properties Identity, where * property has changed. NULL is returned it no attributes have to be synced */ @SuppressWarnings("unchecked") public Map<String, String> prepareUserPropertyForSync(Attributes attributes, Identity identity) { Map<String, String> olatPropertyMap = new HashMap<String, String>(); User user = identity.getUser(); NamingEnumeration<Attribute> neAttrs = (NamingEnumeration<Attribute>) attributes.getAll(); try { while (neAttrs.hasMore()) { Attribute attr = neAttrs.next(); String olatProperty = mapLdapAttributeToOlatProperty(attr.getID()); if(olatProperty == null) { continue; } String ldapValue = getAttributeValue(attr); String olatValue = user.getProperty(olatProperty, null); if (olatValue == null) { // new property or user ID (will always be null, pseudo property) olatPropertyMap.put(olatProperty, ldapValue); } else { if (ldapValue.compareTo(olatValue) != 0) { olatPropertyMap.put(olatProperty, ldapValue); } } } if (olatPropertyMap.size() == 1 && olatPropertyMap.get(LDAPConstants.LDAP_USER_IDENTIFYER) != null) { log.debug("propertymap for identity " + identity.getName() + " contains only userID, NOTHING TO SYNC!"); return null; } else { log.debug("propertymap for identity " + identity.getName() + " contains " + olatPropertyMap.size() + " items (" + olatPropertyMap.keySet() + ") to be synced later on"); return olatPropertyMap; } } catch (NamingException e) { log.error("NamingException when trying to prepare user properties for LDAP sync", e); return null; } } /** * Maps LDAP Attributes to the OLAT Property * * Configuration: LDAP Attributes Map = ldapContext.xml (property=userAttrs) * * @param attrID LDAP Attribute * @return OLAT Property */ private String mapLdapAttributeToOlatProperty(String attrID) { Map<String, String> userAttrMapper = syncConfiguration.getUserAttributeMap(); String olatProperty = userAttrMapper.get(attrID); return olatProperty; } /** * Extracts Value out of LDAP Attribute * * * @param attribute LDAP Naming Attribute * @return String value of Attribute, null on Exception * * @throws NamingException */ private 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; } } /** * Searches for Identity in OLAT. * * @param uid Name of Identity * @param errors LDAPError Object if user exits but not member of * LDAPSecurityGroup * * @return Identity if it's found and member of LDAPSecurityGroup, null * otherwise (if user exists but not managed by LDAP, error Object is * modified) */ @Override public Identity findIdentityByLdapAuthentication(Attributes attrs, LDAPError errors) { if(attrs == null) { errors.insert("findIdentyByLdapAuthentication: attrs::null"); return null; } String uid = getAttributeValue(attrs.get(syncConfiguration .getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER))); String token = getAttributeValue(attrs.get(syncConfiguration.getLdapUserLoginAttribute())); Identity identity = securityManager.findIdentityByNameCaseInsensitive(uid); if (identity == null) { return null; } else { SecurityGroup ldapGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); if (ldapGroup == 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)) { Authentication ldapAuth = securityManager.findAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP); if(ldapAuth == null) { //BUG Fixe: update the user and test if it has a ldap provider securityManager.createAndPersistAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP, token, null, null); } else if(StringHelper.containsNonWhitespace(token) && !token.equals(ldapAuth.getAuthusername())) { ldapAuth.setAuthusername(token); ldapAuth = securityManager.updateAuthentication(ldapAuth); } return identity; } else if (ldapLoginModule.isConvertExistingLocalUsersToLDAPUsers()) { // Add user to LDAP security group and add the ldap provider securityManager.createAndPersistAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP, token, null, null); securityManager.addIdentityToSecurityGroup(identity, ldapGroup); 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; } } } /** * * Creates list of all OLAT Users which have been deleted out of the LDAP * directory but still exits in OLAT * * 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 * @param ctx The LDAP system connection, if NULL or closed NamingExecpiton is * thrown * * @return Returns list of Identity from the user which have been deleted in * LDAP * * @throws NamingException */ public List<Identity> getIdentitysDeletedInLdap(LdapContext ctx) { if (ctx == null) return null; // Find all LDAP Users String userID = syncConfiguration.getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER); String userFilter = syncConfiguration.getLdapUserFilter(); final List<String> ldapList = new ArrayList<String>(); ldapDao.searchInLdap(new LDAPVisitor() { @Override public void visit(SearchResult result) throws NamingException { Attributes attrs = result.getAttributes(); NamingEnumeration<? extends Attribute> aEnum = attrs.getAll(); while (aEnum.hasMore()) { Attribute attr = aEnum.next(); // use lowercase username ldapList.add(attr.get().toString().toLowerCase()); } } }, (userFilter == null ? "" : userFilter), new String[] { userID }, ctx); if (ldapList.isEmpty()) { 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) { log.error("Error getting users from OLAT security group '" + LDAPConstants.SECURITY_GROUP_LDAP + "' : group does not exist", null); return null; } List<Identity> identityListToDelete = new ArrayList<Identity>(); List<Identity> olatListIdentity = securityManager.getIdentitiesOfSecurityGroup(ldapGroup); for (Identity ida:olatListIdentity) { // compare usernames with lowercase if (!ldapList.contains(ida.getName().toLowerCase())) { identityListToDelete.add(ida); } } return identityListToDelete; } /** * Execute Batch Sync. Will update all Attributes of LDAP users in OLAt, create new users and delete users in OLAT. * Can be configured in ldapContext.xml * * @param LDAPError * */ @Override public boolean doBatchSync(LDAPError errors, boolean full) { //fxdiff: also run on nodes != 1 as nodeid = tomcat-id in fx-environment // if(WebappHelper.getNodeId() != 1) { // log.warn("Sync happens only on node 1", null); // return false; // } // o_clusterNOK // Synchronize on class so that only one thread can read the // batchSyncIsRunning flag Only this read operation is synchronized to not // block the whole execution of the do BatchSync method. The method is used // in automatic cron scheduler job and also in GUI controllers that can't // wait for the concurrent running request to finish first, an immediate // feedback about the concurrent job is needed. -> only synchronize on the // property read. synchronized (LDAPLoginManagerImpl.class) { if (batchSyncIsRunning) { // don't run twice, skip this execution 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; } } WorkThreadInformations.setLongRunningTask("ldapSync"); coordinator.getEventBus().fireEventToListenersOf(new LDAPEvent(LDAPEvent.SYNCHING), ldapSyncLockOres); if(full) { lastSyncDate = null; } LdapContext ctx = null; boolean success = false; try { acquireSyncLock(); ctx = bindSystem(); if (ctx == null) { errors.insert("LDAP connection ERROR"); log.error("Error in LDAP batch sync: LDAP connection empty", null); freeSyncLock(); success = false; return success; } Date timeBeforeSync = new Date(); //check server capabilities // 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); // 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(); //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; ctx.close(); success = true; return success; } catch (Exception e) { errors.insert("Unknown error"); log.error("Error in LDAP batch sync, unknown reason", e); success = false; return success; } finally { WorkThreadInformations.unsetLongRunningTask("ldapSync"); freeSyncLock(); if(ctx != null) { try { ctx.close(); } catch (NamingException e) { //try but failed silently } } LDAPEvent endEvent = new LDAPEvent(LDAPEvent.SYNCHING_ENDED); endEvent.setTimestamp(new Date()); endEvent.setSuccess(success); endEvent.setErrors(errors); coordinator.getEventBus().fireEventToListenersOf(endEvent, ldapSyncLockOres); } } 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) { log.info("LDAP batch sync: no users to delete" + sinceSentence); } else { 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 // by the sync job SecurityGroup ldapGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); List<Identity> olatListIdentity = securityManager.getIdentitiesOfSecurityGroup(ldapGroup); if (olatListIdentity.isEmpty()) 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()) { 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); log.info("LDAP batch sync: " + deletedUserList.size() + " users deleted" + sinceSentence); } } } else { // Do nothing, only log users to logfile StringBuilder users = new StringBuilder(); for (Identity toBeDeleted : deletedUserList) { users.append(toBeDeleted.getName()).append(','); } log.info("LDAP batch sync: " + deletedUserList.size() + " users detected as to be deleted" + sinceSentence + ". Automatic deleting is disabled in LDAPLoginModule, delete these users manually::[" + users.toString() + "]"); } } } 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<LDAPUser> ldapUserList = ldapDao.getUserAttributesModifiedSince(lastSyncDate, ctx); // Check for new and modified users List<LDAPUser> newLdapUserList = new ArrayList<LDAPUser>(); Map<Identity, Map<String, String>> changedMapIdentityMap = new HashMap<Identity, Map<String, String>>(); for (LDAPUser ldapUser: ldapUserList) { String user = null; try { Attributes userAttrs = ldapUser.getAttributes(); String uidProp = syncConfiguration.getOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER); user = getAttributeValue(userAttrs.get(uidProp)); Identity identity = findIdentityByLdapAuthentication(userAttrs, errors); if (identity != null) { Map<String, String> changedAttrMap = prepareUserPropertyForSync(userAttrs, identity); 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 = syncConfiguration.checkRequestAttributes(userAttrs); if (reqAttrs == null) { 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 { log.warn(errors.get(), null); } if(++count % 20 == 0) { dbInstance.intermediateCommit(); } } catch (Exception e) { // catch here to go on with other users on exeptions! 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()) { 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) { dbInstance.intermediateCommit(); } } log.info("LDAP batch sync: " + changedMapIdentityMap.size() + " users synced" + sinceSentence); } // create new users if (newLdapUserList.isEmpty()) { log.info("LDAP batch sync: no users to create" + sinceSentence); } else { for (LDAPUser ldapUser: newLdapUserList) { Attributes userAttrs = ldapUser.getAttributes(); try { Identity identity = createAndPersistUser(userAttrs); if(++count % 20 == 0) { 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! log.error("some error occured while creating new users, actual userAttribs " + userAttrs + ". Will still continue with others.", e); } } log.info("LDAP batch sync: " + newLdapUserList.size() + " users created" + sinceSentence); } dbInstance.intermediateCommit(); return ldapUserList; } private void doBatchSyncGroups(LdapContext ctx, List<LDAPUser> ldapUsers, Map<String,LDAPUser> dnToIdentityKeyMap, LDAPError errors) throws NamingException { ctx.close(); log.info("LDAP batch sync LDAP user to OO groups"); ctx = bindSystem(); //sync groups by LDAP groups or attributes Map<String,LDAPGroup> cnToGroupMap = new HashMap<>(); // retrieve all ldap group's with their list of members if(syncConfiguration.syncGroupWithLDAPGroup()) { List<String> groupDNs = syncConfiguration.getLdapGroupBases(); List<LDAPGroup> ldapGroups = ldapDao.searchGroups(ctx, groupDNs); for(LDAPGroup ldapGroup:ldapGroups) { cnToGroupMap.put(ldapGroup.getCommonName(), ldapGroup); } } if(syncConfiguration.syncGroupWithAttribute()) { doSyncGroupByAttribute(ldapUsers, cnToGroupMap); } for(LDAPGroup group:cnToGroupMap.values()) { BusinessGroup managedGroup = getManagerBusinessGroup(group.getCommonName()); if(managedGroup != null) { syncBusinessGroup(ctx, managedGroup, group, dnToIdentityKeyMap, errors); } dbInstance.commitAndCloseSession(); } } private void doSyncGroupByAttribute(List<LDAPUser> ldapUsers, Map<String,LDAPGroup> cnToGroupMap) { for(LDAPUser ldapUser:ldapUsers) { List<String> groupIds = ldapUser.getGroupIds(); List<String> coachedGroupIds = ldapUser.getCoachedGroupIds(); if((groupIds != null && groupIds.size() > 0) || (coachedGroupIds != null && coachedGroupIds.size() > 0)) { Identity identity = ldapUser.getCachedIdentity(); if(identity == null) { log.error("Identity with dn=" + ldapUser.getDn() + " not found"); } else { if(groupIds != null && groupIds.size() > 0) { for(String groupId:groupIds) { if(!cnToGroupMap.containsKey(groupId)) { cnToGroupMap.put(groupId, new LDAPGroup(groupId)); } cnToGroupMap.get(groupId).getParticipants().add(ldapUser); } } if(coachedGroupIds != null && coachedGroupIds.size() > 0) { for(String coachedGroupId:coachedGroupIds) { if(!cnToGroupMap.containsKey(coachedGroupId)) { cnToGroupMap.put(coachedGroupId, new LDAPGroup(coachedGroupId)); } cnToGroupMap.get(coachedGroupId).getCoaches().add(ldapUser); } } } } } } private void syncBusinessGroup(LdapContext ctx, BusinessGroup businessGroup, LDAPGroup ldapGroup, Map<String,LDAPUser> dnToIdentityKeyMap, LDAPError errors) { List<Identity> currentMembers = businessGroupRelationDao .getMembers(businessGroup, GroupRoles.coach.name(), GroupRoles.participant.name()); List<LDAPUser> coaches = new ArrayList<>(ldapGroup.getCoaches()); List<LDAPUser> participants = new ArrayList<>(ldapGroup.getParticipants()); // transfer member cn's to the participants list for(String member:ldapGroup.getMembers()) { LDAPUser ldapUser = getLDAPUser(ctx, member, dnToIdentityKeyMap, errors); dnToIdentityKeyMap.get(member); if(ldapUser != null && !participants.contains(ldapUser)) { participants.add(ldapUser); } } // transfer to ldap user flagged as coach to the coach list for(Iterator<LDAPUser> participantIt=participants.iterator(); participantIt.hasNext(); ) { LDAPUser participant = participantIt.next(); if(participant.isCoach()) { if(!coaches.contains(participant)) { coaches.add(participant); } participantIt.remove(); } } for(LDAPUser participant:participants) { Identity memberIdentity = participant.getCachedIdentity(); syncMembership(businessGroup, memberIdentity, false); currentMembers.remove(memberIdentity); } for(LDAPUser coach:coaches) { Identity memberIdentity = coach.getCachedIdentity(); syncMembership(businessGroup, memberIdentity, true); 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 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 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 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(); identity = findIdentityByLdapAuthentication(userAttrs, errors); if(identity != null) { dnToIdentityKeyMap.put(userDN, ldapUser); } } } return ldapUser; } @Override public void doSyncSingleUser(Identity ident){ LdapContext ctx = bindSystem(); if (ctx == null) { log.error("could not bind to ldap", null); } String userDN = ldapDao.searchUserDNByUid(ident.getName(), ctx); final List<Attributes> ldapUserList = new ArrayList<Attributes>(); // TODO: use userDN instead of filter to get users attribs ldapDao.searchInLdap(new LDAPVisitor() { @Override public void visit(SearchResult result) { Attributes resAttribs = result.getAttributes(); log.debug(" found : " + resAttribs.size() + " attributes in result " + result.getName()); ldapUserList.add(resAttribs); } }, userDN, syncConfiguration.getUserAttributes(), ctx); Attributes attrs = ldapUserList.get(0); Map<String, String> olatProToSync = prepareUserPropertyForSync(attrs, ident); if (olatProToSync != null) { syncUser(olatProToSync, ident); } } /** * @see org.olat.ldap.LDAPLoginManager#getLastSyncDate() */ @Override public Date getLastSyncDate() { return lastSyncDate; } /** * Internal helper to add the SSL protocol to the environment * * @param env */ private void enableSSL(Hashtable<String, String> env) { env.put(Context.SECURITY_PROTOCOL, "ssl"); 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; } batchSyncIsRunning=true; return true; } /** * Release lock for administration jobs * */ @Override public synchronized void freeSyncLock() { batchSyncIsRunning = false; } /** * remove all cached authentications for fallback-login. useful if users logged in first with a default pw and changed it outside in AD/LDAP, but OLAT doesn't know about. * removing fallback-auths means login is only possible by AD/LDAP and if server is reachable! * see FXOLAT-284 */ @Override public void removeFallBackAuthentications() { if (ldapLoginModule.isCacheLDAPPwdAsOLATPwdOnLogin()){ SecurityGroup ldapGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); if (ldapGroup == null) { log.error("Error getting user from OLAT security group '" + LDAPConstants.SECURITY_GROUP_LDAP + "' : group does not exist", null); } 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 = securityManager.findAuthentication(identity, BaseSecurityModule.getDefaultAuthProviderIdentifier()); if (auth!=null){ securityManager.deleteAuthentication(auth); count++; } if (count % 20 == 0){ dbInstance.intermediateCommit(); } } log.info("removed cached authentications (fallback login provider: " + BaseSecurityModule.getDefaultAuthProviderIdentifier() + ") for " + count + " users."); } } @Override public boolean isIdentityInLDAPSecGroup(Identity ident) { SecurityGroup ldapSecurityGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP); return ldapSecurityGroup != null && securityManager.isIdentityInSecurityGroup(ident, ldapSecurityGroup); } }