Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
  * JBoss, Home of Professional Open Source
  * Copyright 2005, JBoss Inc., and individual contributors as indicated
  * by the @authors tag. See the copyright.txt in the distribution for a
  * full listing of individual contributors.
  *
  * This is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as
  * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
 package org.jboss.security.auth.spi;
 
 
 
An implementation of LoginModule that authenticates against an LDAP server using JNDI, based on the configuration properties.

The LoginModule options include whatever options your LDAP JNDI provider supports. Examples of standard property names are:

  • Context.INITIAL_CONTEXT_FACTORY = "java.naming.factory.initial"
  • Context.SECURITY_PROTOCOL = "java.naming.security.protocol"
  • Context.PROVIDER_URL = "java.naming.provider.url"
  • Context.SECURITY_AUTHENTICATION = "java.naming.security.authentication"

The Context.SECURITY_PRINCIPAL is set to the distinguished name of the user as obtained by the callback handler and the Context.SECURITY_CREDENTIALS property is either set to the String password or Object credential depending on the useObjectCredential option.

Additional module properties include:

  • principalDNPrefix, principalDNSuffix : A prefix and suffix to add to the username when forming the user distinguished name. This is useful if you prompt a user for a username and you don't want them to have to enter the fully distinguished name. Using this property and principalDNSuffix the userDN will be formed as:
        String userDN = principalDNPrefix + username + principalDNSuffix;
     
  • useObjectCredential : indicates that the credential should be obtained as an opaque Object using the org.jboss.security.plugins.ObjectCallback type of Callback rather than as a char[] password using a JAAS PasswordCallback.
  • rolesCtxDN : The fixed distinguished name to the context to search for user roles.
  • userRolesCtxDNAttributeName : The name of an attribute in the user object that contains the distinguished name to the context to search for user roles. This differs from rolesCtxDN in that the context to search for a user's roles can be unique for each user.
  • uidAttributeID : The name of the attribute that in the object containing the user roles that corresponds to the userid. This is used to locate the user roles.
  • matchOnUserDN : A flag indicating if the search for user roles should match on the user's fully distinguished name. If false just the username is used as the match value. If true, the userDN is used as the match value.
  • allowEmptyPasswords : A flag indicating if empty(length==0) passwords should be passed to the LDAP server. An empty password is treated as an anonymous login by some LDAP servers and this may not be a desirable feature. Set this to false to reject empty passwords, true to have the ldap server validate the empty password. The default is true.
  • roleAttributeIsDN : A flag indicating whether the user's role attribute contains the fully distinguished name of a role object, or the users's role attribute contains the role name. If false, the role name is taken from the value of the user's role attribute. If true, the role attribute represents the distinguished name of a role object. The role name is taken from the value of the `roleNameAttributeId` attribute of the corresponding object. In certain directory schemas (e.g., Microsoft Active Directory), role (group) attributes in the user object are stored as DNs to role objects instead of as simple names, in which case, this property should be set to true. The default value of this property is false.
  • roleNameAttributeID : The name of the attribute of the role object which corresponds to the name of the role. If the `roleAttributeIsDN` property is set to true, this property is used to find the role object's name attribute. If the `roleAttributeIsDN` property is set to false, this property is ignored.
  • java.naming.security.principal (4.0.3+): This standard JNDI property if specified in the login configuration, it is used to rebind to the ldap server after user authentication for the role searches. This may be necessary if the user does not have permission to perform these queres. If specified, the java.naming.security.credentials provides the rebind credentials.
  • java.naming.security.credentials (4.0.3+): This standard JNDI property if specified in the login configuration, it is used to rebind to the LDAP server after user authentication for the role searches along with the java.naming.security.principal value. This can be encrypted using the jaasSecurityDomain.
  • jaasSecurityDomain (4.0.3+): The JMX ObjectName of the JaasSecurityDomain to use to decrypt the java.naming.security.principal. The encrypted form of the password is that returned by the JaasSecurityDomain#encrypt64(byte[]) method. The org.jboss.security.plugins.PBEUtils can also be used to generate the encrypted form.
A sample login config:

