Package org.camunda.bpm.identity.impl.ldap

Source Code of org.camunda.bpm.identity.impl.ldap.LdapIdentityProviderSession

/* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.camunda.bpm.identity.impl.ldap;

import org.camunda.bpm.engine.authorization.Permission;
import org.camunda.bpm.engine.authorization.Resource;
import org.camunda.bpm.engine.identity.Group;
import org.camunda.bpm.engine.identity.GroupQuery;
import org.camunda.bpm.engine.identity.User;
import org.camunda.bpm.engine.identity.UserQuery;
import org.camunda.bpm.engine.impl.AbstractQuery;
import org.camunda.bpm.engine.impl.UserQueryImpl;
import org.camunda.bpm.engine.impl.UserQueryProperty;
import org.camunda.bpm.engine.impl.identity.IdentityProviderException;
import org.camunda.bpm.engine.impl.identity.ReadOnlyIdentityProvider;
import org.camunda.bpm.engine.impl.interceptor.CommandContext;
import org.camunda.bpm.engine.impl.persistence.entity.GroupEntity;
import org.camunda.bpm.engine.impl.persistence.entity.UserEntity;

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.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;
import javax.naming.ldap.SortControl;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import static org.camunda.bpm.engine.authorization.Permissions.READ;
import static org.camunda.bpm.engine.authorization.Resources.GROUP;
import static org.camunda.bpm.engine.authorization.Resources.USER;

/**
* <p>LDAP {@link ReadOnlyIdentityProvider}.</p>
*
* @author Daniel Meyer
*
*/
public class LdapIdentityProviderSession implements ReadOnlyIdentityProvider {

  private final static Logger LOG = Logger.getLogger(LdapIdentityProviderSession.class.getName());

  protected LdapConfiguration ldapConfiguration;
  protected LdapContext initialContext;

  public LdapIdentityProviderSession(LdapConfiguration ldapConfiguration) {
    this.ldapConfiguration = ldapConfiguration;
  }

  // Session Lifecycle //////////////////////////////////

  public void flush() {
    // nothing to do
  }

  public void close() {
    if (initialContext != null) {
      try {
        initialContext.close();
      } catch (Exception e) {
        // ignore
        LOG.log(Level.FINE, "exception while closing LDAP DIR CTX", e);
      }
    }
  }

  protected InitialLdapContext openContext(String userDn, String password) {
    Hashtable<String, String> env = new Hashtable<String, String>();
    env.put(Context.INITIAL_CONTEXT_FACTORY, ldapConfiguration.getInitialContextFactory());
    env.put(Context.SECURITY_AUTHENTICATION, ldapConfiguration.getSecurityAuthentication());
    env.put(Context.PROVIDER_URL, ldapConfiguration.getServerUrl());
    env.put(Context.SECURITY_PRINCIPAL, userDn);
    env.put(Context.SECURITY_CREDENTIALS, password);

    // for anonymous login
    if(ldapConfiguration.isAllowAnonymousLogin() && password.isEmpty()) {
      env.put(Context.SECURITY_AUTHENTICATION, "none");
    }

    if(ldapConfiguration.isUseSsl()) {
      env.put(Context.SECURITY_PROTOCOL, "ssl");
    }

    // add additional properties
    Map<String, String> contextProperties = ldapConfiguration.getContextProperties();
    if(contextProperties != null) {
      env.putAll(contextProperties);
    }

    try {
      return new InitialLdapContext(env, null);

    } catch(AuthenticationException e) {
      throw new LdapAuthenticationException("Could not authenticate with LDAP server", e);

    } catch(NamingException e) {
      throw new IdentityProviderException("Could not connect to LDAP server", e);

    }
  }

  protected void ensureContextInitialized() {
    if(initialContext == null) {
      initialContext = openContext(ldapConfiguration.getManagerDn(), ldapConfiguration.getManagerPassword());
    }
  }

  // Users /////////////////////////////////////////////////

  public User findUserById(String userId) {
    return createUserQuery(org.camunda.bpm.engine.impl.context.Context.getCommandContext())
      .userId(userId)
      .singleResult();
  }

  public UserQuery createUserQuery() {
    return new LdapUserQueryImpl(org.camunda.bpm.engine.impl.context.Context.getProcessEngineConfiguration().getCommandExecutorTxRequired());
  }

  public UserQueryImpl createUserQuery(CommandContext commandContext) {
    return new LdapUserQueryImpl(commandContext);
  }

  public long findUserCountByQueryCriteria(LdapUserQueryImpl query) {
    ensureContextInitialized();
    return findUserByQueryCriteria(query).size();
  }

