Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Scout/HowTo/4.0/Authentication with a LDAPSecurityFilter"

< Scout‎ | HowTo‎ | 4.0
(Replaced content with "The Scout documentation has been moved to https://eclipsescout.github.io/.")
 
Line 1: Line 1:
{{ScoutPage|cat=HowTo 4.0}}
+
The Scout documentation has been moved to https://eclipsescout.github.io/.
 
+
The [[Scout/Concepts/Security|Security Concept]] page is rather brief on the use of the LDAPSecurityFilter. This how-to describes how to use it and offers an alternate security filter.
+
 
+
= LDAPSecurityFilter  =
+
 
+
In order to use the LDAPSecurityFilter, the corresponding filter class must be added to the server's plugin.xml in the org.eclipse.scout.rt.server.commons.filters extension point
+
 
+
<source lang="xml">
+
<filter
+
    aliases="/process"
+
    class="org.eclipse.scout.http.servletfilter.security.LDAPSecurityFilter"
+
    ranking="50">
+
</filter>
+
</source>
+
 
+
Next, the necessary configuration lines must be added to the server's config.ini file:
+
<pre>org.eclipse.scout.http.servletfilter.security.LDAPSecurityFilter#active=true
+
org.eclipse.scout.http.servletfilter.security.LDAPSecurityFilter#failover=false
+
org.eclipse.scout.http.servletfilter.security.LDAPSecurityFilter#realm=developmnet
+
org.eclipse.scout.http.servletfilter.security.LDAPSecurityFilter#ldapServer=ldap://ldap.company.com:389
+
org.eclipse.scout.http.servletfilter.security.LDAPSecurityFilter#ldapBaseDN=dc=company,dc=com
+
org.eclipse.scout.http.servletfilter.security.LDAPSecurityFilter#lDAPgroupDN=???
+
org.eclipse.scout.http.servletfilter.security.LDAPSecurityFilter#lDAPgroupAttributeId=cn
+
</pre>
+
'''''Note:''' It is not yet quite clear what value is needed for the lDAPgroupDN parameter.''
+
 
+
The LDAPSecurityFilter connects to the LDAP server anonymously. This may well work for many servers, but the some LDAP servers only return empty responses when queried anonymously.
+
 
+
= LDAPExSecurityFilter  =
+
 
+
To work around this problem, an extended LDAPSecurityFilter which connects to the LDAP server using a pre-configured system user can be used.
+
<pre>package org.eclipse.minicrm.server.services.custom.security;
+
 
+
import java.io.IOException;
+
import java.util.Enumeration;
+
import java.util.Hashtable;
+
import javax.naming.Context;
+
import javax.naming.NamingEnumeration;
+
import javax.naming.NamingException;
+
import javax.naming.directory.DirContext;
+
import javax.naming.directory.InitialDirContext;
+
import javax.naming.directory.SearchControls;
+
import javax.naming.directory.SearchResult;
+
import javax.servlet.FilterConfig;
+
import javax.servlet.ServletException;
+
import javax.servlet.http.HttpServletRequest;
+
import javax.servlet.http.HttpServletResponse;
+
import org.eclipse.scout.commons.Base64Utility;
+
import org.eclipse.scout.commons.logger.IScoutLogger;
+
import org.eclipse.scout.commons.logger.ScoutLogManager;
+
import org.eclipse.scout.commons.security.SimplePrincipal;
+
import org.eclipse.scout.http.servletfilter.FilterConfigInjection;
+
import org.eclipse.scout.http.servletfilter.security.AbstractChainableSecurityFilter;
+
import org.eclipse.scout.http.servletfilter.security.PrincipalHolder;
+
/**
+
* LDAPSecurityFilter The following properties can be set in the config.ini file:
+
*
+
* &lt;fully qualified name of class&gt;#active=true/false might be set in the extension point
+
* &lt;fully qualified name of class&gt;#realm=abcde required
+
* &lt;fully qualified name of class&gt;#failover=true/false default false
+
* &lt;fully qualified name of class&gt;#ldapDirectory=[e.g. ldap://100.100.29.4:389] required
+
* &lt;fully qualified name of class&gt;#ldapBaseDN=[e.g. dc=bridgesolutions,dc=net] required
+
* &lt;fully qualified name of class&gt;#ldapSearchFilter=[e.g. (uid=%s)] required
+
* &lt;fully qualified name of class&gt;#ldapSearchBindPassword=[e.g. changeme]
+
* &lt;fully qualified name of class&gt;#m_searchBindDN=[e.g. cn=readonly,dc=bridgesolutions,dc=net]
+
*
+
* based on org.eclipse.scout.http.servletfilter.security.LDAPSecurityFilter
+
*/
+
 