testLdap {
org.jboss.security.auth.spi.LdapLoginModule required
java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
java.naming.provider.url="ldap://ldaphost.jboss.org:1389/"
java.naming.security.authentication=simple
principalDNPrefix=uid=
uidAttributeID=userid
roleAttributeID=roleName
principalDNSuffix=,ou=People,o=jboss.org
rolesCtxDN=cn=JBossSX Tests,ou=Roles,o=jboss.org
};

testLdap2 {
org.jboss.security.auth.spi.LdapLoginModule required
java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
java.naming.provider.url="ldap://ldaphost.jboss.org:1389/"
java.naming.security.authentication=simple
principalDNPrefix=uid=
uidAttributeID=userid
roleAttributeID=roleName
principalDNSuffix=,ou=People,o=jboss.org
userRolesCtxDNAttributeName=ou=Roles,dc=user1,dc=com
};

testLdapToActiveDirectory {
org.jboss.security.auth.spi.LdapLoginModule required
java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
java.naming.provider.url="ldap://ldaphost.jboss.org:1389/"
java.naming.security.authentication=simple
rolesCtxDN=cn=Users,dc=ldaphost,dc=jboss,dc=org
uidAttributeID=userPrincipalName
roleAttributeID=memberOf
roleAttributeIsDN=true
roleNameAttributeID=name
};

Author(s):
Scott.Stark@jboss.org
Version:
$Revision: 79395 $
   private static final String PRINCIPAL_DN_PREFIX_OPT = "principalDNPrefix";
   private static final String PRINCIPAL_DN_SUFFIX_OPT = "principalDNSuffix";
   private static final String ROLES_CTX_DN_OPT = "rolesCtxDN";
   private static final String USER_ROLES_CTX_DN_ATTRIBUTE_ID_OPT = "userRolesCtxDNAttributeName";
   private static final String UID_ATTRIBUTE_ID_OPT = "uidAttributeID";
   private static final String ROLE_ATTRIBUTE_ID_OPT = "roleAttributeID";
   private static final String MATCH_ON_USER_DN_OPT = "matchOnUserDN";
   private static final String ROLE_ATTRIBUTE_IS_DN_OPT = "roleAttributeIsDN";
   private static final String ROLE_NAME_ATTRIBUTE_ID_OPT = "roleNameAttributeID";
   private static final String SEARCH_TIME_LIMIT_OPT = "searchTimeLimit";
   private static final String SEARCH_SCOPE_OPT = "searchScope";
   private static final String SECURITY_DOMAIN_OPT = "jaasSecurityDomain";
   public LdapLoginModule()
   {
   }
   private transient SimpleGroup userRoles = new SimpleGroup("Roles");

   
Overridden to return an empty password string as typically one cannot obtain a user's password. We also override the validatePassword so this is ok.

Returns:
and empty password String
   protected String getUsersPassword() throws LoginException
   {
      return "";
   }

   
Overridden by subclasses to return the Groups that correspond to the to the role sets assigned to the user. Subclasses should create at least a Group named "Roles" that contains the roles assigned to the user. A second common group is "CallerPrincipal" that provides the application identity of the user rather than the security domain identity.

Returns:
Group[] containing the sets of roles
   protected Group[] getRoleSets() throws LoginException
   {
      Group[] roleSets = {};
      return roleSets;
   }

   
Validate the inputPassword by creating a ldap InitialContext with the SECURITY_CREDENTIALS set to the password.

