Announcement Announcement Module
Collapse
No announcement yet.
LDAPPasswordAuthenticationDao problem Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • LDAPPasswordAuthenticationDao problem

    Hi,
    i'm using the acegi security framework in order to authenticate
    and manage rights with an ldap backend.
    I expected that LDAPPasswordAuthenticationDao will give me some
    solutions about that but it seems to be not the case.

    In fact, the
    LDAPPasswordAuthenticationDao class has a lot of limitations and problems.
    For instance, you cannot logon with the uid or sAMAccountName.
    if you have a password like a%6(t_ => it doesn't work.
    I will have to rewrite it completely for my own use...
    I think Tomcat JNDIRealm.java is far better.

    I expect that acegi will have a better ldap dao integration. It's necessary to be deployed in an enterprise environment.

    Best regards.

  • #2
    Re: LDAPPasswordAuthenticationDao problem

    Originally posted by benoit_m35
    I think Tomcat JNDIRealm.java is far better.
    Originally posted by benoit_m35
    I expect that acegi will have a better ldap dao integration.
    I don't find these sort of comments very helpful. By all means you're entitled to an opinion, but if you would like help it generally assists to be polite.

    Acegi Security's LDAP support classes are in the sandbox. They are not finalised.

    If you wish to provide a contribution, we would be happy to apply it to CVS.

    I should also add that I didn't personally write the LDAP support classes, so I'm not in a position to answer the specifics of your post.

    Comment


    • #3
      I expect that the LDAP code will get better. I have had little time to work with it lately, but I am always open to suggestions and feedback.

      Please note that I have started a set of unit tests which aim to simulate various configurations. If I could get a sample of your LDAP setup, or even a pointer to more information I will gladly work on integrating these and updating the code for the LDAP Dao to support your use case.

      Comment


      • #4
        thanks

        sorry for my first post. I 've modify the code to allow authentication with the sAMAccountName field. It works good. The code is very maintenable. Thanks for this pretty system.

        -- Benoît.

        Comment


        • #5
          Originally posted by benoit_m35
          sorry for my first post. I 've modify the code to allow authentication with the sAMAccountName field. It works good. The code is very maintenable. Thanks for this pretty system.

          -- Benoît.
          Hi Benoit,

          Please post your code. I'm trying to do exactly the same thing and your help would enable me to get up and running much faster!

          Comment


          • #6
            Also, if you could post the code I will try and get it integrated into the LDAP DAO as soon as I can.

            Comment


            • #7
              the code

              Hi,
              here is the code which allow me to authenticate a user with his sAMAccountName instead of using the distinguished name.
              As in almost authentication process, the manager do a bind in order to search for the user account with the sAMAccountName. Then the authentication process bind the user with the dn found (to authenticate). I prefer this solution which could fit with many configuration. In fact, i think that ldapPawwordAuthenticaton should work as this :
              the xml config will have :
              - the server url + port
              - the admin or an anonymous dn
              - the password for this account
              - a userBase
              - a userSearch attribute (wich attribute for searching the authenticating user) ex :"sAMAccountName={0}"
              - no user context
              - all the roles attributes defined by acegi at the moment (very good)

              the authentication could be achieve by re-binding the user or comparing the password.

              But for the moment, LdapPasswordAuthenticationDao works fine in almost case and i'm happy to see that acegi allow me to secure my J2EE application.

              Thaks all.


              Code:
              public class LdapPasswordAuthenticationDao implements PasswordAuthenticationDao {
                  static Hashtable superUserEnv;
              
                  static DirContext superUserCtx;
                  static {
                      superUserEnv = new Hashtable();
                      superUserEnv.put(Context.INITIAL_CONTEXT_FACTORY,
                              "com.sun.jndi.ldap.LdapCtxFactory");
                      superUserEnv.put(Context.PROVIDER_URL, "ldap://annu.crbdom:389");
                      superUserEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
                      superUserEnv.put(Context.SECURITY_PRINCIPAL,
                              "cn=administrateur,cn=users,dc=crbdom"); // specify the username
                      superUserEnv.put(Context.SECURITY_CREDENTIALS, "password"); // specify
                                                                                 // the
                                                                                 // password
                     
              
                  }
                  public LdapPasswordAuthenticationDao() throws NamingException{
                      superUserCtx = new InitialDirContext(superUserEnv);
                  }
                  /** InnerClass used to keep context variable together. */
                  private class UserContext {
                      public DirContext dirContext;
              
                      public String userPrincipal;
              
                      /**
                       * Get the attribute(s) to match when searching for the user object.
                       * This implementation returns a "distinguishedName" attribute with the
                       * value returned by <code>getUserPrincipal&#40;username&#41;</code>. A
                       * subclass may customize this behavior by overriding
                       * <code>getUserPrincipal</code> and/or
                       * <code>getUsernameAttributes</code>.
                       * 
                       * @param username
                       *            DOCUMENT ME!
                       * 
                       * @return DOCUMENT ME!
                       */
                      public Attributes getUsernameAttributes&#40;&#41; &#123;
                          Attributes matchAttrs = new BasicAttributes&#40;true&#41;; // ignore case
                          matchAttrs.put&#40;new BasicAttribute&#40;"distinguishedName",
                                  userPrincipal&#41;&#41;;
                          
                          return matchAttrs;
                      &#125;
                  &#125;
              
                  public static final String BAD_CREDENTIALS_EXCEPTION_MESSAGE = "Invalid username, password or context";
              
                  private static final transient Log log = LogFactory
                          .getLog&#40;LdapPasswordAuthenticationDao.class&#41;;
              
                  /** Type of authentication within LDAP; default is simple. */
                  private String authenticationType = "simple";
              
                  /**
                   * If set to a non-null value, and a user can be bound to a LDAP Conext, but
                   * no role information is found then this role is automatically added. If
                   * null &#40;the default&#41; then a BadCredentialsException is thrown
                   * 
                   * <p>
                   * For example; if you have an LDAP directory with no role information
                   * stored, you might simply want to give any user who can login a role of
                   * "USER".
                   * </p>
                   */
                  private String defaultRole = null;
              
                  /**
                   * The INITIAL_CONTEXT_FACTORY used to create the JNDI Factory. Default is
                   * "com.sun.jndi.ldap.LdapCtxFactory"; you <b>should not </b> need to set
                   * this unless you have unusual needs.
                   */
                  private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
              
                  /** Internal variable, concatenation */
                  private String providerUrl;
              
                  /**
                   * Used to build LDAP Search Filter for finding roles &#40;in the roleContexts&#41;
                   * pointing to a user. Uses MessageFormat like tokens; &#123;0&#125; is the user's
                   * DistiguishedName, &#123;1&#125; is the user's username. For more information on
                   * syntax see javax.naming.directory.DirContext.search&#40;&#41;, or RFC 2254.
                   * 
                   * <p>
                   * Example&#58; if each group has an attribute 'memberUid' with values being the
                   * usernames of the user's in that group, then the value of this property
                   * would be <b>&#40;memberUid=&#123;1&#125;&#41; </b>
                   * </p>
                   */
                  private String roleAttributesSearchFilter;
              
                  /**
                   * Contexts to search for role's &#40;which point to the user id&#41;.
                   * <p>
                   * Example, if you have a Groups object containing Groups of users then the
                   * expression&#58; <b>ou=Groups,dc=mycompany,dc=com </b> might be used;
                   * alternatively, if rootContext="dc=mycompany,dc=com" then simply use
                   * "ou=Groups" here.
                   */
                  private String&#91;&#93; roleContexts;
              
                  /**
                   * Attribute&#40;s&#41; of any role object returned from the roleContexts to use as
                   * role-names. <b>Warning&#58; </b> if you do role lookups using the
                   * roleContexts and roleAttributesSearchFilter then you need to set
                   * roleNameAttributes or ALL attributes will be returned.
                   *  
                   */
                  private String&#91;&#93; roleNameAttributes;
              
                  /**
                   * Prefix to be associated with any roles found for a user, defaults to an
                   * empty string. Older versions of this class used "ROLE_" for this value.
                   */
                  private String rolePrefix = "";
              
                  /**
                   * Suffix to be associated with any roles found for a user, defaults to an
                   * empty string.
                   */
                  private String roleSuffix = "";
              
                  /**
                   * Root context of the LDAP Connection, if any is needed.
                   * <p>
                   * Example&#58; <b>dc=mycompany,dc=com </b>
                   * </p>
                   * <p>
                   * <strong>Note&#58; </strong> It is usually preferable to add this data as part
                   * of the userContexts and/or roleContexts attributes.
                   * </p>
                   */
                  private String rootContext = "";
              
                  /**
                   * If true then all role name values returned from the directory will be
                   * converted to uppercase.
                   */
                  private boolean upperCaseRoleNames = false;
              
                  /**
                   * LDAP URL &#40;without the port&#41; of the LDAP server to connect to; example
                   * <b>ldap&#58;//dir.mycompany.com&#58;389/ </b> &#40;port 389 is the standard LDAP
                   * port&#41;.
                   */
                  private String url;
              
                  /**
                   * One or more LDAP Contexts which contain user account information, use the
                   * MessageFormat key &#123;0&#125; to denote location where the user's username should
                   * be inserted into the expression to create a DistiguishedName.
                   * <p>
                   * Example&#58;
                   * <p>
                   * cn=&#123;0&#125;,ou=Users,dc=mycompnay,dc=com </b>
                   * </p>
                   * <p>
                   * Alternatively, if you had set rootContext="dc=mycompany,dc=com" then the
                   * first example would be rewritten as <b>cn=&#123;0&#125;,ou=Users </b>.
                   * </p>
                   */
                  private MessageFormat&#91;&#93; userContexts;
              
                  /**
                   * Name&#40;s&#41; of the attribute&#40;s&#41; for a user account object contaning role
                   * names assigned to the user. Leave unset if there are none. Consult your
                   * LDAP server administrator to determine these value&#40;s&#41;.
                   *  
                   */
                  private String&#91;&#93; userRolesAttributes;
              
                  /**
                   * 
                   * @param results
                   *            Result of searching on of the roleContexts for matches against
                   *            the current user.
                   * @param roles
                   *            List of roles the user has already been assigned.
                   * @throws NamingException
                   */
                  protected void addAnyRolesFound&#40;NamingEnumeration results, Collection roles&#41;
                          throws NamingException &#123;
                      while &#40;results.hasMore&#40;&#41;&#41; &#123;
                          SearchResult result = &#40;SearchResult&#41; results.next&#40;&#41;;
                          Attributes attrs = result.getAttributes&#40;&#41;;
                          if &#40;attrs == null&#41; &#123;
                              continue;
                          &#125;
                          // Here we loop over the attributes returned in the SearchResult
                          // TODO replace with Utility method call&#58;
                          NamingEnumeration e = attrs.getAll&#40;&#41;;
                          while &#40;e.hasMore&#40;&#41;&#41; &#123;
                              Attribute a = &#40;Attribute&#41; e.next&#40;&#41;;
                              for &#40;int i = 0; i < a.size&#40;&#41;; i++&#41; &#123;
                                  roles.add&#40;&#40;String&#41; a.get&#40;i&#41;&#41;;
                              &#125;
                          &#125;
                      &#125;
                  &#125;
              
                  /**
                   * @return Returns the defaultRole.
                   */
                  public String getDefaultRole&#40;&#41; &#123;
                      return defaultRole;
                  &#125;
              
                  /**
                   * Get an array <code>GrantedAuthorities</code> given the list of roles
                   * obtained from the LDAP context. Delegates to
                   * <code>getGrantedAuthority&#40;String ldapRole&#41;</code>. This function may
                   * be overridden in a subclass.
                   * 
                   * @param ldapRoles
                   *            DOCUMENT ME!
                   * 
                   * @return DOCUMENT ME!
                   */
                  protected GrantedAuthority&#91;&#93; getGrantedAuthorities&#40;String&#91;&#93; ldapRoles&#41; &#123;
                      GrantedAuthority&#91;&#93; grantedAuthorities = new GrantedAuthority&#91;ldapRoles.length&#93;;
              
                      for &#40;int i = 0; i < ldapRoles.length; i++&#41; &#123;
                          grantedAuthorities&#91;i&#93; = getGrantedAuthority&#40;ldapRoles&#91;i&#93;&#41;;
                      &#125;
              
                      return grantedAuthorities;
                  &#125;
              
                  /**
                   * Get a <code>GrantedAuthority</code> given a role obtained from the LDAP
                   * context. If found in the LDAP role, the following characters are
                   * converted to underscore&#58; ',' &#40;comma&#41;, '=' &#40;equals&#41;, ' ' &#40;space&#41; This
                   * function may be overridden in a subclass.
                   * 
                   * @param ldapRole
                   *            DOCUMENT ME!
                   * 
                   * @return DOCUMENT ME!
                   */
                  protected GrantedAuthority getGrantedAuthority&#40;String ldapRole&#41; &#123;
                      String roleName = rolePrefix + ldapRole.toUpperCase&#40;&#41; + roleSuffix;
                      if &#40;upperCaseRoleNames&#41; &#123;
                          roleName = roleName.toUpperCase&#40;&#41;;
                      &#125;
                      GrantedAuthority ga = new GrantedAuthorityImpl&#40;roleName&#41;;
              
                      if &#40;log.isDebugEnabled&#40;&#41;&#41; &#123;
                          log.debug&#40;"GrantedAuthority&#58; " + ga&#41;;
                      &#125;
              
                      return ga;
                  &#125;
              
                  /**
                   * @return The InitialContextFactory for creating the root JNDI context;
                   *         defaults to "com.sun.jndi.ldap.LdapCtxFactory"
                   */
                  public String getInitialContextFactory&#40;&#41; &#123;
                      return initialContextFactory;
                  &#125;
              
                  // ~ Methods
                  // ================================================================
              
                  /**
                   * Given a password, construct the Hashtable of JNDI values for a bind
                   * attempt.
                   */
                  protected Hashtable getJdniEnvironment&#40;String password&#41; &#123;
                      Hashtable env = new Hashtable&#40;11&#41;;
                      env.put&#40;Context.INITIAL_CONTEXT_FACTORY, initialContextFactory&#41;;
                      env.put&#40;Context.PROVIDER_URL, getProviderURL&#40;&#41;&#41;;
                      env.put&#40;Context.SECURITY_AUTHENTICATION, authenticationType&#41;;
                      env.put&#40;Context.SECURITY_CREDENTIALS, password&#41;;
                      return env;
                  &#125;
              
                  /**
                   * @return The full "Provuder" URL for the LDAP source; it should look
                   *         something like&#58; ldap&#58;//www.mycompany.com&#58;389/
                   */
                  public synchronized String getProviderURL&#40;&#41; &#123;
                      if &#40;null == this.providerUrl&#41; &#123;
                          StringBuffer providerUrl = new StringBuffer&#40;this.url&#41;;
                          if &#40;!this.url.endsWith&#40;"/"&#41;&#41; &#123;
                              providerUrl.append&#40;"/"&#41;;
                          &#125;
                          providerUrl.append&#40;this.rootContext&#41;;
                          this.providerUrl = providerUrl.toString&#40;&#41;;
                      &#125;
                      return this.providerUrl;
                  &#125;
              
                  /**
                   * @return Returns the roleUserAttributes.
                   */
                  public String getRoleAttributesSearchFilter&#40;&#41; &#123;
                      return roleAttributesSearchFilter;
                  &#125;
              
                  /**
                   * @return Array of MessageFormat String's for Contexts that store role
                   *         information for users.
                   */
                  public String&#91;&#93; getRoleContexts&#40;&#41; &#123;
                      return roleContexts;
                  &#125;
              
                  /**
                   * @return Returns the roleNameAttributes.
                   */
                  public String&#91;&#93; getRoleNameAttributes&#40;&#41; &#123;
                      return roleNameAttributes;
                  &#125;
              
                  /**
                   * @return Returns the rolePrefix.
                   */
                  public String getRolePrefix&#40;&#41; &#123;
                      return rolePrefix;
                  &#125;
              
                  protected Collection getRolesFromRoleSearch&#40;UserContext userContext, String&#91;&#93; roleAttributes&#41; &#123;
                      if &#40;&#40;null == roleContexts&#41; || &#40;roleContexts.length == 0&#41;&#41; &#123;
                          System.out.println&#40;"role context vaut null &#58; "&#41;;
                          
                          return null;
                      &#125;
                      String&#91;&#93; searchFilterVars = new String&#91;&#93; &#123; userContext.userPrincipal&#125;;
              
                      SearchControls controls = new SearchControls&#40;&#41;;
                      controls.setSearchScope&#40;SearchControls.SUBTREE_SCOPE&#41;;
                    // controls.setReturningAttributes&#40;roleAttributes&#41;;
                      System.out.println&#40;"roleAttributesSearchFilter &#58; " + roleAttributesSearchFilter&#41;;
                      
                      List roles = new ArrayList&#40;&#41;;
                      for &#40;int i = 0; i < roleContexts.length; i++&#41; &#123;
                          try &#123; 
                              System.out.println&#40;"le role contexte vaut &#58; " + roleContexts&#91;i&#93; &#41;;
                              System.out.println&#40;"le role attribute search vaut &#58; " + roleAttributesSearchFilter&#41;;
                              System.out.println&#40;"searchFilterVars &#58; " + searchFilterVars&#91;0&#93;&#41;;
                              
                              /**NamingEnumeration results = userContext.dirContext.search&#40;
                                       roleContexts&#91;i&#93;, roleAttributesSearchFilter,
                                      searchFilterVars, controls&#41;;*/
                              NamingEnumeration results = userContext.dirContext.search&#40;
                                      "ou=Applis,dc=crbdom", "&#40;&&#40;member=CN=Benoit MORAILLON,OU=STI,OU=DFI,DC=crbdom&#41;&#41;", controls&#41;;
                             
                              addAnyRolesFound&#40;results, roles&#41;;
                          &#125; catch &#40;NamingException e&#41; &#123;
                              if &#40;log.isInfoEnabled&#40;&#41;&#41; &#123;
                                  log.info&#40;"Unable to find user-role match in context = "
                                          + roleContexts&#91;i&#93;, e&#41;;
                              &#125;
                          &#125;
                      &#125;
                      return roles;
                  &#125;
              
                  /**
                   * Looksup any roleAttributes associated with the user's DN within the
                   * DirContext.
                   * 
                   * @param userContext
                   *            UserContext Object containing DirContext in which to operate,
                   *            and the user's DistinguishedName.
                   * @param roleAttributes
                   *            Names of all attributes to search for role name information.
                   * @return Collection of roles granted to the user within the JNDI Context.
                   * @throws NamingException
                   */
                  protected Collection getRolesFromUserContext&#40;UserContext userContext,
                          String&#91;&#93; roleAttributes&#41; throws NamingException &#123;
                      List roles = new ArrayList&#40;&#41;;
                      if &#40;roleAttributes != null&#41; &#123;
                          if &#40;log.isDebugEnabled&#40;&#41;&#41; &#123;
                              StringBuffer rolesString = new StringBuffer&#40;&#41;;
              
                              for &#40;int i = 0; i < roleAttributes.length; i++&#41; &#123;
                                  rolesString.append&#40;", "&#41;;
                                  rolesString.append&#40;roleAttributes&#91;i&#93;&#41;;
                              &#125;
              
                              log.debug&#40;"Searching user context '"
                                      + userContext.userPrincipal + "' for roles "
                                      + "attributes&#58; " + rolesString.substring&#40;1&#41;&#41;;
                          &#125;
                          Attributes attrs = userContext.dirContext.getAttributes&#40;
                                  userContext.userPrincipal, roleAttributes&#41;;
                          NamingEnumeration roleEnum = attrs.getAll&#40;&#41;;
                          while &#40;roleEnum.hasMore&#40;&#41;&#41; &#123;
                              Attribute roleAttr = &#40;Attribute&#41; roleEnum.next&#40;&#41;;
                              for &#40;int i = 0; i < roleAttr.size&#40;&#41;; i++&#41; &#123;
                                  roles.add&#40;roleAttr.get&#40;i&#41;&#41;;
                              &#125;
                          &#125;
                      &#125;
                      return roles;
                  &#125;
              
                  /**
                   * @return Returns the roleSuffix.
                   */
                  public String getRoleSuffix&#40;&#41; &#123;
                      return roleSuffix;
                  &#125;
              
                  /**
                   * @return Returns the rootContext which to connect to; typically it could
                   *         look something like&#58; dc=mycompany,dc=com.
                   */
                  public String getRootContext&#40;&#41; &#123;
                      return rootContext;
                  &#125;
              
                  /**
                   * @return The LDAP URL to conntect to; example&#58;
                   *         ldap&#58;//ldap.mycompany.com&#58;389/
                   */
                  public String getURL&#40;&#41; &#123;
                      return url;
                  &#125;
              
                  protected User changeForsAMAccountName&#40;String username&#41; throws NamingException&#123;
              
                      SearchControls cons = new SearchControls&#40;&#41;;
                      cons.setSearchScope&#40;SearchControls.SUBTREE_SCOPE&#41;;
                      User user = new User&#40;&#41;;
              
                      NamingEnumeration answer = superUserCtx.search&#40;this.rootContext,
                              "&#40;&&#40;sAMAccountName=" + username + "&#41;&#41;", cons&#41;;
                      while &#40;answer.hasMore&#40;&#41;&#41; &#123;
                          SearchResult si = &#40;SearchResult&#41; answer.next&#40;&#41;;
              
                          Attributes att = si.getAttributes&#40;&#41;;
                          String uc = si.getNameInNamespace&#40;&#41;;
                          uc.replaceAll&#40;username,"&#123;0&#125;"&#41;;
                          this.setUserContext&#40;uc&#41;;
                          user.setUsername&#40;att.get&#40;"sAMAccountName"&#41;.get&#40;&#41;.toString&#40;&#41;&#41;;
                          user.setNom&#40;att.get&#40;"cn"&#41;.get&#40;&#41;.toString&#40;&#41;&#41;;
                          user.setEmail&#40;att.get&#40;"mail"&#41;.get&#40;&#41;.toString&#40;&#41;&#41;;
                          user.setTelephone&#40;att.get&#40;"telephoneNumber"&#41;.get&#40;&#41;.toString&#40;&#41;&#41;;
                          user.setService&#40;"rien"&#41;;
                          user.setDirection&#40;"rien"&#41;;
                          
                                    
                          return user;
                      &#125;
                      return null;
              
                  &#125;
              
                  /**
                   * Attempts to bind to the userContexts; returning on the first successful
                   * bind; or failing with a BadCredentialsException.
                   * 
                   * @param username
                   * @param password
                   * @return UserContext, an innerclass holding the DirContext, and the user's
                   *         LDAP Principal String.
                   * @throws NamingException
                   * @throws BadCredentialsException
                   */
                  protected UserContext getUserContext&#40;String username, String password&#41;
                          throws NamingException, BadCredentialsException &#123;
                      Hashtable env = getJdniEnvironment&#40;password&#41;;
                      UserContext userContext = new UserContext&#40;&#41;;
                      for &#40;int i = 0; i < userContexts.length; i++&#41; &#123;
                          env.remove&#40;Context.SECURITY_PRINCIPAL&#41;;
                          userContext.userPrincipal = userContexts&#91;i&#93;
                                  .format&#40;new String&#91;&#93; &#123; username &#125;&#41;;
                          env.put&#40;Context.SECURITY_PRINCIPAL, userContext.userPrincipal&#41;;
                          try &#123;
                              userContext.dirContext = new InitialDirContext&#40;env&#41;;
                              if &#40;userContext.dirContext != null&#41; &#123;
                                  return userContext;
                              &#125;
                          &#125; catch &#40;AuthenticationException ax&#41; &#123;
                              if &#40;log.isInfoEnabled&#40;&#41;&#41; &#123;
                                  log.info&#40;"Authentication exception for user.", ax&#41;;
                              &#125;
                          &#125;
                      &#125;
                      throw new BadCredentialsException&#40;BAD_CREDENTIALS_EXCEPTION_MESSAGE&#41;;
                  &#125;
              
                  /**
                   * @return Returns the userContexts.
                   */
                  public String&#91;&#93; getUserContexts&#40;&#41; &#123;
                      String&#91;&#93; formats = new String&#91;userContexts.length&#93;;
                      for &#40;int i = 0; i < userContexts.length; i++&#41; &#123;
                          formats&#91;i&#93; = userContexts&#91;i&#93;.toPattern&#40;&#41;;
                      &#125;
                      return formats;
                  &#125;
              
                  /**
                   * @return Returns the userRolesAttributes.
                   */
                  public String&#91;&#93; getUserRolesAttributes&#40;&#41; &#123;
                      return userRolesAttributes;
                  &#125;
              
                  /**
                   * @FIXME When using a search &#40;see getRolesFromContext&#40;&#41;&#41; I don't think this
                   *        extra check is needed; JNDI should be responible for returning
                   *        only the attributes requested &#40;or maybe I don't understand JNDI
                   *        well enough&#41;.
                   * 
                   * @param Name/Id
                   *            of the JNDI Attribute.
                   * 
                   * @return Return true if the given name is a role attribute.
                   */
                  protected boolean isRoleAttribute&#40;String name&#41; &#123;
                      log.info&#40;"Checking rolename&#58; " + name&#41;;
                      if &#40;name != null&#41; &#123;
                          for &#40;int i = 0; i < userRolesAttributes.length; i++&#41; &#123;
                              if &#40;name.equals&#40;userRolesAttributes&#91;i&#93;&#41;&#41; &#123;
                                  return true;
                              &#125;
                          &#125;
                      &#125;
                      return false;
                  &#125;
              
                  /**
                   * @return Returns the upperCaseRoleNames.
                   */
                  public boolean isUpperCaseRoleNames&#40;&#41; &#123;
                      return upperCaseRoleNames;
                  &#125;
              
                  public UserDetails loadUserByUsernameAndPassword&#40;String username,
                          String password&#41; throws DataAccessException,
                          BadCredentialsException &#123;
                      if &#40;&#40;password == null&#41; || &#40;password.length&#40;&#41; == 0&#41;&#41; &#123;
                          throw new BadCredentialsException&#40;"Empty password"&#41;;
                      &#125;
              
                      try &#123;
                          if &#40;log.isDebugEnabled&#40;&#41;&#41; &#123;
                              log.debug&#40;"Connecting to " + getProviderURL&#40;&#41; + " as "
                                      + username&#41;;
                          &#125;
                          User u = this.changeForsAMAccountName&#40;username&#41;;
                          
                          if &#40;u == null&#41;
                              throw new BadCredentialsException&#40;
                                      BAD_CREDENTIALS_EXCEPTION_MESSAGE&#41;;
                          UserContext userContext = getUserContext&#40;username, password&#41;;
              
              
                          Collection roles = getRolesFromUserContext&#40;userContext,
                                  getUserRolesAttributes&#40;&#41;&#41;;
                          Collection roles2 = getRolesFromRoleSearch&#40;userContext, getRoleNameAttributes&#40;&#41;&#41;;
                          if &#40;null != roles2&#41; &#123;
                              roles.addAll&#40;roles2&#41;;
                          &#125;
              
                          userContext.dirContext.close&#40;&#41;;
              
                          if &#40;roles.isEmpty&#40;&#41;&#41; &#123;
                              if &#40;null == defaultRole&#41; &#123;
                                  throw new BadCredentialsException&#40;
                                          "The user has no granted "
                                                  + "authorities or the rolesAttribute is invalid"&#41;;
                              &#125; else &#123;
                                  roles.add&#40;defaultRole&#41;;
                              &#125;
                          &#125;
              
                          String&#91;&#93; ldapRoles = &#40;String&#91;&#93;&#41; roles.toArray&#40;new String&#91;&#93; &#123;&#125;&#41;;
              
                          
                          
                          
                          return new User&#40;username, password, u.getNom&#40;&#41;, "", u.getTelephone&#40;&#41;,
                                  u.getEmail&#40;&#41;, u.getService&#40;&#41;, u.getDirection&#40;&#41;, true, true, true, true,
                                  getGrantedAuthorities&#40;ldapRoles&#41;&#41;;
                      &#125; catch &#40;AuthenticationException ex&#41; &#123;
                          throw new BadCredentialsException&#40;
                                  BAD_CREDENTIALS_EXCEPTION_MESSAGE, ex&#41;;
                      &#125; catch &#40;CommunicationException ex&#41; &#123;
                          throw new DataAccessResourceFailureException&#40;ex.getRootCause&#40;&#41;
                                  .getMessage&#40;&#41;, ex&#41;;
                      &#125; catch &#40;NamingException ex&#41; &#123;
                          throw new DataAccessResourceFailureException&#40;ex.getMessage&#40;&#41;, ex&#41;;
                      &#125;
                  &#125;
              
                  /**
                   * If set to a non-null value, and a user can be bound to a LDAP Conext, but
                   * no role information is found then this role is automatically added. If
                   * null &#40;the default&#41; then a BadCredentialsException is thrown
                   * 
                   * <p>
                   * For example; if you have an LDAP directory with no role information
                   * stored, you might simply want to give any user who can login a role of
                   * "USER".
                   * </p>
                   * 
                   * @param defaultRole
                   *            The defaultRole to set.
                   */
                  public void setDefaultRole&#40;String defaultRole&#41; &#123;
                      this.defaultRole = defaultRole;
                  &#125;
              
                  /**
                   * The INITIAL_CONTEXT_FACTORY used to create the JNDI Factory. Default is
                   * "com.sun.jndi.ldap.LdapCtxFactory"; you <b>should not </b> need to set
                   * this unless you have unusual needs.
                   * 
                   * @param initialContextFactory
                   *            The InitialContextFactory for creating the root JNDI context;
                   *            defaults to "com.sun.jndi.ldap.LdapCtxFactory"
                   */
                  public void setInitialContextFactory&#40;String initialContextFactory&#41; &#123;
                      this.initialContextFactory = initialContextFactory;
                  &#125;
              
                  /**
                   * Name&#40;s&#41; of the attribute&#40;s&#41; for a user account object contaning role
                   * names assigned to the user. Leave unset if there are none. Consult your
                   * LDAP server administrator to determine these value&#40;s&#41;.
                   * 
                   * @param roleUserAttributes
                   *            The roleUserAttributes to set.
                   */
                  public void setRoleAttributesSearchFilter&#40;String roleAttributesSearchArgs&#41; &#123;
                      this.roleAttributesSearchFilter = roleAttributesSearchArgs;
                  &#125;
              
                  /** Shortcut for setRoleContexts&#40; new String&#91;&#93;&#123;roleContext&#125; &#41;; */
                  public void setRoleContext&#40;String roleContext&#41; &#123;
                      setRoleContexts&#40;new String&#91;&#93; &#123; roleContext &#125;&#41;;
                  &#125;
              
                  /**
                   * Contexts to search for role's &#40;which point to the user id&#41;.
                   * <p>
                   * Example, if you have a Groups object containing Groups of users then the
                   * expression&#58; <b>ou=Groups,dc=mycompany,dc=com </b> might be used;
                   * alternatively, if rootContext="dc=mycompany,dc=com" then simply use
                   * "ou=Groups" here.
                   * 
                   * @param roleContexts
                   *            Array of MessageFormat String's for Contexts that store role
                   *            information for users.
                   */
                  public void setRoleContexts&#40;String&#91;&#93; roleContexts&#41; &#123;
                      this.roleContexts = roleContexts;
                  &#125;
              
                  /**
                   * Used to build LDAP Search Filter for finding roles &#40;in the roleContexts&#41;
                   * pointing to a user. Uses MessageFormat like tokens; &#123;0&#125; is the user's
                   * DistiguishedName, &#123;1&#125; is the user's username. For more information on
                   * syntax see javax.naming.directory.DirContext.search&#40;&#41;, or RFC 2254.
                   * 
                   * <p>
                   * Example&#58; if each group has an attribute 'memberUid' with values being the
                   * usernames of the user's in that group, then the value of this property
                   * would be <b>&#40;memberUid=&#123;1&#125;&#41; </b>
                   * </p>
                   * 
                   * @param roleNameAttributes
                   *            The roleNameAttributes to set.
                   */
                  public void setRoleNameAttribute&#40;String roleNameAttribute&#41; &#123;
                      setRoleNameAttributes&#40;new String&#91;&#93; &#123; roleNameAttribute &#125;&#41;;
                  &#125;
              
                  /**
                   * Attribute&#40;s&#41; of any role object returned from the roleContexts to use as
                   * role-names. <b>Warning&#58; </b> if you do role lookups using the
                   * roleContexts and roleAttributesSearchFilter then you need to set
                   * roleNameAttributes or ALL attributes will be returned.
                   * 
                   * @param roleNameAttributes
                   *            The roleNameAttributes to set.
                   */
                  public void setRoleNameAttributes&#40;String&#91;&#93; roleNameAttributes&#41; &#123;
                      this.roleNameAttributes = roleNameAttributes;
                  &#125;
              
                  /**
                   * Prefix to be associated with any roles found for a user, defaults to an
                   * empty string. Older versions of this class used "ROLE_" for this value.
                   * 
                   * @param rolePrefix
                   *            The rolePrefix to set.
                   */
                  public void setRolePrefix&#40;String rolePrefix&#41; &#123;
                      this.rolePrefix = rolePrefix;
                  &#125;
              
                  /**
                   * Suffix to be associated with any roles found for a user, defaults to an
                   * empty string.
                   * 
                   * @param roleSuffix
                   *            The roleSuffix to set.
                   */
                  public void setRoleSuffix&#40;String roleSuffix&#41; &#123;
                      this.roleSuffix = roleSuffix;
                  &#125;
              
                  /**
                   * Root context of the LDAP Connection, if any is needed.
                   * <p>
                   * Example&#58; <b>dc=mycompany,dc=com </b>
                   * </p>
                   * <p>
                   * <strong>Note&#58; </strong> It is usually preferable to add this data as part
                   * of the userContexts and/or roleContexts attributes.
                   * </p>
                   * 
                   * @param rootContext
                   *            The rootContext which to connect to; typically it could look
                   *            something like&#58; dc=mycompany,dc=com.
                   */
                  public void setRootContext&#40;String rootContext&#41; &#123;
                      this.rootContext = rootContext;
                  &#125;
              
                  /**
                   * If true then all role name values returned from the directory will be
                   * converted to uppercase.
                   * 
                   * @param upperCaseRoleNames
                   *            The upperCaseRoleNames to set.
                   */
                  public void setUpperCaseRoleNames&#40;boolean upperCaseRoleNames&#41; &#123;
                      this.upperCaseRoleNames = upperCaseRoleNames;
                  &#125;
              
                  /**
                   * @param host
                   *            The LDAP URL to conntect to; example&#58;
                   *            ldap&#58;//ldap.mycompany.com&#58;389/
                   */
                  public void setURL&#40;String url&#41; &#123;
                      this.url = url;
                  &#125;
              
                  /** Shortcut for setUserContexts&#40; new String&#91;&#93;&#123;userContext&#125; &#41;; */
                  public void setUserContext&#40;String userContext&#41; &#123;
                      setUserContexts&#40;new String&#91;&#93; &#123; userContext &#125;&#41;;
                  &#125;
              
                  /**
                   * One or more LDAP Contexts which contain user account information, use the
                   * MessageFormat key &#123;0&#125; to denote location where the user's username should
                   * be inserted into the expression to create a DistiguishedName.
                   * <p>
                   * Example&#58;
                   * <p>
                   * cn=&#123;0&#125;,ou=Users,dc=mycompnay,dc=com </b>
                   * </p>
                   * <p>
                   * Alternatively, if you had set rootContext="dc=mycompany,dc=com" then the
                   * first example would be rewritten as <b>cn=&#123;0&#125;,ou=Users </b>.
                   * </p>
                   * 
                   * @param userContexts
                   *            The userContexts to set.
                   */
                  public void setUserContexts&#40;String&#91;&#93; userContexts&#41; &#123;
                      this.userContexts = new MessageFormat&#91;userContexts.length&#93;;
                      for &#40;int i = 0; i < userContexts.length; i++&#41; &#123;
                          this.userContexts&#91;i&#93; = new MessageFormat&#40;userContexts&#91;i&#93;&#41;;
                      &#125;
                  &#125;
              
                  /** Shortcut for setUserRolesAttributes&#40;new String&#91;&#93;&#123;userRolesAttribute&#125;&#41;; */
                  public void setUserRolesAttribute&#40;String userRolesAttribute&#41; &#123;
                      this.userRolesAttributes = new String&#91;&#93; &#123; userRolesAttribute &#125;;
                  &#125;
              
                  /**
                   * Attribute&#40;s&#41; of any role object returned from the roleContexts to use as
                   * role-names. <b>Warning&#58; </b> if you do role lookups using the
                   * roleContexts and roleAttributesSearchFilter then you need to set
                   * roleNameAttributes or ALL attributes will be returned.
                   * 
                   * @param userRolesAttributes
                   *            The userRolesAttributes to set.
                   */
                  public void setUserRolesAttributes&#40;String&#91;&#93; userRolesAttributes&#41; &#123;
                      this.userRolesAttributes = userRolesAttributes;
                  &#125;
              
              &#125;

              Comment


              • #8
                Thanks for the code. I have actually created a branch in the acegi CVS to work on some major changes. I am looking at making a mode where the LDAP DAO performs similar to apache's mod_auth_ldap, I think your change will probably help me in working out the best way to implement this. Thanks.

                Comment


                • #9
                  new release

                  hi,
                  i've change the code in order to make a lot of enhancement.
                  please feel free to ask in case of misunderstanding.

                  -- Benoît.

                  Code:
                  public class LdapPasswordAuthenticationDao implements PasswordAuthenticationDao&#123;
                      SearchControls constraints;
                  
                      Hashtable env;
                  
                      SearchControls consOne;
                  
                      SearchControls consSub;
                  
                     public DirContext  getManagerEnv&#40;&#41; throws NamingException&#123;
                         env = new Hashtable&#40;&#41;;
                         env.put&#40;Context.INITIAL_CONTEXT_FACTORY,
                                 "com.sun.jndi.ldap.LdapCtxFactory"&#41;;
                         env.put&#40;Context.PROVIDER_URL, "ldap&#58;//server&#58;389"&#41;;
                         env.put&#40;Context.SECURITY_AUTHENTICATION, "simple"&#41;;
                         env.put&#40;Context.SECURITY_PRINCIPAL, "cn=acadi acadi,ou=STI,dc=server"&#41;; // specify
                                                                                                 // the
                                                                                                 // username
                         env.put&#40;Context.SECURITY_CREDENTIALS, "password"&#41;; // specify the password
                  
                      
                         return new InitialDirContext&#40;env&#41;;
                     &#125;
                     
                     
                      public LdapPasswordAuthenticationDao&#40;&#41; throws NamingException&#123;
                          consOne = new SearchControls&#40;&#41;;
                          consOne.setSearchScope&#40;SearchControls.ONELEVEL_SCOPE&#41;;
                          consSub = new SearchControls&#40;&#41;;
                  
                          consSub.setSearchScope&#40;SearchControls.SUBTREE_SCOPE&#41;;
                      &#125;
                      /** InnerClass used to keep context variable together. */
                      private class UserContext &#123;
                          public DirContext dirContext;
                  
                          public String userPrincipal;
                  
                          /**
                           * Get the attribute&#40;s&#41; to match when searching for the user object.
                           * This implementation returns a "distinguishedName" attribute with the
                           * value returned by <code>getUserPrincipal&#40;username&#41;</code>. A
                           * subclass may customize this behavior by overriding
                           * <code>getUserPrincipal</code> and/or
                           * <code>getUsernameAttributes</code>.
                           * 
                           * @param username
                           *            DOCUMENT ME!
                           * 
                           * @return DOCUMENT ME!
                           */
                          public Attributes getUsernameAttributes&#40;&#41; &#123;
                              Attributes matchAttrs = new BasicAttributes&#40;true&#41;; // ignore case
                              matchAttrs.put&#40;new BasicAttribute&#40;"distinguishedName",
                                      userPrincipal&#41;&#41;;
                              
                              return matchAttrs;
                          &#125;
                      &#125;
                  
                      public static final String BAD_CREDENTIALS_EXCEPTION_MESSAGE = "Invalid username, password or context";
                  
                      private static final transient Log log = LogFactory
                              .getLog&#40;LdapPasswordAuthenticationDao.class&#41;;
                  
                      /** Type of authentication within LDAP; default is simple. */
                      private String authenticationType = "simple";
                  
                      /**
                       * If set to a non-null value, and a user can be bound to a LDAP Conext, but
                       * no role information is found then this role is automatically added. If
                       * null &#40;the default&#41; then a BadCredentialsException is thrown
                       * 
                       * <p>
                       * For example; if you have an LDAP directory with no role information
                       * stored, you might simply want to give any user who can login a role of
                       * "USER".
                       * </p>
                       */
                      private String defaultRole = null;
                  
                      /**
                       * The INITIAL_CONTEXT_FACTORY used to create the JNDI Factory. Default is
                       * "com.sun.jndi.ldap.LdapCtxFactory"; you <b>should not </b> need to set
                       * this unless you have unusual needs.
                       */
                      private String initialContextFactory = "com.sun.jndi.ldap.LdapCtxFactory";
                  
                      /** Internal variable, concatenation */
                      private String providerUrl;
                  
                      /**
                       * Used to build LDAP Search Filter for finding roles &#40;in the roleContexts&#41;
                       * pointing to a user. Uses MessageFormat like tokens; &#123;0&#125; is the user's
                       * DistiguishedName, &#123;1&#125; is the user's username. For more information on
                       * syntax see javax.naming.directory.DirContext.search&#40;&#41;, or RFC 2254.
                       * 
                       * <p>
                       * Example&#58; if each group has an attribute 'memberUid' with values being the
                       * usernames of the user's in that group, then the value of this property
                       * would be <b>&#40;memberUid=&#123;1&#125;&#41; </b>
                       * </p>
                       */
                      private String roleAttributesSearchFilter;
                  
                      /**
                       * Contexts to search for role's &#40;which point to the user id&#41;.
                       * <p>
                       * Example, if you have a Groups object containing Groups of users then the
                       * expression&#58; <b>ou=Groups,dc=mycompany,dc=com </b> might be used;
                       * alternatively, if rootContext="dc=mycompany,dc=com" then simply use
                       * "ou=Groups" here.
                       */
                      private String&#91;&#93; roleContexts;
                  
                      /**
                       * Attribute&#40;s&#41; of any role object returned from the roleContexts to use as
                       * role-names. <b>Warning&#58; </b> if you do role lookups using the
                       * roleContexts and roleAttributesSearchFilter then you need to set
                       * roleNameAttributes or ALL attributes will be returned.
                       *  
                       */
                      private String&#91;&#93; roleNameAttributes;
                  
                      /**
                       * Prefix to be associated with any roles found for a user, defaults to an
                       * empty string. Older versions of this class used "ROLE_" for this value.
                       */
                      private String rolePrefix = "";
                  
                      /**
                       * Suffix to be associated with any roles found for a user, defaults to an
                       * empty string.
                       */
                      private String roleSuffix = "";
                  
                      /**
                       * Root context of the LDAP Connection, if any is needed.
                       * <p>
                       * Example&#58; <b>dc=mycompany,dc=com </b>
                       * </p>
                       * <p>
                       * <strong>Note&#58; </strong> It is usually preferable to add this data as part
                       * of the userContexts and/or roleContexts attributes.
                       * </p>
                       */
                      private String rootContext = "";
                  
                      /**
                       * If true then all role name values returned from the directory will be
                       * converted to uppercase.
                       */
                      private boolean upperCaseRoleNames = false;
                  
                      /**
                       * LDAP URL &#40;without the port&#41; of the LDAP server to connect to; example
                       * <b>ldap&#58;//dir.mycompany.com&#58;389/ </b> &#40;port 389 is the standard LDAP
                       * port&#41;.
                       */
                      private String url;
                  
                  
                      /**
                       * Name&#40;s&#41; of the attribute&#40;s&#41; for a user account object contaning role
                       * names assigned to the user. Leave unset if there are none. Consult your
                       * LDAP server administrator to determine these value&#40;s&#41;.
                       *  
                       */
                      private String&#91;&#93; userRolesAttributes;
                  
                    
                      
                      
                      /**
                       * 
                       * @param results
                       *            Result of searching on of the roleContexts for matches against
                       *            the current user.
                       * @param roles
                       *            List of roles the user has already been assigned.
                       * @throws NamingException
                       */
                      protected void addAnyRolesFound&#40;NamingEnumeration results, Collection roles&#41;
                              throws NamingException &#123;
                          while &#40;results.hasMore&#40;&#41;&#41; &#123;
                              SearchResult result = &#40;SearchResult&#41; results.next&#40;&#41;;
                              Attributes attrs = result.getAttributes&#40;&#41;;
                              if &#40;attrs == null&#41; &#123;
                                  continue;
                              &#125;
                              // Here we loop over the attributes returned in the SearchResult
                              // TODO replace with Utility method call&#58;
                              NamingEnumeration e = attrs.getAll&#40;&#41;;
                              while &#40;e.hasMore&#40;&#41;&#41; &#123;
                                  Attribute a = &#40;Attribute&#41; e.next&#40;&#41;;
                                  for &#40;int i = 0; i < a.size&#40;&#41;; i++&#41; &#123;
                                      roles.add&#40;&#40;String&#41; a.get&#40;i&#41;&#41;;
                                  &#125;
                              &#125;
                          &#125;
                      &#125;
                  
                      /**
                       * @return Returns the defaultRole.
                       */
                      public String getDefaultRole&#40;&#41; &#123;
                          return defaultRole;
                      &#125;
                  
                      /**
                       * Get an array <code>GrantedAuthorities</code> given the list of roles
                       * obtained from the LDAP context. Delegates to
                       * <code>getGrantedAuthority&#40;String ldapRole&#41;</code>. This function may
                       * be overridden in a subclass.
                       * 
                       * @param ldapRoles
                       *            DOCUMENT ME!
                       * 
                       * @return DOCUMENT ME!
                       */
                      protected GrantedAuthority&#91;&#93; getGrantedAuthorities&#40;String&#91;&#93; ldapRoles&#41; &#123;
                          GrantedAuthority&#91;&#93; grantedAuthorities = new GrantedAuthority&#91;ldapRoles.length&#93;;
                  
                          for &#40;int i = 0; i < ldapRoles.length; i++&#41; &#123;
                              grantedAuthorities&#91;i&#93; = getGrantedAuthority&#40;ldapRoles&#91;i&#93;&#41;;
                          &#125;
                  
                          return grantedAuthorities;
                      &#125;
                  
                      /**
                       * Get a <code>GrantedAuthority</code> given a role obtained from the LDAP
                       * context. If found in the LDAP role, the following characters are
                       * converted to underscore&#58; ',' &#40;comma&#41;, '=' &#40;equals&#41;, ' ' &#40;space&#41; This
                       * function may be overridden in a subclass.
                       * 
                       * @param ldapRole
                       *            DOCUMENT ME!
                       * 
                       * @return DOCUMENT ME!
                       */
                      protected GrantedAuthority getGrantedAuthority&#40;String ldapRole&#41; &#123;
                          String roleName = rolePrefix + ldapRole.toUpperCase&#40;&#41; + roleSuffix;
                          if &#40;upperCaseRoleNames&#41; &#123;
                              roleName = roleName.toUpperCase&#40;&#41;;
                          &#125;
                          GrantedAuthority ga = new GrantedAuthorityImpl&#40;roleName&#41;;
                  
                          if &#40;log.isDebugEnabled&#40;&#41;&#41; &#123;
                              log.debug&#40;"GrantedAuthority&#58; " + ga&#41;;
                          &#125;
                  
                          return ga;
                      &#125;
                  
                      /**
                       * @return The InitialContextFactory for creating the root JNDI context;
                       *         defaults to "com.sun.jndi.ldap.LdapCtxFactory"
                       */
                      public String getInitialContextFactory&#40;&#41; &#123;
                          return initialContextFactory;
                      &#125;
                  
                      // ~ Methods
                      // ================================================================
                  
                      /**
                       * Given a password, construct the Hashtable of JNDI values for a bind
                       * attempt.
                       */
                      protected Hashtable getJdniEnvironment&#40;String password&#41; &#123;
                          Hashtable env = new Hashtable&#40;11&#41;;
                          env.put&#40;Context.INITIAL_CONTEXT_FACTORY, initialContextFactory&#41;;
                          env.put&#40;Context.PROVIDER_URL, getProviderURL&#40;&#41;&#41;;
                          env.put&#40;Context.SECURITY_AUTHENTICATION, authenticationType&#41;;
                          env.put&#40;Context.SECURITY_CREDENTIALS, password&#41;;
                          return env;
                      &#125;
                  
                      /**
                       * @return The full "Provuder" URL for the LDAP source; it should look
                       *         something like&#58; ldap&#58;//www.mycompany.com&#58;389/
                       */
                      public synchronized String getProviderURL&#40;&#41; &#123;
                          if &#40;null == this.providerUrl&#41; &#123;
                              StringBuffer providerUrl = new StringBuffer&#40;this.url&#41;;
                              if &#40;!this.url.endsWith&#40;"/"&#41;&#41; &#123;
                                  providerUrl.append&#40;"/"&#41;;
                              &#125;
                              providerUrl.append&#40;this.rootContext&#41;;
                              this.providerUrl = providerUrl.toString&#40;&#41;;
                          &#125;
                          return this.providerUrl;
                      &#125;
                      public void unLockAccount&#40;String userDn&#41;throws NamingException&#123;
                          ModificationItem&#91;&#93; mods = new ModificationItem&#91;1&#93;;
                          DirContext superUserCtx = getManagerEnv&#40;&#41;;
                          BasicAttribute mod1 = new BasicAttribute&#40;"lockoutTime"&#41;;
                          mod1.add&#40;"0"&#41;;
                          mods&#91;0&#93; = new ModificationItem&#40;DirContext.REPLACE_ATTRIBUTE, mod1&#41;;
                     	 
                          superUserCtx.modifyAttributes&#40;userDn, mods&#41;;
                          superUserCtx.close&#40;&#41;;
                      &#125;
                      
                      /**
                       * @return Returns the roleUserAttributes.
                       */
                      public String getRoleAttributesSearchFilter&#40;&#41; &#123;
                          return roleAttributesSearchFilter;
                      &#125;
                  
                      /**
                       * @return Array of MessageFormat String's for Contexts that store role
                       *         information for users.
                       */
                      public String&#91;&#93; getRoleContexts&#40;&#41; &#123;
                          return roleContexts;
                      &#125;
                  
                      /**
                       * @return Returns the roleNameAttributes.
                       */
                      public String&#91;&#93; getRoleNameAttributes&#40;&#41; &#123;
                          return roleNameAttributes;
                      &#125;
                  
                      /**
                       * @return Returns the rolePrefix.
                       */
                      public String getRolePrefix&#40;&#41; &#123;
                          return rolePrefix;
                      &#125;
                  
                      protected Collection getRolesFromRoleSearch&#40;String userDn, String&#91;&#93; roleAttributes&#41; throws NamingException&#123;
                          if &#40;&#40;null == roleContexts&#41; || &#40;roleContexts.length == 0&#41;&#41; &#123;
                              System.out.println&#40;"role context vaut null &#58; "&#41;;
                              
                              return null;
                          &#125;
                          DirContext ctx = this.getManagerEnv&#40;&#41;;
                          SearchControls controls = new SearchControls&#40;&#41;;
                          controls.setSearchScope&#40;SearchControls.SUBTREE_SCOPE&#41;;
                          controls.setReturningAttributes&#40;this.roleNameAttributes&#41;;
                          String&#91;&#93;varFilter = new String&#91;1&#93;;
                          varFilter&#91;0&#93; = userDn;
                          List roles = new ArrayList&#40;&#41;;
                          for &#40;int i = 0; i < roleContexts.length; i++&#41; &#123;
                              try &#123; 
                                  
                                  
                                  NamingEnumeration results = ctx.search&#40;
                                          roleContexts&#91;i&#93;, roleAttributesSearchFilter,varFilter ,controls&#41;;
                                 
                                  addAnyRolesFound&#40;results, roles&#41;;
                              &#125; catch &#40;NamingException e&#41; &#123;
                                  if &#40;log.isInfoEnabled&#40;&#41;&#41; &#123;
                                      log.info&#40;"Unable to find user-role match in context = "
                                              + roleContexts&#91;i&#93;, e&#41;;
                                  &#125;
                              &#125;
                          &#125;
                          ctx.close&#40;&#41;;
                          return roles;
                      &#125;
                  
                      /**
                       * Looksup any roleAttributes associated with the user's DN within the
                       * DirContext.
                       * 
                       * @param userContext
                       *            UserContext Object containing DirContext in which to operate,
                       *            and the user's DistinguishedName.
                       * @param roleAttributes
                       *            Names of all attributes to search for role name information.
                       * @return Collection of roles granted to the user within the JNDI Context.
                       * @throws NamingException
                       */
                      protected Collection getRolesFromUserContext&#40;String userDn,
                              String&#91;&#93; roleAttributes&#41; throws NamingException &#123;
                          List roles = new ArrayList&#40;&#41;;
                          DirContext ctx = this.getManagerEnv&#40;&#41;;
                          if &#40;roleAttributes != null&#41; &#123;
                              if &#40;log.isDebugEnabled&#40;&#41;&#41; &#123;
                                  StringBuffer rolesString = new StringBuffer&#40;&#41;;
                  
                                  for &#40;int i = 0; i < roleAttributes.length; i++&#41; &#123;
                                      rolesString.append&#40;", "&#41;;
                                      rolesString.append&#40;roleAttributes&#91;i&#93;&#41;;
                                  &#125;
                  
                                  log.debug&#40;"Searching user context '"
                                          + userDn+ "' for roles "
                                          + "attributes&#58; " + rolesString.substring&#40;1&#41;&#41;;
                              &#125;
                              
                              Attributes attrs = ctx.getAttributes&#40;
                                      userDn, roleAttributes&#41;;
                              NamingEnumeration roleEnum = attrs.getAll&#40;&#41;;
                              while &#40;roleEnum.hasMore&#40;&#41;&#41; &#123;
                                  Attribute roleAttr = &#40;Attribute&#41; roleEnum.next&#40;&#41;;
                                  for &#40;int i = 0; i < roleAttr.size&#40;&#41;; i++&#41; &#123;
                                      roles.add&#40;roleAttr.get&#40;i&#41;&#41;;
                                  &#125;
                              &#125;
                              
                          &#125;
                          ctx.close&#40;&#41;;
                          return roles;
                      &#125;
                  
                      /**
                       * @return Returns the roleSuffix.
                       */
                      public String getRoleSuffix&#40;&#41; &#123;
                          return roleSuffix;
                      &#125;
                  
                      /**
                       * @return Returns the rootContext which to connect to; typically it could
                       *         look something like&#58; dc=mycompany,dc=com.
                       */
                      public String getRootContext&#40;&#41; &#123;
                          return rootContext;
                      &#125;
                  
                      /**
                       * @return The LDAP URL to conntect to; example&#58;
                       *         ldap&#58;//ldap.mycompany.com&#58;389/
                       */
                      public String getURL&#40;&#41; &#123;
                          return url;
                      &#125;
                  
                      protected User changeForsAMAccountName&#40;String username&#41; throws NamingException&#123;
                  
                          SearchControls cons = new SearchControls&#40;&#41;;
                          cons.setSearchScope&#40;SearchControls.SUBTREE_SCOPE&#41;;
                          User user = new User&#40;&#41;;
                          DirContext superUserCtx = getManagerEnv&#40;&#41;;
                          NamingEnumeration answer = superUserCtx.search&#40;this.rootContext,
                                  "&#40;&&#40;sAMAccountName=" + username + "&#41;&#41;", cons&#41;;
                          while &#40;answer.hasMore&#40;&#41;&#41; &#123;
                              SearchResult si = &#40;SearchResult&#41; answer.next&#40;&#41;;
                  
                              Attributes att = si.getAttributes&#40;&#41;;
                              String uc = si.getNameInNamespace&#40;&#41;;
                              user.setUsername&#40;att.get&#40;"sAMAccountName"&#41;.get&#40;&#41;.toString&#40;&#41;&#41;;
                              user.setNom&#40;att.get&#40;"cn"&#41;.get&#40;&#41;.toString&#40;&#41;&#41;;
                              user.setEmail&#40;att.get&#40;"mail"&#41;.get&#40;&#41;.toString&#40;&#41;&#41;;
                              user.setTelephone&#40;att.get&#40;"telephoneNumber"&#41;.get&#40;&#41;.toString&#40;&#41;&#41;;
                              user.setService&#40;"rien"&#41;;
                              user.setDirection&#40;"rien"&#41;;
                              user.setDn&#40;uc&#41;;
                              // on délocke le compte
                             
                              superUserCtx.close&#40;&#41;;             
                              return user;
                          &#125;
                          superUserCtx.close&#40;&#41;;
                          return null;
                  
                      &#125;
                  
                      /**
                       * Attempts to bind to the userContexts; returning on the first successful
                       * bind; or failing with a BadCredentialsException.
                       * 
                       * @param username
                       * @param password
                       * @return UserContext, an innerclass holding the DirContext, and the user's
                       *         LDAP Principal String.
                       * @throws NamingException
                       * @throws BadCredentialsException
                       */
                      protected UserContext bindUser&#40;String userDn, String password&#41;
                              throws NamingException, BadCredentialsException &#123;
                          Hashtable env = getJdniEnvironment&#40;password&#41;;
                          UserContext userContext = new UserContext&#40;&#41;;
                              env.remove&#40;Context.SECURITY_PRINCIPAL&#41;;
                              userContext.userPrincipal = userDn;
                              env.put&#40;Context.SECURITY_PRINCIPAL, userContext.userPrincipal&#41;;
                              try &#123;
                                  userContext.dirContext = new InitialDirContext&#40;env&#41;;
                                  if &#40;userContext.dirContext != null&#41; &#123;
                                      return userContext;
                                  &#125;
                              &#125; catch &#40;AuthenticationException ax&#41; &#123;
                                  if &#40;log.isInfoEnabled&#40;&#41;&#41; &#123;
                                      log.info&#40;"Authentication exception for user.", ax&#41;;
                                  &#125;
                              &#125;
                          
                          throw new BadCredentialsException&#40;BAD_CREDENTIALS_EXCEPTION_MESSAGE&#41;;
                      &#125;
                  
                  
                      /**
                       * @return Returns the userRolesAttributes.
                       */
                      public String&#91;&#93; getUserRolesAttributes&#40;&#41; &#123;
                          return userRolesAttributes;
                      &#125;
                  
                      /**
                       * @FIXME When using a search &#40;see getRolesFromContext&#40;&#41;&#41; I don't think this
                       *        extra check is needed; JNDI should be responible for returning
                       *        only the attributes requested &#40;or maybe I don't understand JNDI
                       *        well enough&#41;.
                       * 
                       * @param Name/Id
                       *            of the JNDI Attribute.
                       * 
                       * @return Return true if the given name is a role attribute.
                       */
                      protected boolean isRoleAttribute&#40;String name&#41; &#123;
                          log.info&#40;"Checking rolename&#58; " + name&#41;;
                          if &#40;name != null&#41; &#123;
                              for &#40;int i = 0; i < userRolesAttributes.length; i++&#41; &#123;
                                  if &#40;name.equals&#40;userRolesAttributes&#91;i&#93;&#41;&#41; &#123;
                                      return true;
                                  &#125;
                              &#125;
                          &#125;
                          return false;
                      &#125;
                  
                      /**
                       * @return Returns the upperCaseRoleNames.
                       */
                      public boolean isUpperCaseRoleNames&#40;&#41; &#123;
                          return upperCaseRoleNames;
                      &#125;
                  
                      public UserDetails loadUserByUsernameAndPassword&#40;String username,
                              String password&#41; throws DataAccessException,
                              BadCredentialsException &#123;
                          if &#40;&#40;password == null&#41; || &#40;password.length&#40;&#41; == 0&#41;&#41; &#123;
                              throw new BadCredentialsException&#40;"Empty password"&#41;;
                          &#125;
                  
                          try &#123;
                              if &#40;log.isDebugEnabled&#40;&#41;&#41; &#123;
                                  log.debug&#40;"Connecting to " + getProviderURL&#40;&#41; + " as "
                                          + username&#41;;
                              &#125;
                              User u = this.changeForsAMAccountName&#40;username&#41;;
                             
                              if &#40;u == null&#41;
                                  throw new BadCredentialsException&#40;
                                          BAD_CREDENTIALS_EXCEPTION_MESSAGE&#41;;
                                                  
                              UserContext userContext = bindUser&#40;u.getDn&#40;&#41;, password&#41;;
                              
                              Collection roles = getRolesFromUserContext&#40;u.getDn&#40;&#41;,
                                      getUserRolesAttributes&#40;&#41;&#41;;
                              Collection roles2 = getRolesFromRoleSearch&#40;u.getDn&#40;&#41;, getRoleNameAttributes&#40;&#41;&#41;;
                             
                              if &#40;null != roles2&#41; &#123;
                                  roles.addAll&#40;roles2&#41;;
                              &#125;
                  
                              userContext.dirContext.close&#40;&#41;;
                  
                              if &#40;roles.isEmpty&#40;&#41;&#41; &#123;
                                  if &#40;null == defaultRole&#41; &#123;
                                      throw new BadCredentialsException&#40;
                                              "The user has no granted "
                                                      + "authorities or the rolesAttribute is invalid"&#41;;
                                  &#125; else &#123;
                                      roles.add&#40;defaultRole&#41;;
                                  &#125;
                              &#125;
                  
                              String&#91;&#93; ldapRoles = &#40;String&#91;&#93;&#41; roles.toArray&#40;new String&#91;&#93; &#123;&#125;&#41;;
                  
                              
                              
                              
                              return new User&#40;username, password, u.getNom&#40;&#41;, "", u.getTelephone&#40;&#41;,
                                      u.getEmail&#40;&#41;, u.getService&#40;&#41;, u.getDirection&#40;&#41;, true, true, true, true,
                                      getGrantedAuthorities&#40;ldapRoles&#41;&#41;;
                          &#125; catch &#40;AuthenticationException ex&#41; &#123;
                              throw new BadCredentialsException&#40;
                                      BAD_CREDENTIALS_EXCEPTION_MESSAGE, ex&#41;;
                          &#125; catch &#40;CommunicationException ex&#41; &#123;
                              throw new DataAccessResourceFailureException&#40;ex.getRootCause&#40;&#41;
                                      .getMessage&#40;&#41;, ex&#41;;
                          &#125; catch &#40;NamingException ex&#41; &#123;
                              throw new DataAccessResourceFailureException&#40;ex.getMessage&#40;&#41;, ex&#41;;
                          &#125;
                      &#125;
                  
                      /**
                       * If set to a non-null value, and a user can be bound to a LDAP Conext, but
                       * no role information is found then this role is automatically added. If
                       * null &#40;the default&#41; then a BadCredentialsException is thrown
                       * 
                       * <p>
                       * For example; if you have an LDAP directory with no role information
                       * stored, you might simply want to give any user who can login a role of
                       * "USER".
                       * </p>
                       * 
                       * @param defaultRole
                       *            The defaultRole to set.
                       */
                      public void setDefaultRole&#40;String defaultRole&#41; &#123;
                          this.defaultRole = defaultRole;
                      &#125;
                  
                      /**
                       * The INITIAL_CONTEXT_FACTORY used to create the JNDI Factory. Default is
                       * "com.sun.jndi.ldap.LdapCtxFactory"; you <b>should not </b> need to set
                       * this unless you have unusual needs.
                       * 
                       * @param initialContextFactory
                       *            The InitialContextFactory for creating the root JNDI context;
                       *            defaults to "com.sun.jndi.ldap.LdapCtxFactory"
                       */
                      public void setInitialContextFactory&#40;String initialContextFactory&#41; &#123;
                          this.initialContextFactory = initialContextFactory;
                      &#125;
                  
                      /**
                       * Name&#40;s&#41; of the attribute&#40;s&#41; for a user account object contaning role
                       * names assigned to the user. Leave unset if there are none. Consult your
                       * LDAP server administrator to determine these value&#40;s&#41;.
                       * 
                       * @param roleUserAttributes
                       *            The roleUserAttributes to set.
                       */
                      public void setRoleAttributesSearchFilter&#40;String roleAttributesSearchArgs&#41; &#123;
                          this.roleAttributesSearchFilter = roleAttributesSearchArgs;
                      &#125;
                  
                      /** Shortcut for setRoleContexts&#40; new String&#91;&#93;&#123;roleContext&#125; &#41;; */
                      public void setRoleContext&#40;String roleContext&#41; &#123;
                          setRoleContexts&#40;new String&#91;&#93; &#123; roleContext &#125;&#41;;
                      &#125;
                  
                      /**
                       * Contexts to search for role's &#40;which point to the user id&#41;.
                       * <p>
                       * Example, if you have a Groups object containing Groups of users then the
                       * expression&#58; <b>ou=Groups,dc=mycompany,dc=com </b> might be used;
                       * alternatively, if rootContext="dc=mycompany,dc=com" then simply use
                       * "ou=Groups" here.
                       * 
                       * @param roleContexts
                       *            Array of MessageFormat String's for Contexts that store role
                       *            information for users.
                       */
                      public void setRoleContexts&#40;String&#91;&#93; roleContexts&#41; &#123;
                          this.roleContexts = roleContexts;
                      &#125;
                  
                      /**
                       * Used to build LDAP Search Filter for finding roles &#40;in the roleContexts&#41;
                       * pointing to a user. Uses MessageFormat like tokens; &#123;0&#125; is the user's
                       * DistiguishedName, &#123;1&#125; is the user's username. For more information on
                       * syntax see javax.naming.directory.DirContext.search&#40;&#41;, or RFC 2254.
                       * 
                       * <p>
                       * Example&#58; if each group has an attribute 'memberUid' with values being the
                       * usernames of the user's in that group, then the value of this property
                       * would be <b>&#40;memberUid=&#123;1&#125;&#41; </b>
                       * </p>
                       * 
                       * @param roleNameAttributes
                       *            The roleNameAttributes to set.
                       */
                      public void setRoleNameAttribute&#40;String roleNameAttribute&#41; &#123;
                          setRoleNameAttributes&#40;new String&#91;&#93; &#123; roleNameAttribute &#125;&#41;;
                      &#125;
                  
                      /**
                       * Attribute&#40;s&#41; of any role object returned from the roleContexts to use as
                       * role-names. <b>Warning&#58; </b> if you do role lookups using the
                       * roleContexts and roleAttributesSearchFilter then you need to set
                       * roleNameAttributes or ALL attributes will be returned.
                       * 
                       * @param roleNameAttributes
                       *            The roleNameAttributes to set.
                       */
                      public void setRoleNameAttributes&#40;String&#91;&#93; roleNameAttributes&#41; &#123;
                          this.roleNameAttributes = roleNameAttributes;
                      &#125;
                  
                      /**
                       * Prefix to be associated with any roles found for a user, defaults to an
                       * empty string. Older versions of this class used "ROLE_" for this value.
                       * 
                       * @param rolePrefix
                       *            The rolePrefix to set.
                       */
                      public void setRolePrefix&#40;String rolePrefix&#41; &#123;
                          this.rolePrefix = rolePrefix;
                      &#125;
                  
                      /**
                       * Suffix to be associated with any roles found for a user, defaults to an
                       * empty string.
                       * 
                       * @param roleSuffix
                       *            The roleSuffix to set.
                       */
                      public void setRoleSuffix&#40;String roleSuffix&#41; &#123;
                          this.roleSuffix = roleSuffix;
                      &#125;
                  
                      /**
                       * Root context of the LDAP Connection, if any is needed.
                       * <p>
                       * Example&#58; <b>dc=mycompany,dc=com </b>
                       * </p>
                       * <p>
                       * <strong>Note&#58; </strong> It is usually preferable to add this data as part
                       * of the userContexts and/or roleContexts attributes.
                       * </p>
                       * 
                       * @param rootContext
                       *            The rootContext which to connect to; typically it could look
                       *            something like&#58; dc=mycompany,dc=com.
                       */
                      public void setRootContext&#40;String rootContext&#41; &#123;
                          this.rootContext = rootContext;
                      &#125;
                  
                      /**
                       * If true then all role name values returned from the directory will be
                       * converted to uppercase.
                       * 
                       * @param upperCaseRoleNames
                       *            The upperCaseRoleNames to set.
                       */
                      public void setUpperCaseRoleNames&#40;boolean upperCaseRoleNames&#41; &#123;
                          this.upperCaseRoleNames = upperCaseRoleNames;
                      &#125;
                  
                      /**
                       * @param host
                       *            The LDAP URL to conntect to; example&#58;
                       *            ldap&#58;//ldap.mycompany.com&#58;389/
                       */
                      public void setURL&#40;String url&#41; &#123;
                          this.url = url;
                      &#125;
                  
                    
                      
                  
                      /** Shortcut for setUserRolesAttributes&#40;new String&#91;&#93;&#123;userRolesAttribute&#125;&#41;; */
                      public void setUserRolesAttribute&#40;String userRolesAttribute&#41; &#123;
                          this.userRolesAttributes = new String&#91;&#93; &#123; userRolesAttribute &#125;;
                      &#125;
                  
                      /**
                       * Attribute&#40;s&#41; of any role object returned from the roleContexts to use as
                       * role-names. <b>Warning&#58; </b> if you do role lookups using the
                       * roleContexts and roleAttributesSearchFilter then you need to set
                       * roleNameAttributes or ALL attributes will be returned.
                       * 
                       * @param userRolesAttributes
                       *            The userRolesAttributes to set.
                       */
                      public void setUserRolesAttributes&#40;String&#91;&#93; userRolesAttributes&#41; &#123;
                          this.userRolesAttributes = userRolesAttributes;
                      &#125;
                  
                      
                      
                      
                  &#125;

                  the config is

                  Code:
                  <bean id="ldapAuthenticationDao" class="org.appfuse.dao.ldap.LdapPasswordAuthenticationDao">
                           <property name="URL"><value>ldap&#58;//server&#58;389</value></property> 
                           <property name="rootContext"><value>dc=server</value></property> 
                  	     <property name="roleContext"><value>ou=Applis,dc=server</value></property> 
                           <property name="roleAttributesSearchFilter"><value>&#40;&amp;&#40;member=&#123;0&#125;&#41;&#41;</value></property>
                           <property name="roleNameAttributes"><value>sAMAccountName</value></property> 
                     </bean>

                  Comment


                  • #10
                    Thanks Benoit - I got it working also using your code.

                    I have a question regarding your value for 'roleNameAttributes'. If sAMAccountName is used here for an Active Directory server, then for me it returns a user name, not the actual name of the role for that user. I guess this is dependent on the LDAP server you are pointing to, but this is what occurs for me.

                    Anyway, the code is generic enough to work in both our cases which is good! One thing that would be useful is to create properties for the JNDI context values so they can be set through the Spring config.

                    Comment


                    • #11
                      Thanks for sharing your code, Benoît.

                      Comment


                      • #12
                        Re: new release

                        Originally posted by benoit_m35

                        Code:
                        protected User changeForsAMAccountName&#40;String username&#41; throws NamingException&#123;
                              SearchControls cons = new SearchControls&#40;&#41;;
                              cons.setSearchScope&#40;SearchControls.SUBTREE_SCOPE&#41;;
                              User user = new User&#40;&#41;;
                              DirContext superUserCtx = getManagerEnv&#40;&#41;;
                              NamingEnumeration answer = superUserCtx.search&#40;this.rootContext,
                                      "&#40;&&#40;sAMAccountName=" + username + "&#41;&#41;", cons&#41;;
                              while &#40;answer.hasMore&#40;&#41;&#41; &#123;
                                  SearchResult si = &#40;SearchResult&#41; answer.next&#40;&#41;;
                        
                                  Attributes att = si.getAttributes&#40;&#41;;
                                  String uc = si.getNameInNamespace&#40;&#41;;
                                  user.setUsername&#40;att.get&#40;"sAMAccountName"&#41;.get&#40;&#41;.toString&#40;&#41;&#41;;
                                  user.setNom&#40;att.get&#40;"cn"&#41;.get&#40;&#41;.toString&#40;&#41;&#41;;
                                  user.setEmail&#40;att.get&#40;"mail"&#41;.get&#40;&#41;.toString&#40;&#41;&#41;;
                                  user.setTelephone&#40;att.get&#40;"telephoneNumber"&#41;.get&#40;&#41;.toString&#40;&#41;&#41;;
                                  user.setService&#40;"rien"&#41;;
                                  user.setDirection&#40;"rien"&#41;;
                                  user.setDn&#40;uc&#41;;
                                  // on délocke le compte
                                 
                                  superUserCtx.close&#40;&#41;;             
                                  return user;
                              &#125;
                              superUserCtx.close&#40;&#41;;
                              return null;
                        
                          &#125;
                        Do you use a custom User object?

                        Comment


                        • #13
                          beginner question

                          Sorry Im new to ldap and just staring using spring.
                          Im about to connect to a Lotus Notes LDAP from springframework.

                          Are the classes discussed above the right ones to use or does anyone know if I need custom Lotus Notes configuration/code ?

                          Regards
                          Mike

                          Comment


                          • #14
                            Originally posted by kanonmicke
                            Sorry Im new to ldap and just staring using spring.
                            Im about to connect to a Lotus Notes LDAP from springframework.

                            Are the classes discussed above the right ones to use or does anyone know if I need custom Lotus Notes configuration/code ?
                            It depends on what you're doing. Are you intending to use it for security purposes (with Acegi) or are you talking about general LDAP access using Spring?

                            There is a new LDAP authentication provider with Acegi which supercedes the classes discussed above. There is a version of the sample application that provides an illustration of how to configure the LDAP beans.

                            Comment


                            • #15
                              Hi,
                              For the moment Im just interested in getting a connection to the ldap server to verify that I can reach it, no security involved.

                              Later I will probably need to involve it in a security solutioin.

                              You mention a "sample application". Do you know where this samle application resides or do you have any examples on yourself?

                              Best Regards
                              Mike

                              Comment

                              Working...
                              X