Package org.olat.ldap

Source Code of org.olat.ldap.LDAPLoginManagerImpl

package org.olat.ldap;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.TimeZone;
import java.util.Map.Entry;

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.InitialDirContext;
import javax.naming.directory.ModificationItem;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

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.Constants;
import org.olat.basesecurity.Manager;
import org.olat.basesecurity.ManagerFactory;
import org.olat.basesecurity.SecurityGroup;
import org.olat.core.commons.persistence.DBFactory;
import org.olat.core.id.Identity;
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.ldap.ui.LDAPAuthenticationController;
import org.olat.user.UserManager;

/**
* Description: This manager handles  communication between LDAP and OLAT. LDAP access is done by JNDI.
* <p>
* LDAPLoginMangerImpl
* <ul>
* </ul>
* <p>
*
* @author Maurus Rohrer
*/
public class LDAPLoginManagerImpl extends LDAPLoginManager {
  private static OLog log = Tracing.createLoggerFor(LDAPLoginManagerImpl.class);

  private static final TimeZone UTC_TIME_ZONE;
  private static final SimpleDateFormat generalizedTimeFormatter;
  private static boolean batchSyncIsRunning = false;
  private static Date lastSyncDate = null; // first sync is always a full sync
 

  static {
    UTC_TIME_ZONE = TimeZone.getTimeZone(TimeZones.UTC_ID);
    generalizedTimeFormatter = new SimpleDateFormat("yyyyMMddHHmmss'Z'");
    generalizedTimeFormatter.setTimeZone(UTC_TIME_ZONE);
  }

  /**
   * Private constructor. Use LDAPLoginManager.getInstance() method instead
   */
  private LDAPLoginManagerImpl() {
    super();
  }