Parameters:
inputPassword the password to validate.
expectedPassword ignored
   protected boolean validatePassword(String inputPasswordString expectedPassword)
   {
      boolean trace = .isTraceEnabled();
      boolean isValid = false;
      if (inputPassword != null)
      {
         // See if this is an empty password that should be disallowed
         if (inputPassword.length() == 0)
         {
            // Check for an allowEmptyPasswords option
            boolean allowEmptyPasswords = true;
            String flag = (String.get("allowEmptyPasswords");
            if (flag != null)
               allowEmptyPasswords = Boolean.valueOf(flag).booleanValue();
            if (allowEmptyPasswords == false)
            {
               if (trace)
                  .trace("Rejecting empty password due to allowEmptyPasswords");
               return false;
            }
         }
         try
         {
            // Validate the password by trying to create an initial context
            String username = getUsername();
            createLdapInitContext(usernameinputPassword);
            isValid = true;
         }
         catch (Throwable e)
         {
            super.setValidateError(e);
         }
      }
      return isValid;
   }
   @SuppressWarnings("unchecked")
   private void createLdapInitContext(String usernameObject credentialthrows Exception
   {
      boolean trace = .isTraceEnabled();
      Properties env = new Properties();
      // Map all option into the JNDI InitialLdapContext env
      Iterator iter = .entrySet().iterator();
      while (iter.hasNext())
      {
         Entry entry = (Entryiter.next();
         env.put(entry.getKey(), entry.getValue());
      }
      // Set defaults for key values if they are missing
      String factoryName = env.getProperty(.);
      if (factoryName == null)
      {
         factoryName = "com.sun.jndi.ldap.LdapCtxFactory";
         env.setProperty(.factoryName);
      }
      String authType = env.getProperty(.);
      if (authType == null)
         env.setProperty(."simple");
      String protocol = env.getProperty(.);
      String providerURL = (String.get(.);
      if (providerURL == null)
         providerURL = "ldap://localhost:" + ((protocol != null && protocol.equals("ssl")) ? "636" : "389");
      String bindDN = (String.get(.);
      String bindCredential = (String.get(.);
      String securityDomain = (String.get();
      if (securityDomain != null)
      {
         ObjectName serviceName = new ObjectName(securityDomain);
         char[] tmp = DecodeAction.decode(bindCredentialserviceName);
         bindCredential = new String(tmp);
      }
      String principalDNPrefix = (String.get();
      if (principalDNPrefix == null)
         principalDNPrefix = "";
      String principalDNSuffix = (String.get();
      if (principalDNSuffix == null)
         principalDNSuffix = "";
      String matchType = (String.get();
      boolean matchOnUserDN = Boolean.valueOf(matchType).booleanValue();
      String userDN = principalDNPrefix + username + principalDNSuffix;
      env.setProperty(.providerURL);
      env.setProperty(.userDN);
      env.put(.credential);
      if (trace)
      {
         Properties tmp = new Properties();
         tmp.putAll(env);
         tmp.setProperty(."***");
         if (trace)
            .trace("Logging into LDAP server, env=" + tmp.toString());
      }
      InitialLdapContext ctx = null;
      try
      {
         ctx = new InitialLdapContext(envnull);
         if (trace)
            .trace("Logged into LDAP server, " + ctx);
         if (bindDN != null)
         {
            // Rebind the ctx to the bind dn/credentials for the roles searches
            if (trace)
               .trace("Rebind SECURITY_PRINCIPAL to: " + bindDN);
            env.setProperty(.bindDN);
            env.put(.bindCredential);
            ctx = new InitialLdapContext(envnull);
         }
         /* If a userRolesCtxDNAttributeName was speocified, see if there is a
          user specific roles DN. If there is not, the default rolesCtxDN will
          be used.
          */
         String rolesCtxDN = (String.get();
         String userRolesCtxDNAttributeName = (String.get();
         if (userRolesCtxDNAttributeName != null)
         {
            // Query the indicated attribute for the roles ctx DN to use
            String[] returnAttribute = {userRolesCtxDNAttributeName};
            try
            {
               Attributes result = ctx.getAttributes(userDNreturnAttribute);
               if (result.get(userRolesCtxDNAttributeName) != null)
               {
                  rolesCtxDN = result.get(userRolesCtxDNAttributeName).get().toString();
                  if (trace)
                     .trace("Found user roles context DN: " + rolesCtxDN);
               }
            }
            catch (NamingException e)
            {
               if (trace)
                  .debug("Failed to query userRolesCtxDNAttributeName"e);
            }
         }
         // Search for any roles associated with the user
         if (rolesCtxDN != null)
         {
            String uidAttrName = (String.get();
            if (uidAttrName == null)
               uidAttrName = "uid";
            String roleAttrName = (String.get();
            if (roleAttrName == null)
               roleAttrName = "roles";
            StringBuffer roleFilter = new StringBuffer("(");
            roleFilter.append(uidAttrName);
            roleFilter.append("={0})");
            String userToMatch = username;
            if (matchOnUserDN == true)
               userToMatch = userDN;
            String[] roleAttr = {roleAttrName};
            // Is user's role attribute a DN or the role name
            String roleAttributeIsDNOption = (String.get();
            boolean roleAttributeIsDN = Boolean.valueOf(roleAttributeIsDNOption).booleanValue();
            // If user's role attribute is a DN, what is the role's name attribute
            // Default to 'name' (Group name attribute in Active Directory)
            String roleNameAttributeID = (String.get();
            if (roleNameAttributeID == null)
               roleNameAttributeID = "name";
            int searchScope = .;
            int searchTimeLimit = 10000;
            String timeLimit = (String.get();
            if (timeLimit != null)
            {
               try
               {
                  searchTimeLimit = Integer.parseInt(timeLimit);
               }
               catch (NumberFormatException e)
               {
                  if (trace)
                     .trace("Failed to parse: " + timeLimit + ", using searchTimeLimit=" + searchTimeLimite);
               }
            }
            String scope = (String.get();
            if ("OBJECT_SCOPE".equalsIgnoreCase(scope))
               searchScope = .;
            else if ("ONELEVEL_SCOPE".equalsIgnoreCase(scope))
               searchScope = .;
            if ("SUBTREE_SCOPE".equalsIgnoreCase(scope))
               searchScope = .;
            NamingEnumeration answer = null;
            try
            {
               SearchControls controls = new SearchControls();
               controls.setSearchScope(searchScope);
               controls.setReturningAttributes(roleAttr);
               controls.setTimeLimit(searchTimeLimit);
               Object[] filterArgs = {userToMatch};
               if (trace)
               {
                  .trace("searching rolesCtxDN=" + rolesCtxDN + ", roleFilter=" + roleFilter + ", filterArgs="
                        + userToMatch + ", roleAttr=" + roleAttr + ", searchScope=" + searchScope
                        + ", searchTimeLimit=" + searchTimeLimit);
               }
               answer = ctx.search(rolesCtxDNroleFilter.toString(), filterArgscontrols);
               while (answer.hasMore())
               {
                  SearchResult sr = (SearchResultanswer.next();
                  if (trace)
                  {
                     .trace("Checking answer: " + sr.getName());
                  }
                  Attributes attrs = sr.getAttributes();
                  Attribute roles = attrs.get(roleAttrName);
                  if (roles != null)
                  {
                     for (int r = 0; r < roles.size(); r++)
                     {
                        Object value = roles.get(r);
                        String roleName = null;
                        if (roleAttributeIsDN == true)
                        {
                           // Query the roleDN location for the value of roleNameAttributeID
                           String roleDN = value.toString();
                           String[] returnAttribute = {roleNameAttributeID};
                           if (trace)
                              .trace("Following roleDN: " + roleDN);
                           try
                           {
                              Attributes result2 = ctx.getAttributes(roleDNreturnAttribute);
                              Attribute roles2 = result2.get(roleNameAttributeID);
                              if (roles2 != null)
                              {
                                 for (int m = 0; m < roles2.size(); m++)
                                 {
                                    roleName = (Stringroles2.get(m);
                                    addRole(roleName);
                                 }
                              }
                           }
                           catch (NamingException e)
                           {
                              if (trace)
                                 .trace("Failed to query roleNameAttrName"e);
                           }
                        }
                        else
                        {
                           // The role attribute value is the role name
                           roleName = value.toString();
                           addRole(roleName);
                        }
                     }
                  }
                  else
                  {
                     if (trace)
                        .trace("No attribute " + roleAttrName + " found in " + sr.getName());
                  }
               }
            }
            catch (NamingException e)
            {
               if (trace)
                  .trace("Failed to locate roles"e);
            }
            finally
            {
               if (answer != null)
                  answer.close();
            }
         }
      }
      finally
      {
         // Close the context to release the connection
         if (ctx != null)
            ctx.close();
      }
   }
   private void addRole(String roleName)
   {
      boolean trace = .isTraceEnabled();
      if (roleName != null)
      {
         try
         {
            Principal p = super.createIdentity(roleName);
            if (trace)
               .trace("Assign user to role " + roleName);
            .addMember(p);
         }
         catch (Exception e)
         {
            .debug("Failed to create principal: " + roleNamee);
         }
      }
   }
New to GrepCode? Check out our FAQ X