  public List<User> findUserByQueryCriteria(LdapUserQueryImpl query) {
    ensureContextInitialized();
    if(query.getGroupId() != null) {
      // if restriction on groupId is provided, we need to search in group tree first, look for the group and then further restrict on the members
      return findUsersByGroupId(query);
    } else {
      String userBaseDn = composeDn(ldapConfiguration.getUserSearchBase(), ldapConfiguration.getBaseDn());
      return findUsersWithoutGroupId(query, userBaseDn);
    }
  }

  protected List<User> findUsersByGroupId(LdapUserQueryImpl query) {
    String baseDn = getDnForGroup(query.getGroupId());

    // compose group search filter
    String groupSearchFilter = "(& " + ldapConfiguration.getGroupSearchFilter() + ")";

    NamingEnumeration<SearchResult> enumeration = null;
    try {
      enumeration = initialContext.search(baseDn, groupSearchFilter, ldapConfiguration.getSearchControls());

      List<String> userDnList = new ArrayList<String>();

      // first find group
      while (enumeration.hasMoreElements()) {
        SearchResult result = enumeration.nextElement();
        Attribute memberAttribute = result.getAttributes().get(ldapConfiguration.getGroupMemberAttribute());
        NamingEnumeration<?> allMembers = memberAttribute.getAll();

        // iterate group members
        while (allMembers.hasMoreElements() && userDnList.size() < query.getMaxResults()) {
          userDnList.add((String) allMembers.nextElement());
        }

      }

      List<User> userList = new ArrayList<User>();
      for (String userDn : userDnList) {
          List<User> users = findUsersWithoutGroupId(query, userDn);
          if (users.size() > 0) {
            userList.add(users.get(0));
          }
      }

      return userList;

    } catch (NamingException e) {
      throw new IdentityProviderException("Could not query for users", e);

    } finally {
      try {
        if (enumeration != null) {
          enumeration.close();
        }
      } catch (Exception e) {
        // ignore silently
      }
    }
  }

  public List<User> findUsersWithoutGroupId(LdapUserQueryImpl query, String userBaseDn) {

    if(ldapConfiguration.isSortControlSupported()) {
      applyRequestControls(query);
    }

    NamingEnumeration<SearchResult> enumeration = null;
    try {

      String filter = getUserSearchFilter(query);
      enumeration = initialContext.search(userBaseDn, filter, ldapConfiguration.getSearchControls());

      // perform client-side paging
      int resultCount = 0;
      List<User> userList = new ArrayList<User>();
      while (enumeration.hasMoreElements() && userList.size() < query.getMaxResults()) {
        SearchResult result = enumeration.nextElement();

        if(resultCount >= query.getFirstResult()) {
          UserEntity user = transformUser(result);
          if(isAuthenticatedUser(user) || isAuthorized(READ, USER, user.getId())) {
            userList.add(user);
          }
        }

        resultCount ++;
      }

      return userList;

    } catch (NamingException e) {
      throw new IdentityProviderException("Could not query for users", e);

    } finally {
      try {
        if (enumeration != null) {
          enumeration.close();
        }
      } catch (Exception e) {
        // ignore silently
      }
    }
  }

  public boolean checkPassword(String userId, String password) {

    // prevent a null password
    if(password == null) {
      return false;
    }

    // engine can't work without users
    if(userId == null || userId.isEmpty()) {
      return false;
    }

    /*
    * We only allow login with no password if anonymous login is set.
    * RFC allows such a behavior but discourages the usage so we provide it for
    * user which have an ldap with anonymous login.
    */
    if(!ldapConfiguration.isAllowAnonymousLogin() && password.equals("")) {
      return false;
    }

    // first search for user using manager DN
    LdapUserEntity user = (LdapUserEntity) findUserById(userId);
    close();

    if(user == null) {
      return false;
    } else {

      try {
        // bind authenticate for user + supplied password
        openContext(user.getDn(), password);
        return true;

      } catch(LdapAuthenticationException e) {
        return false;

      }

    }

  }

  protected String getUserSearchFilter(LdapUserQueryImpl query) {

    StringWriter search = new StringWriter();
    search.write("(&");

    // restrict to users
    search.write(ldapConfiguration.getUserSearchFilter());

    // add additional filters from query
    if(query.getId() != null) {
      addFilter(ldapConfiguration.getUserIdAttribute(), escapeLDAPSearchFilter(query.getId()), search);
    }
    if(query.getIds() != null && query.getIds().length > 0) {
      // wrap ids in OR statement
      search.write("(|");
      for (String userId : query.getIds()) {
        addFilter(ldapConfiguration.getUserIdAttribute(), escapeLDAPSearchFilter(userId), search);
      }
      search.write(")");
    }
    if(query.getEmail() != null) {
      addFilter(ldapConfiguration.getUserEmailAttribute(), query.getEmail(), search);
    }
    if(query.getEmailLike() != null) {
      addFilter(ldapConfiguration.getUserEmailAttribute(), query.getEmailLike(), search);
    }
    if(query.getFirstName() != null) {
      addFilter(ldapConfiguration.getUserFirstnameAttribute(), query.getFirstName(), search);
    }
    if(query.getFirstNameLike() != null) {
      addFilter(ldapConfiguration.getUserFirstnameAttribute(), query.getFirstNameLike(), search);
    }
    if(query.getLastName() != null) {
      addFilter(ldapConfiguration.getUserLastnameAttribute(), query.getLastName(), search);
    }
    if(query.getLastNameLike() != null) {
      addFilter(ldapConfiguration.getUserLastnameAttribute(), query.getLastNameLike(), search);
    }

    search.write(")");

    return search.toString();
  }