+
public class LDAPExSecurityFilter extends AbstractChainableSecurityFilter {
+
private static final IScoutLogger LOG = ScoutLogManager.getLogger(LDAPExSecurityFilter.class);
+
public static final String PROP_BASIC_ATTEMPT = "LDAPExSecurityFilter.basicAttempt";
+
 
+
private String m_directory;
+
private String m_baseDn;
+
private String m_searchFilter;
+
private String m_searchBindPassword;
+
private String m_searchBindDN;
+
 
+
@Override
+
public void init(FilterConfig config0) throws ServletException {
+
  super.init(config0);
+
  FilterConfigInjection.FilterConfig config = new FilterConfigInjection(config0, getClass()).getAnyConfig();
+
  m_directory = getParam(config, "ldapDirectory", false);
+
  m_baseDn = getParam(config, "ldapBaseDN", false);
+
  m_searchFilter = getParam(config, "ldapSearchFilter", false);
+
  m_searchBindPassword = getParam(config, "ldapSearchBindPassword", true);
+
  m_searchBindDN = getParam(config, "ldapSearchBindDN", true);
+
}
+
 
+
protected String getParam(FilterConfig filterConfig, String paramName, boolean nullAllowed) throws ServletException {
+
  String paramValue = filterConfig.getInitParameter(paramName);
+
  boolean exists = false;
+
  if (paramValue == null &amp;&amp; nullAllowed) { // check if parameter exists
+
    Enumeration initParameterNames = filterConfig.getInitParameterNames();
+
    while (initParameterNames.hasMoreElements() &amp;&amp; exists == false) {
+
      String object = (String) initParameterNames.nextElement();
+
      exists = object.equals(paramName);
+
    }
+
  }
+
  if (paramValue == null &amp;&amp;&nbsp;!exists) {
+
    throw new ServletException("Missing init-param with name '" + paramName + "'.");
+
  }
+
  return paramValue;
+
}
+
 
+
@Override
+
protected int negotiate(HttpServletRequest req, HttpServletResponse resp, PrincipalHolder holder) throws IOException, ServletException {
+
  String h = req.getHeader("Authorization");
+
  if (h&nbsp;!= null &amp;&amp; h.matches("Basic .*")) {
+
    String[] a = new String(Base64Utility.decode(h.substring(6)), "ISO-8859-1").split(":", 2);
+
    String user = a[0].toLowerCase();
+
    String pass = a[1];
+
    if (user&nbsp;!= null &amp;&amp; pass&nbsp;!= null) {
+
      if (login(user, pass, m_directory, m_baseDn, m_searchBindDN, m_searchBindPassword, m_searchFilter)) {
+
        // success
+
        holder.setPrincipal(new SimplePrincipal(user));
+
        return STATUS_CONTINUE_WITH_PRINCIPAL;
+
      }
+
    }
+
  }
+
  int attempts = getBasicAttempt(req);
+
  if (attempts &gt; 2) {
+
    return STATUS_CONTINUE_CHAIN;
+
  }
+
  else {
+
    setBasicAttept(req, attempts + 1);
+
    resp.setHeader("WWW-Authenticate", "Basic realm=\"" + getRealm() + "\"");
+
    return STATUS_CONTINUE_CHAIN;
+
  }
+
}
+
 
+
private int getBasicAttempt(HttpServletRequest req) {
+
  int basicAtttempt = 0;
+
  Object attribute = req.getSession().getAttribute(PROP_BASIC_ATTEMPT);
+
  if (attribute instanceof Integer) {
+
    basicAtttempt = ((Integer) attribute).intValue();
+
  }
+
  return basicAtttempt;
+
}
+
 
+
private void setBasicAttept(HttpServletRequest req, int attempts) {
+
  req.getSession().setAttribute(PROP_BASIC_ATTEMPT, attempts);
+
}
+
 
+
@SuppressWarnings("unchecked")
+
protected boolean login(String username, String userPassword, String directory, String baseDN, String searchBindDN, String searchBindPassword, String searchFilter) throws ServletException {
+
  boolean isAuthenticated = false;
+
 
+
  Hashtable env = new Hashtable();
+
  env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
+
  env.put(Context.PROVIDER_URL, directory);
+
  env.put(Context.SECURITY_PRINCIPAL, searchBindDN);
+
  env.put(Context.SECURITY_CREDENTIALS, searchBindPassword);
+
 
+
  try {
+
    DirContext ctx = new InitialDirContext(env);
+
    SearchControls constraints = new SearchControls();
+
    constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
+
    String search = String.format(searchFilter, username);
+
 
+
    NamingEnumeration&lt;SearchResult&gt; results = ctx.search(baseDN, search, constraints);
+
 
+
    SearchResult sr = null;
+
 
+
    if (results&nbsp;!= null &amp;&amp; results.hasMore()) {
+
      // Step 2 - simple authentication
+
      sr = results.next();
+
 
+
      env.put(Context.SECURITY_AUTHENTICATION, "simple");
+
      if (sr.isRelative()) {
+
        env.put(Context.SECURITY_PRINCIPAL, sr.getName() + "," + baseDN);
+
      }
+
      else {
+
        env.put(Context.SECURITY_PRINCIPAL, sr.getName());
+
      }
+
      env.put(Context.SECURITY_CREDENTIALS, userPassword);
+
 
+
      try {
+
        new InitialDirContext(env);
+
        isAuthenticated = true;
+
      }
+
      catch (NamingException ne) {
+
        LOG.warn("User '" + username + "' could not be authenticated");
+
        isAuthenticated = false;
+
      }
+
    }
+
    else {
+
      LOG.warn("User '" + username + "' does not exist on LDAP");
+
      isAuthenticated = false;
+
    }
+
  }
+
  catch (NamingException ne) {
+
    LOG.error("Exception in getting user DN from LDAP: " + ne);
+
    throw new SecurityException(ne.getMessage(), ne);
+
  }
+
  return isAuthenticated;
+
}
+
}
+
</pre>
+
It must be registered in the server's plugin.xml file in the org.eclipse.scout.http.servletfilter.filters extension point
+
 