  /**
   * 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
   * (property=ldapSystemPW)
   *
   * @return The LDAP connection (InitialDirContext) or NULL if connect fails
   *
   * @throws NamingException
   */
  public InitialDirContext 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());

    // check ssl
    if (LDAPLoginModule.isSslEnabled()) {
      enableSSL(env);
    }

    try {
      InitialDirContext ctx = new InitialDirContext(env);
      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;
    }

  }

  /**
   *
   * 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)
   *
   *
   * @param uid The users LDAP login name (can't be null)
   * @param pwd The users LDAP password (can't be null)
   *
   * @return After succsessful bind Attributes otherwise NULL
   *
   * @throws NamingException
   */
  public Attributes bindUser(String uid, String pwd, LDAPError errors) {
    // get user name, password and attributes
    String ldapUrl = LDAPLoginModule.getLdapUrl();
    List<String> ldapBases = LDAPLoginModule.getLdapBases();
    String objctClass = LDAPLoginModule.getLdapUserObjectClass();
    String[] userAttr = LDAPLoginModule.getUserAttrs();
    String[] serachAttr = { "dn" };
    InitialDirContext ctx;

    if (uid == null || pwd == null) {
      if (log.isDebug()) log.debug("Error when trying to bind user, missing username or password. Username::" + uid + " pwd::" + pwd);
      errors.insert("Username and passwort must be selected");
      return null;
    }
    String ldapUserIDAttribute = LDAPHelper.mapOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER);
    String filter = "(&(objectClass=" + objctClass + ")(" + ldapUserIDAttribute + "=" + uid + "))";
    SearchControls ctls = new SearchControls();
    ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
    ctls.setReturningAttributes(serachAttr);
    ctx = bindSystem();
    if (ctx == null) {
      errors.insert("LDAP connection error");
      return null;
    }
    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);
      }
    }
    if (userDN == null) {
      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 passwort 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.isSslEnabled()) {
      enableSSL(env);
    }

    try {
      InitialDirContext userBind = new InitialDirContext(env);
      Attributes attributes = userBind.getAttributes(userDN, userAttr);
      userBind.close();
      return attributes;
    } catch (AuthenticationException e) {
      log.info("Error when trying to bind user with username::" + uid + " - invalid LDAP password");
      errors.insert("Username or passwort incorrect");
      return null;
    } catch (NamingException e) {
      log.error("NamingException when trying to get attributes after binding user with username::" + uid, e);
      errors.insert("Username or passwort incorrect");
      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 void changePassword(Identity identity, String pwd, LDAPError errors) {
    String uid = identity.getName();
    String ldapUserPasswordAttribute = LDAPLoginModule.getLdapUserPasswordAttribute();
    try {
      DirContext ctx = bindSystem();
      String dn = searchUserDN(uid, ctx);
     
      ModificationItem [] modificationItems = new ModificationItem [ 1 ];
     
      Attribute userPasswordAttribute;
      if(LDAPLoginModule.isActiveDirectory()) {
        //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);
        }
        userPasswordAttribute = new BasicAttribute ( ldapUserPasswordAttribute, pwdArray );
      } else {
        userPasswordAttribute = new BasicAttribute ( ldapUserPasswordAttribute, pwd );
      }

      modificationItems [ 0 ] = new ModificationItem ( DirContext.REPLACE_ATTRIBUTE, userPasswordAttribute );
      ctx.modifyAttributes ( dn, modificationItems );
      ctx.close();
    } catch (NamingException 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 objctClass = LDAPLoginModule.getLdapUserObjectClass();
    String[] serachAttr = { "dn" };
   
    String ldapUserIDAttribute = LDAPHelper.mapOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER);
    String filter = "(&(objectClass=" + objctClass + ")(" + ldapUserIDAttribute + "=" + 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;
  }

  /**
   *
   * 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, InitialDirContext ctx) {
    String[] userAttr = LDAPLoginModule.getUserAttrs();
    List<String> ldapBases = LDAPLoginModule.getLdapBases();
    String objctClass = LDAPLoginModule.getLdapUserObjectClass();
    StringBuffer filterBuffer = new StringBuffer();
    if (syncTime == null) {
      filterBuffer.append("(objectClass=").append(objctClass).append(")");
    } else {
      String syncTimeForm = generalizedTimeFormatter.format(syncTime);
      filterBuffer.append("(&(objectClass=").append(objctClass).append(")(|(");
      filterBuffer.append(LDAPLoginModule.getLdapUserLastModifiedTimestampAttribute()).append(">=").append(syncTimeForm);
      filterBuffer.append(")(");
      filterBuffer.append(LDAPLoginModule.getLdapUserCreatedTimestampAttribute()).append(">=").append(syncTimeForm);
      filterBuffer.append(")))");
    }
    String filter = filterBuffer.toString();
    List<Attributes> ldapUserList = new LinkedList<Attributes>();
    SearchControls ctls = new SearchControls();
    ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
    ctls.setReturningAttributes(userAttr);
    ctls.setCountLimit(0); // no limits

    int counter = 0;
    try {
      for (String ldapBase : ldapBases) {
        NamingEnumeration<SearchResult> enm = ctx.search(ldapBase, filter, ctls);
        while (enm.hasMore()) {
          SearchResult result = enm.next();
          ldapUserList.add(result.getAttributes());
          counter++;
        }
      }
    } catch (SizeLimitExceededException e) {
      log
          .error("SizeLimitExceededException after "
              + counter
              + " records when getting all user attributers from LDAP, reconfigure your LDAP server, hints: http://www.ldapbrowser.com/forum/viewtopic.php?t=14");
      return null;
    } catch (NamingException e) {
      log.error("NamingException when trying to get modified LDAP user attributes with filter::" + filter + " at row::" + counter, e);
    }
    return ldapUserList;
  }

  /**
   * Delete all Identities in List and removes them from LDAPSecurityGroup
   *
   * @param identityList List of Identities to delete
   */
  public void deletIdentities(List<Identity> identityList) {
    Manager securityManager = ManagerFactory.getManager();
    Identity identity;
    UserDeletionManager deletionManager = UserDeletionManager.getInstance();
    SecurityGroup secGroup = securityManager.findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP);
    for (Iterator<Identity> itr = identityList.iterator(); itr.hasNext();) {
      identity = itr.next();
      securityManager.removeIdentityFromSecurityGroup(identity, secGroup);
      deletionManager.deleteIdentity(identity);
    }
  }

  /**
   * Sync all OLATPropertys in Map of Identity
   *
   * @param olatPropertyMap Map of changed OLAT properties
   *          (OLATProperty,LDAPValue)
   * @param identity Identity to sync
   */
  public void syncUser(Map<String, String> olatPropertyMap, Identity identity) {
    if (identity == null) {
      log.warn("Identiy is null - should not happen");
      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();
    if (syncOnlyOnCreateProperties != null) {
      for (String syncOnlyOnCreateKey : syncOnlyOnCreateProperties) {
        olatPropertyMap.remove(syncOnlyOnCreateKey);
      }     
    }
    Iterator<String> itrSync = olatPropertyMap.keySet().iterator();
    while (itrSync.hasNext()) {
      String key = itrSync.next();
      user.setProperty(key, olatPropertyMap.get(key));
    }
    // Add static user properties from the configuration
    Map<String, String> staticProperties = LDAPLoginModule.getStaticUserProperties();
    if (staticProperties != null && staticProperties.size() > 0) {
      for (Entry<String, String> staticProperty : staticProperties.entrySet()) {
        user.setProperty(staticProperty.getKey(), staticProperty.getValue());
      }
    }

  }

  /**
   * 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
   */
  @SuppressWarnings("unchecked")
  public void createAndPersistUser(Attributes userAttributes) {
    // Get and Check Config
    String[] reqAttrs = LDAPHelper.checkReqAttr(userAttributes);
    if (reqAttrs != null) {
      log.warn("Can not create and persist user, the following attributes are missing::" + ArrayUtils.toString(reqAttrs));
      return;
    }
    Manager securityManager = ManagerFactory.getManager();
    String uid = LDAPHelper.getAttributeValue(userAttributes.get(LDAPHelper
        .mapOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER)));
    String email = LDAPHelper.getAttributeValue(userAttributes.get(LDAPHelper.mapOlatPropertyToLdapAttribute(UserConstants.EMAIL)));
    // Lookup user
    if (securityManager.findIdentityByName(uid) != null) {
      log.error("Can't create user with username='" + uid + "', does already exist in OLAT database");
      return;
    }
    // Create User (first and lastname is added in next step)
    User user = UserManager.getInstance().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();
    try {
      while (neAttr.hasMore()) {
        Attribute attr = neAttr.next();
        String olatProperty = LDAPHelper.mapLdapAttributeToOlatProperty(attr.getID());
        if (attr.get() != uid) {
          String ldapValue = LDAPHelper.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();
      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;
    } 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;
    }

    // Create Identity
    Identity identity = securityManager.createAndPersistIdentityAndUser(uid, user, LDAPAuthenticationController.PROVIDER_LDAP, uid, null);
    // 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);

  }

  /**
   * 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 = LDAPHelper.mapLdapAttributeToOlatProperty(attr.getID());
        if(olatProperty == null) {
          continue;
        }
        String ldapValue = LDAPHelper.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) return null;
      return olatPropertyMap;

    } catch (NamingException e) {
      log.error("NamingException when trying to prepare user properties for LDAP sync", 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)
   */
  public Identity findIdentyByLdapAuthentication(String uid, LDAPError errors) {
    Manager securityManager = ManagerFactory.getManager();
    Identity identity = securityManager.findIdentityByName(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");
        return null;
      }
      if (securityManager.isIdentityInSecurityGroup(identity, ldapGroup)) {
        Authentication ldapAuth = ManagerFactory.getManager().findAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP);
        if(ldapAuth == null) {
          //BUG Fixe: update the user and test if it has a ldap provider
          ManagerFactory.getManager().createAndPersistAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP, identity.getName(), null);
        }
        return identity;
      }
      else {
        if (LDAPLoginModule.isConvertExistingLocalUsersToLDAPUsers()) {
          // Add user to LDAP security group and add the ldap provider
          ManagerFactory.getManager().createAndPersistAuthentication(identity, LDAPAuthenticationController.PROVIDER_LDAP, identity.getName(), 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 = olatextconfig.xml (property=reqAttrs)
   * 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 Identity from the user which have been deleted in
   *         LDAP
   *
   * @throws NamingException
   */
  public List<Identity> getIdentitysDeletedInLdap(InitialDirContext ctx) {
    String userID = LDAPHelper.mapOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER);
    String objctClass = LDAPLoginModule.getLdapUserObjectClass();
    List<String> ldapBases = LDAPLoginModule.getLdapBases();
    List<String> ldapList = new LinkedList<String>();
    List<Identity> identityListToDelete = new LinkedList<Identity>();

    if (ctx == null) return null;
    // Find all LDAP Users
    SearchControls ctls = new SearchControls();
    ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
    ctls.setReturningAttributes(new String[] { userID });
    ctls.setCountLimit(0); // set no limits

    for (String ldapBase : ldapBases) {
      int counter = 0;
      try {
        NamingEnumeration<SearchResult> enm = ctx.search(ldapBase, "(objectClass=" + objctClass + ")", ctls);
        while (enm.hasMore()) {
          SearchResult result = enm.next();
          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());
          }
          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");
        return null;
      } catch (NamingException e) {
        log.error("NamingException when trying to fetch deleted users from LDAP using ldapBase::" + ldapBase + " on row::" + counter, e);
        return null;
      }
    }

    if (ldapList.size() == 0) {
      log.warn("No users in LDAP found, can't create deletionList!!");
      return null;
    }

    // Find all User in OLAT, members of LDAPSecurityGroup
    Manager securityManager = ManagerFactory.getManager();
    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");
      return null;
    }
    List<Identity> olatListIdentity = securityManager.getIdentitiesOfSecurityGroup(ldapGroup);
    ListIterator<Identity> itr = olatListIdentity.listIterator();
    while (itr.hasNext()) {
      Identity ida = itr.next();
      // 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 olatextconfig.xml
   *
   * @param LDAPError
   *
   */
  public boolean doBatchSync(LDAPError errors) {
    // 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;
      }
    }
    try {
      acquireSyncLock();
      InitialDirContext ctx;
      List<Attributes> ldapUserList;
      List<Attributes> newLdapUserList;
      Map<Identity, Map<String, String>> changedMapIdenityMap;
      List<Identity> deletedUserList;
      String user;
      LDAPLoginManager ldapMan = LDAPLoginManager.getInstance();

      ctx = ldapMan.bindSystem();
      if (ctx == null) {
        errors.insert("LDAP connection ERROR");
        log.error("Error in LDAP batch sync: LDAP connection empty");
        freeSyncLock();
        return false;
      }
      // Get time before sync to have a save sync time when sync is successful
      Date timeBeforeSync = new Date();
      String sinceSentence = (lastSyncDate == null ? " (full sync)" : " since last sync from " + lastSyncDate);
      // create User to Delete List
      deletedUserList = ldapMan.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
          Manager securityManager = ManagerFactory.getManager();
          SecurityGroup ldapGroup = securityManager
              .findSecurityGroupByName(LDAPConstants.SECURITY_GROUP_LDAP);
          List<Identity> olatListIdentity = securityManager
              .getIdentitiesOfSecurityGroup(ldapGroup);
          if (olatListIdentity.size() == 0)
            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
              ldapMan.deletIdentities(deletedUserList);
              log.info("LDAP batch sync: "
                  + deletedUserList.size() + " users deleted"
                  + sinceSentence);
            }
          }
        } else {
          // Do nothing, only log users to logfile
          StringBuffer users = new StringBuffer();
          for (Iterator iterator = deletedUserList.iterator(); iterator
              .hasNext();) {
            Identity toBeDeleted = (Identity) iterator.next();
            users.append(toBeDeleted.getName());
            if (iterator.hasNext())
              users.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() + "]");
        }
      }
      // Get new and modified users from LDAP
      ldapUserList = ldapMan.getUserAttributesModifiedSince(lastSyncDate, ctx);
      Identity identity;
      // Check for new and modified users
      Map<String, String> changedAttrMap;
      newLdapUserList = new LinkedList<Attributes>();
      changedMapIdenityMap = new HashMap<Identity, Map<String, String>>();
      for (int i = 0; i < ldapUserList.size(); i++) {
        user = LDAPHelper.getAttributeValue(ldapUserList.get(i).get(
            LDAPHelper.mapOlatPropertyToLdapAttribute(LDAPConstants.LDAP_USER_IDENTIFYER)));
        identity = ldapMan.findIdentyByLdapAuthentication(user, errors);
        if (identity != null) {
          changedAttrMap = ldapMan.prepareUserPropertyForSync(ldapUserList.get(i), identity);
          if (changedAttrMap != null) changedMapIdenityMap.put(identity, changedAttrMap);
        } else {
          if (errors.isEmpty()) {
            String[] reqAttrs = LDAPHelper.checkReqAttr(ldapUserList.get(i));
            if (reqAttrs == null) newLdapUserList.add(ldapUserList.get(i));
            else log.warn("Error in LDAP batch sync: can't create user with username::" + user + " : missing required attributes::"
                + ArrayUtils.toString(reqAttrs));
          } else {
            log.warn(errors.get());
          }
        }
      }
      // sync existing users
      if (changedMapIdenityMap == null || changedMapIdenityMap.size() == 0) {
        log.info("LDAP batch sync: no users to sync" + sinceSentence);
      } else {
        Iterator<Identity> itrIdent = changedMapIdenityMap.keySet().iterator();
        while (itrIdent.hasNext()) {
          Identity ident = itrIdent.next();
          ldapMan.syncUser(changedMapIdenityMap.get(ident), ident);
        }
        log.info("LDAP batch sync: " + changedMapIdenityMap.size() + " users synced" + sinceSentence);
      }
      // create new users
      if (newLdapUserList.size() == 0) {
        log.info("LDAP batch sync: no users to create" + sinceSentence);
      } else {
        for (int i = 0; i < newLdapUserList.size(); i++) {
          ldapMan.createAndPersistUser(newLdapUserList.get(i));
          // commit new users after 100 users
          DBFactory.getInstance().intermediateCommit();
        }
        log.info("LDAP batch sync: " + newLdapUserList.size() + " users created" + sinceSentence);
      }
      // update sync time and set running flag
      lastSyncDate = timeBeforeSync;
      freeSyncLock();
      ctx.close();
      return true;
    } catch (Exception e) {
      // don't block following retries
      freeSyncLock();
      errors.insert("Unknown error");
      log.error("Error in LDAP batch sync, unknown reason", e);
      return false;
    }
  }

  /**
   * @see org.olat.ldap.LDAPLoginManager#getLastSyncDate()
   */
  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");
    System.setProperty("javax.net.ssl.trustStore", LDAPLoginModule.getTrustStoreLocation());
  }
 
  /**
   * Acquire lock for administration jobs
   *
   */
  public synchronized boolean acquireSyncLock(){
    if(batchSyncIsRunning){
      return false;
    }
    batchSyncIsRunning=true;
    return true;
  }
 
  /**
   * Release lock for administration jobs
   *
   */
  public synchronized void freeSyncLock() {
    batchSyncIsRunning=false;
  }

}
TOP

Related Classes of org.olat.ldap.LDAPLoginManagerImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.