  // Groups ///////////////////////////////////////////////

  public Group findGroupById(String groupId) {
    return createGroupQuery(org.camunda.bpm.engine.impl.context.Context.getCommandContext())
      .groupId(groupId)
      .singleResult();
  }

  public GroupQuery createGroupQuery() {
    return new LdapGroupQuery(org.camunda.bpm.engine.impl.context.Context.getProcessEngineConfiguration().getCommandExecutorTxRequired());
  }

  public GroupQuery createGroupQuery(CommandContext commandContext) {
    return new LdapGroupQuery(commandContext);
  }

  public long findGroupCountByQueryCriteria(LdapGroupQuery ldapGroupQuery) {
    ensureContextInitialized();
    return findGroupByQueryCriteria(ldapGroupQuery).size();
  }

  public List<Group> findGroupByQueryCriteria(LdapGroupQuery query) {
    ensureContextInitialized();

    String groupBaseDn = composeDn(ldapConfiguration.getGroupSearchBase(),ldapConfiguration.getBaseDn());

    if(ldapConfiguration.isSortControlSupported()) {
      applyRequestControls(query);
    }

    NamingEnumeration<SearchResult> enumeration = null;
    try {

      String filter = getGroupSearchFilter(query);
      enumeration = initialContext.search(groupBaseDn, filter, ldapConfiguration.getSearchControls());

      // perform client-side paging
      int resultCount = 0;
      List<Group> groupList = new ArrayList<Group>();
      while (enumeration.hasMoreElements() && groupList.size() < query.getMaxResults()) {
        SearchResult result = enumeration.nextElement();

        if(resultCount >= query.getFirstResult()) {
          GroupEntity group = transformGroup(result);
          if(isAuthorized(READ, GROUP, group.getId())) {
            groupList.add(group);
          }
        }

        resultCount ++;
      }

      return groupList;

    } catch (NamingException e) {
      throw new IdentityProviderException("Could not query for users", e);

    } finally {
      try {
        if (enumeration != null) {
          enumeration.close();
        }
      } catch (Exception e) {
        // ignore silently
      }
    }
  }

  protected String getGroupSearchFilter(LdapGroupQuery query) {

    StringWriter search = new StringWriter();
    search.write("(&");

    // restrict to groups
    search.write(ldapConfiguration.getGroupSearchFilter());

    // add additional filters from query
    if(query.getId() != null) {
      addFilter(ldapConfiguration.getGroupIdAttribute(), query.getId(), search);
    }
    if(query.getName() != null) {
      addFilter(ldapConfiguration.getGroupNameAttribute(), query.getName(), search);
    }
    if(query.getNameLike() != null) {
      addFilter(ldapConfiguration.getGroupNameAttribute(), query.getNameLike(), search);
    }
    if(query.getUserId() != null) {
      String userDn = null;
      if(ldapConfiguration.isUsePosixGroups()) {
        userDn = query.getUserId();
      } else {
        userDn = getDnForUser(query.getUserId());
      }
      addFilter(ldapConfiguration.getGroupMemberAttribute(), escapeLDAPSearchFilter(userDn), search);
    }
    search.write(")");

    return search.toString();
  }

  // Utils ////////////////////////////////////////////

  protected String getDnForUser(String userId) {
    LdapUserEntity user = (LdapUserEntity) createUserQuery(org.camunda.bpm.engine.impl.context.Context.getCommandContext())
      .userId(userId)
      .singleResult();
    if(user == null) {
      return "";
    } else {
      return user.getDn();
    }
  }

  protected String getDnForGroup(String groupId) {
    LdapGroupEntity group = (LdapGroupEntity) createGroupQuery(org.camunda.bpm.engine.impl.context.Context.getCommandContext())
      .groupId(groupId)
      .singleResult();
    if(group == null) {
      return "";
    } else {
      return group.getDn();
    }
  }

  protected String getStringAttributeValue(String attrName, Attributes attributes) throws NamingException {
    Attribute attribute = attributes.get(attrName);
    if(attribute != null){
      return (String) attribute.get();
    } else {
      return null;
    }
  }