+
      &lt;filter
+
          aliases="/process"
+
          class="org.eclipse.minicrm.server.services.custom.security.LDAPExSecurityFilter"
+
          ranking="30"&gt;
+
    &lt;/filter&gt;
+
 
+
It must be configured in the server product's config.ini file:
+
<pre>org.eclipse.minicrm.server.services.custom.security.LDAPExSecurityFilter#active=true
+
org.eclipse.minicrm.server.services.custom.security.LDAPExSecurityFilter#realm=LDAP
+
org.eclipse.minicrm.server.services.custom.security.LDAPExSecurityFilter#ldapDirectory=ldap://ldap.company.com:389
+
org.eclipse.minicrm.server.services.custom.security.LDAPExSecurityFilter#ldapBaseDN=dc=company,dc=com
+
org.eclipse.minicrm.server.services.custom.security.LDAPExSecurityFilter#ldapSearchFilter=(cn=%s)
+
org.eclipse.minicrm.server.services.custom.security.LDAPExSecurityFilter#ldapSearchBindDN=cn=&lt;system user name&gt;,ou=Administrators,dc=company,dc=com
+
org.eclipse.minicrm.server.services.custom.security.LDAPExSecurityFilter#ldapSearchBindPassword=&lt;system user password&gt;
+
org.eclipse.minicrm.server.services.custom.security.LDAPExSecurityFilter#failover=false
+
org.eclipse.minicrm.server.services.custom.security.LDAPExSecurityFilter#silent=true
+
</pre>
+
<br>
+

Latest revision as of 07:37, 18 March 2024

The Scout documentation has been moved to https://eclipsescout.github.io/.

Back to the top