Coverage Report - org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator
 
Classes in this File Line Coverage Branch Coverage Complexity
DefaultLdapAuthoritiesPopulator
90% 
100% 
1.733
 
 1  
 /* Copyright 2004, 2005, 2006 Acegi Technology Pty Limited
 2  
  *
 3  
  * Licensed under the Apache License, Version 2.0 (the "License");
 4  
  * you may not use this file except in compliance with the License.
 5  
  * You may obtain a copy of the License at
 6  
  *
 7  
  *     http://www.apache.org/licenses/LICENSE-2.0
 8  
  *
 9  
  * Unless required by applicable law or agreed to in writing, software
 10  
  * distributed under the License is distributed on an "AS IS" BASIS,
 11  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12  
  * See the License for the specific language governing permissions and
 13  
  * limitations under the License.
 14  
  */
 15  
 
 16  
 package org.acegisecurity.providers.ldap.populator;
 17  
 
 18  
 import org.acegisecurity.GrantedAuthority;
 19  
 import org.acegisecurity.GrantedAuthorityImpl;
 20  
 
 21  
 import org.acegisecurity.ldap.InitialDirContextFactory;
 22  
 import org.acegisecurity.ldap.LdapTemplate;
 23  
 
 24  
 import org.acegisecurity.providers.ldap.LdapAuthoritiesPopulator;
 25  
 
 26  
 import org.acegisecurity.userdetails.ldap.LdapUserDetails;
 27  
 
 28  
 import org.apache.commons.logging.Log;
 29  
 import org.apache.commons.logging.LogFactory;
 30  
 
 31  
 import org.springframework.util.Assert;
 32  
 
 33  
 import java.util.HashSet;
 34  
 import java.util.Iterator;
 35  
 import java.util.Set;
 36  
 
 37  
 import javax.naming.directory.Attributes;
 38  
 import javax.naming.directory.SearchControls;
 39  
 
 40  
 
 41  
 /**
 42  
  * The default strategy for obtaining user role information from the directory.
 43  
  * <p/>
 44  
  * <p>It obtains roles by performing a search for "groups" the user is a member of.</p>
 45  
  * <p/>
 46  
  * <p/>
 47  
  * A typical group search scenario would be where each group/role is specified using the <tt>groupOfNames</tt>
 48  
  * (or <tt>groupOfUniqueNames</tt>) LDAP objectClass and the user's DN is listed in the <tt>member</tt> (or
 49  
  * <tt>uniqueMember</tt>) attribute to indicate that they should be assigned that role. The following LDIF sample has
 50  
  * the groups stored under the DN <tt>ou=groups,dc=acegisecurity,dc=org</tt> and a group called "developers" with
 51  
  * "ben" and "marissa" as members:
 52  
  * <pre>
 53  
  * dn: ou=groups,dc=acegisecurity,dc=orgobjectClass: top
 54  
  * objectClass: organizationalUnitou: groupsdn: cn=developers,ou=groups,dc=acegisecurity,dc=org
 55  
  * objectClass: groupOfNamesobjectClass: topcn: developersdescription: Acegi Security Developers
 56  
  * member: uid=ben,ou=people,dc=acegisecurity,dc=orgmember: uid=marissa,ou=people,dc=acegisecurity,dc=orgou: developer
 57  
  * </pre>
 58  
  * </p>
 59  
  * <p/>
 60  
  * The group search is performed within a DN specified by the <tt>groupSearchBase</tt> property, which should
 61  
  * be relative to the root DN of its <tt>InitialDirContextFactory</tt>. If the search base is null, group searching is
 62  
  * disabled. The filter used in the search is defined by the <tt>groupSearchFilter</tt> property, with the filter
 63  
  * argument {0} being the full DN of the user. You can also optionally use the parameter {1}, which will be substituted
 64  
  * with the username. You can also specify which attribute defines the role name by setting
 65  
  * the <tt>groupRoleAttribute</tt> property (the default is "cn").</p>
 66  
  * <p/>
 67  
  * <p>The configuration below shows how the group search might be performed with the above schema.
 68  
  * <pre>
 69  
  * &lt;bean id="ldapAuthoritiesPopulator"
 70  
  *         class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">
 71  
  *   &lt;constructor-arg>&lt;ref local="initialDirContextFactory"/>&lt;/constructor-arg>
 72  
  *   &lt;constructor-arg>&lt;value>ou=groups&lt;/value>&lt;/constructor-arg>
 73  
  *   &lt;property name="groupRoleAttribute">&lt;value>ou&lt;/value>&lt;/property>
 74  
  * &lt;!-- the following properties are shown with their default values -->
 75  
  *   &lt;property name="searchSubTree">&lt;value>false&lt;/value>&lt;/property>
 76  
  *   &lt;property name="rolePrefix">&lt;value>ROLE_&lt;/value>&lt;/property>
 77  
  *   &lt;property name="convertToUpperCase">&lt;value>true&lt;/value>&lt;/property>
 78  
  * &lt;/bean>
 79  
  * </pre>
 80  
  * A search for roles for user "uid=ben,ou=people,dc=acegisecurity,dc=org" would return the single granted authority
 81  
  * "ROLE_DEVELOPER".
 82  
  * </p>
 83  
  * <p/>
 84  
  * The single-level search is performed by default. Setting the <tt>searchSubTree</tt> property to true will enable
 85  
  * a search of the entire subtree under <tt>groupSearchBase</tt>.
 86  
  *
 87  
  * @author Luke Taylor
 88  
  * @version $Id: DefaultLdapAuthoritiesPopulator.java 1996 2007-08-30 21:15:14Z luke_t $
 89  
  */
 90  
 public class DefaultLdapAuthoritiesPopulator implements LdapAuthoritiesPopulator {
 91  
     //~ Static fields/initializers =====================================================================================
 92  
 
 93  2
     private static final Log logger = LogFactory.getLog(DefaultLdapAuthoritiesPopulator.class);
 94  
 
 95  
     //~ Instance fields ================================================================================================
 96  
 
 97  
     /**
 98  
      * A default role which will be assigned to all authenticated users if set
 99  
      */
 100  5
     private GrantedAuthority defaultRole = null;
 101  
 
 102  
     /**
 103  
      * An initial context factory is only required if searching for groups is required.
 104  
      */
 105  5
     private InitialDirContextFactory initialDirContextFactory = null;
 106  
     private LdapTemplate ldapTemplate;
 107  
 
 108  
     /**
 109  
      * Controls used to determine whether group searches should be performed over the full sub-tree from the
 110  
      * base DN. Modified by searchSubTree property
 111  
      */
 112  5
     private SearchControls searchControls = new SearchControls();
 113  
 
 114  
     /**
 115  
      * The ID of the attribute which contains the role name for a group
 116  
      */
 117  5
     private String groupRoleAttribute = "cn";
 118  
 
 119  
     /**
 120  
      * The base DN from which the search for group membership should be performed
 121  
      */
 122  5
     private String groupSearchBase = null;
 123  
 
 124  
     /**
 125  
      * The pattern to be used for the user search. {0} is the user's DN
 126  
      */
 127  5
     private String groupSearchFilter = "(member={0})";
 128  
 
 129  
     /**
 130  
      * Attributes of the User's LDAP Object that contain role name information.
 131  
      */
 132  
 
 133  
 //    private String[] userRoleAttributes = null;
 134  5
     private String rolePrefix = "ROLE_";
 135  5
     private boolean convertToUpperCase = true;
 136  
 
 137  
     //~ Constructors ===================================================================================================
 138  
 
 139  
     /**
 140  
      * Constructor for group search scenarios. <tt>userRoleAttributes</tt> may still be
 141  
      * set as a property.
 142  
      *
 143  
      * @param initialDirContextFactory supplies the contexts used to search for user roles.
 144  
      * @param groupSearchBase          if this is an empty string the search will be performed from the root DN of the
 145  
      *                                 context factory.
 146  
      */
 147  5
     public DefaultLdapAuthoritiesPopulator(InitialDirContextFactory initialDirContextFactory, String groupSearchBase) {
 148  5
         this.setInitialDirContextFactory(initialDirContextFactory);
 149  5
         this.setGroupSearchBase(groupSearchBase);
 150  5
     }
 151  
 
 152  
     //~ Methods ========================================================================================================
 153  
 
 154  
     /**
 155  
      * This method should be overridden if required to obtain any additional
 156  
      * roles for the given user (on top of those obtained from the standard
 157  
      * search implemented by this class).
 158  
      *
 159  
      * @param ldapUser the user who's roles are required
 160  
      * @return the extra roles which will be merged with those returned by the group search
 161  
      */
 162  
 
 163  
     protected Set getAdditionalRoles(LdapUserDetails ldapUser) {
 164  5
         return null;
 165  
     }
 166  
 
 167  
     /**
 168  
      * Obtains the authorities for the user who's directory entry is represented by
 169  
      * the supplied LdapUserDetails object.
 170  
      *
 171  
      * @param userDetails the user who's authorities are required
 172  
      * @return the set of roles granted to the user.
 173  
      */
 174  
     public final GrantedAuthority[] getGrantedAuthorities(LdapUserDetails userDetails) {
 175  5
         String userDn = userDetails.getDn();
 176  
 
 177  5
         if (logger.isDebugEnabled()) {
 178  0
             logger.debug("Getting authorities for user " + userDn);
 179  
         }
 180  
 
 181  5
         Set roles = getGroupMembershipRoles(userDn, userDetails.getUsername());
 182  
 
 183  
         // Temporary use of deprecated method
 184  5
         Set oldGroupRoles = getGroupMembershipRoles(userDn, userDetails.getAttributes());
 185  
 
 186  5
         if (oldGroupRoles != null) {
 187  5
             roles.addAll(oldGroupRoles);
 188  
         }
 189  
 
 190  5
         Set extraRoles = getAdditionalRoles(userDetails);
 191  
 
 192  5
         if (extraRoles != null) {
 193  0
             roles.addAll(extraRoles);
 194  
         }
 195  
 
 196  5
         if (defaultRole != null) {
 197  1
             roles.add(defaultRole);
 198  
         }
 199  
 
 200  5
         return (GrantedAuthority[]) roles.toArray(new GrantedAuthority[roles.size()]);
 201  
     }
 202  
 
 203  
 //    protected Set getRolesFromUserAttributes(String userDn, Attributes userAttributes) {
 204  
 //        Set userRoles = new HashSet();
 205  
 //
 206  
 //        for(int i=0; userRoleAttributes != null && i < userRoleAttributes.length; i++) {
 207  
 //            Attribute roleAttribute = userAttributes.get(userRoleAttributes[i]);
 208  
 //
 209  
 //            addAttributeValuesToRoleSet(roleAttribute, userRoles);
 210  
 //        }
 211  
 //
 212  
 //        return userRoles;
 213  
 //    }
 214  
 
 215  
 
 216  
     public Set getGroupMembershipRoles(String userDn, String username) {
 217  5
         Set authorities = new HashSet();
 218  
 
 219  5
         if (getGroupSearchBase() == null) {
 220  0
             return authorities;
 221  
         }
 222  
 
 223  5
         if (logger.isDebugEnabled()) {
 224  0
             logger.debug("Searching for roles for user '" + username + "', DN = " + "'" + userDn + "', with filter "
 225  
                     + groupSearchFilter + " in search base '" + getGroupSearchBase() + "'");
 226  
         }
 227  
 
 228  5
         Set userRoles = ldapTemplate.searchForSingleAttributeValues(getGroupSearchBase(), groupSearchFilter,
 229  
                 new String[]{userDn, username}, groupRoleAttribute);
 230  
 
 231  5
         if (logger.isDebugEnabled()) {
 232  0
             logger.debug("Roles from search: " + userRoles);
 233  
         }
 234  
 
 235  5
         Iterator it = userRoles.iterator();
 236  
 
 237  13
         while (it.hasNext()) {
 238  8
             String role = (String) it.next();
 239  
 
 240  8
             if (convertToUpperCase) {
 241  8
                 role = role.toUpperCase();
 242  
             }
 243  
 
 244  8
             authorities.add(new GrantedAuthorityImpl(rolePrefix + role));
 245  8
         }
 246  
 
 247  5
         return authorities;
 248  
     }
 249  
 
 250  
     /**
 251  
      * Searches for groups the user is a member of.
 252  
      *
 253  
      * @param userDn         the user's distinguished name.
 254  
      * @param userAttributes the retrieved user's attributes (unused by default).
 255  
      * @return the set of roles obtained from a group membership search, or null if <tt>groupSearchBase</tt> has been
 256  
      *         set.
 257  
      * @deprecated Subclasses should implement <tt>getAdditionalRoles</tt> instead.
 258  
      */
 259  
     protected Set getGroupMembershipRoles(String userDn, Attributes userAttributes) {
 260  5
         return new HashSet();
 261  
     }
 262  
 
 263  
     protected InitialDirContextFactory getInitialDirContextFactory() {
 264  0
         return initialDirContextFactory;
 265  
     }
 266  
 
 267  
     /**
 268  
      * Set the {@link InitialDirContextFactory}
 269  
      *
 270  
      * @param initialDirContextFactory supplies the contexts used to search for user roles.
 271  
      */
 272  
     private void setInitialDirContextFactory(InitialDirContextFactory initialDirContextFactory) {
 273  5
         Assert.notNull(initialDirContextFactory, "InitialDirContextFactory must not be null");
 274  5
         this.initialDirContextFactory = initialDirContextFactory;
 275  
 
 276  5
         ldapTemplate = new LdapTemplate(initialDirContextFactory);
 277  5
         ldapTemplate.setSearchControls(searchControls);
 278  5
     }
 279  
 
 280  
     /**
 281  
      * Set the group search base (name to search under)
 282  
      *
 283  
      * @param groupSearchBase if this is an empty string the search will be performed from the root DN of the context
 284  
      *                        factory.
 285  
      */
 286  
     private void setGroupSearchBase(String groupSearchBase) {
 287  5
         Assert.notNull(groupSearchBase, "The groupSearchBase (name to search under), must not be null.");
 288  5
         this.groupSearchBase = groupSearchBase;
 289  5
         if (groupSearchBase.length() == 0) {
 290  0
             logger.info("groupSearchBase is empty. Searches will be performed from the root: "
 291  
                     + getInitialDirContextFactory().getRootDn());
 292  
         }
 293  5
     }
 294  
 
 295  
     protected String getGroupSearchBase() {
 296  10
         return groupSearchBase;
 297  
     }
 298  
 
 299  
     public void setConvertToUpperCase(boolean convertToUpperCase) {
 300  4
         this.convertToUpperCase = convertToUpperCase;
 301  4
     }
 302  
 
 303  
     /**
 304  
      * The default role which will be assigned to all users.
 305  
      *
 306  
      * @param defaultRole the role name, including any desired prefix.
 307  
      */
 308  
     public void setDefaultRole(String defaultRole) {
 309  1
         Assert.notNull(defaultRole, "The defaultRole property cannot be set to null");
 310  1
         this.defaultRole = new GrantedAuthorityImpl(defaultRole);
 311  1
     }
 312  
 
 313  
     public void setGroupRoleAttribute(String groupRoleAttribute) {
 314  4
         Assert.notNull(groupRoleAttribute, "groupRoleAttribute must not be null");
 315  4
         this.groupRoleAttribute = groupRoleAttribute;
 316  4
     }
 317  
 
 318  
     public void setGroupSearchFilter(String groupSearchFilter) {
 319  2
         Assert.notNull(groupSearchFilter, "groupSearchFilter must not be null");
 320  2
         this.groupSearchFilter = groupSearchFilter;
 321  2
     }
 322  
 
 323  
     public void setRolePrefix(String rolePrefix) {
 324  1
         Assert.notNull(rolePrefix, "rolePrefix must not be null");
 325  1
         this.rolePrefix = rolePrefix;
 326  1
     }
 327  
 
 328  
     /**
 329  
      * If set to true, a subtree scope search will be performed. If false a single-level search is used.
 330  
      *
 331  
      * @param searchSubtree set to true to enable searching of the entire tree below the <tt>groupSearchBase</tt>.
 332  
      */
 333  
     public void setSearchSubtree(boolean searchSubtree) {
 334  3
         int searchScope = searchSubtree ? SearchControls.SUBTREE_SCOPE : SearchControls.ONELEVEL_SCOPE;
 335  3
         searchControls.setSearchScope(searchScope);
 336  3
     }
 337  
 }