  protected void addFilter(String attributeName, String attributeValue, StringWriter writer) {
    writer.write("(");
    writer.write(attributeName);
    writer.write("=");
    writer.write(attributeValue);
    writer.write(")");
  }

  protected LdapUserEntity transformUser(SearchResult result) throws NamingException {
    final Attributes attributes = result.getAttributes();
    LdapUserEntity user = new LdapUserEntity();
    user.setDn(result.getNameInNamespace());
    user.setId(getStringAttributeValue(ldapConfiguration.getUserIdAttribute(), attributes));
    user.setFirstName(getStringAttributeValue(ldapConfiguration.getUserFirstnameAttribute(), attributes));
    user.setLastName(getStringAttributeValue(ldapConfiguration.getUserLastnameAttribute(), attributes));
    user.setEmail(getStringAttributeValue(ldapConfiguration.getUserEmailAttribute(), attributes));
    return user;
  }

  protected GroupEntity transformGroup(SearchResult result) throws NamingException {
    final Attributes attributes = result.getAttributes();
    LdapGroupEntity group = new LdapGroupEntity();
    group.setDn(result.getNameInNamespace());
    group.setId(getStringAttributeValue(ldapConfiguration.getGroupIdAttribute(), attributes));
    group.setName(getStringAttributeValue(ldapConfiguration.getGroupNameAttribute(), attributes));
    group.setType(getStringAttributeValue(ldapConfiguration.getGroupTypeAttribute(), attributes));
    return group;
  }

  @SuppressWarnings("rawtypes")
  protected void applyRequestControls(AbstractQuery query) {

    try {
      List<Control> controls = new ArrayList<Control>();

      String orderBy = query.getOrderBy();
      if(orderBy != null) {
        orderBy = orderBy.substring(0, orderBy.length()-4);
        if(UserQueryProperty.USER_ID.getName().equals(orderBy)) {
          controls.add(new SortControl(ldapConfiguration.getUserIdAttribute(), Control.CRITICAL));

        } else if(UserQueryProperty.EMAIL.getName().equals(orderBy)) {
          controls.add(new SortControl(ldapConfiguration.getUserEmailAttribute(), Control.CRITICAL));

        } else if(UserQueryProperty.FIRST_NAME.getName().equals(orderBy)) {
          controls.add(new SortControl(ldapConfiguration.getUserFirstnameAttribute(), Control.CRITICAL));

        } else if(UserQueryProperty.LAST_NAME.getName().equals(orderBy)) {
          controls.add(new SortControl(ldapConfiguration.getUserLastnameAttribute(), Control.CRITICAL));
        }
      }

      initialContext.setRequestControls(controls.toArray(new Control[0]));

    } catch (Exception e) {
      throw new IdentityProviderException("Exception while setting paging settings", e);
    }
  }

  protected String composeDn(String... parts) {
    StringWriter resultDn = new StringWriter();
    for (int i = 0; i < parts.length; i++) {
      String part = parts[i];
      if(part == null || part.length()==0) {
        continue;
      }
      if(part.endsWith(",")) {
        part = part.substring(part.length()-2, part.length()-1);
      }
      if(part.startsWith(",")) {
        part = part.substring(1);
      }
      String currentDn = resultDn.toString();
      if(!currentDn.endsWith(",") && currentDn.length()>0) {
        resultDn.write(",");
      }
      resultDn.write(part);
    }
    return resultDn.toString();
  }


  /**
   * @return true if the passed-in user is currently authenticated
   */
  protected boolean isAuthenticatedUser(UserEntity user) {
    if(user.getId() == null) {
      return false;
    }
    return user.getId().equals(org.camunda.bpm.engine.impl.context.Context.getCommandContext().getAuthenticatedUserId());
  }

  protected boolean isAuthorized(Permission permission, Resource resource, String resourceId) {
    return org.camunda.bpm.engine.impl.context.Context.getCommandContext()
      .getAuthorizationManager()
      .isAuthorized(permission, resource, resourceId);
  }

  // Based on https://www.owasp.org/index.php/Preventing_LDAP_Injection_in_Java
  protected final String escapeLDAPSearchFilter(String filter) {
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < filter.length(); i++) {
      char curChar = filter.charAt(i);
        switch (curChar) {
          case '\\':
            sb.append("\\5c");
            break;
          case '*':
            sb.append("\\2a");
            break;
          case '(':
            sb.append("\\28");
            break;
          case ')':
            sb.append("\\29");
            break;
          case '\u0000':
            sb.append("\\00");
            break;
          default:
            sb.append(curChar);
        }
    }
    return sb.toString();
  }
}
TOP

Related Classes of org.camunda.bpm.identity.impl.ldap.LdapIdentityProviderSession

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.