View Javadoc

1   /* 
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *     http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.jetspeed.security.spi.impl.ldap;
18  
19  import javax.naming.Name;
20  import javax.naming.NameParser;
21  import javax.naming.NamingEnumeration;
22  import javax.naming.NamingException;
23  import javax.naming.directory.DirContext;
24  import javax.naming.directory.SearchControls;
25  import javax.naming.directory.SearchResult;
26  import javax.naming.ldap.LdapContext;
27  
28  import org.apache.commons.lang.StringUtils;
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.jetspeed.security.InvalidDnException;
32  import org.apache.jetspeed.security.InvalidPasswordException;
33  import org.apache.jetspeed.security.InvalidUidException;
34  import org.apache.jetspeed.security.SecurityException;
35  
36  /***
37   * <p>
38   * Abstract ldap dao.
39   * </p>
40   * 
41   * @author <a href="mailto:mike.long@dataline.com">Mike Long </a>, <a
42   *         href="mailto:dlestrat@apache.org">David Le Strat</a>
43   */
44  public abstract class AbstractLdapDao
45  {
46  	
47  	private static final Log logger = LogFactory.getLog(AbstractLdapDao.class);
48  	
49      /*** The ldap binding configuration. */
50      private LdapBindingConfig ldapBindingConfig = null;
51  
52      /*** Reference to remote server context */
53      protected LdapContext ctx;
54  
55      /***
56       * <p>
57       * Default constructor.
58       * </p>
59       */
60      public AbstractLdapDao()
61      {
62          throw new UnsupportedOperationException("Must be instantiated with LDAP binding configuration.");
63      }
64  
65      /***
66       * <p>
67       * Initializes the dao.
68       * </p>
69       * 
70       * @param ldapConfig Holds the ldap configuration.
71       * @throws SecurityException
72       */
73      public AbstractLdapDao(LdapBindingConfig ldapConfig) throws SecurityException
74      {
75          this.ldapBindingConfig = ldapConfig;
76          bindToServer(ldapConfig.getRootDn(), ldapConfig.getRootPassword());
77      }
78  
79      /***
80       * <p>
81       * Binds to the ldap server.
82       * </p>
83       * 
84       * @param rootDn
85       * @param rootPassword
86       * @throws SecurityException
87       */
88      protected void bindToServer(String rootDn, String rootPassword) throws SecurityException
89      {
90          if ( ctx == null )
91          {
92              validateDn(rootDn);
93              validatePassword(rootPassword);
94  
95              ctx = LdapContextProxy.createProxy(ldapBindingConfig);
96          }
97      }
98  
99      /***
100      * <p>
101      * Gets the sub context name.
102      * </p>
103      * 
104      * @param dn The domain name.
105      * @return The sub context name.
106      * @throws NamingException
107      */
108     protected String getSubcontextName(final String dn) throws NamingException
109     {
110         NameParser parser = ctx.getNameParser("");
111         Name name = parser.parse(dn);
112         String rootStr = ctx.getNameInNamespace();
113         Name root = parser.parse(rootStr);
114 
115         if (name.startsWith(root))
116         {
117             Name rname = name.getSuffix(root.size());
118 
119             return rname.toString();
120         }
121 
122         return dn;
123     }
124 
125     /***
126      * <p>
127      * Validate the domain name.
128      * </p>
129      * 
130      * @param dn The domain name.
131      */
132     protected void validateDn(final String dn) throws SecurityException
133     {
134         if (StringUtils.isEmpty(dn))
135         {
136             throw new InvalidDnException();
137         }
138     }
139 
140     /***
141      * <p>
142      * Valiate the users password.
143      * </p>
144      * 
145      * @param password The user.
146      */
147     protected void validatePassword(final String password) throws SecurityException
148     {
149         if (StringUtils.isEmpty(password))
150         {
151             throw new InvalidPasswordException();
152         }
153     }
154 
155     /***
156      * @return The factors that determine the scope of the search and what gets returned as a result
157      *         of the search.
158      */
159     protected SearchControls setSearchControls()
160     {
161         SearchControls controls = new SearchControls();
162         controls.setReturningAttributes(getKnownAttributes());
163         controls.setSearchScope(SearchControls.SUBTREE_SCOPE);
164         controls.setReturningObjFlag(true);
165 
166         return controls;
167     }
168 
169     /***
170      * <p>
171      * Searches the LDAP server for the user with the specified userid (uid attribute).
172      * </p>
173      * 
174      * @return the user's DN
175      */
176     public String lookupByUid(final String uid) throws SecurityException
177     {
178         validateUid(uid);
179 
180         try
181         {
182             SearchControls cons = setSearchControls();
183             NamingEnumeration searchResults = searchByWildcardedUid(uid, cons);
184 
185             return getFirstDnForUid(searchResults);
186         }
187         catch (NamingException e)
188         {
189             throw new SecurityException(e);
190         }
191     }
192     
193 
194     /***
195      * <p>
196      * Gets the first matching user for the given uid.
197      * </p>
198      * 
199      * @param searchResults The {@link NamingEnumeration}.
200      * @return the user's DN of the first use in the list. Null if no users were found.
201      * @throws NamingException Throws a {@link NamingException}.
202      */
203     private String getFirstDnForUid(NamingEnumeration searchResults) throws NamingException
204     {
205         String userDn = null;
206         while ((null != searchResults) && searchResults.hasMore())
207         {
208             SearchResult searchResult = (SearchResult) searchResults.next();
209             userDn = searchResult.getName();
210         }
211         return userDn;
212     }
213 
214     /***
215      * <p>
216      * Validate the uid.
217      * </p>
218      * 
219      * @param uid The uid.
220      */
221     protected void validateUid(String uid) throws SecurityException
222     {
223         String pattern = ".*//(.*|.*//[.*|.*//{.*|.*////.*|.*//^.*|.*//$.*|.*//|.*|.*//).*|.*//?.*|.*//*.*|.*//+.*|.*//..*";
224         if (StringUtils.isEmpty(uid) || uid.matches(pattern))
225         {
226             throw new InvalidUidException();
227         }
228     }
229 
230     /***
231      * <p>
232      * Search uid by wild card.
233      * </p>
234      * 
235      * @param filter The filter.
236      * @param cons The {@link SearchControls}
237      * @return The {@link NamingEnumeration}
238      * @throws NamingException Throws a {@link NamingEnumeration}.
239      */
240     protected NamingEnumeration searchByWildcardedUid(final String filter, SearchControls cons) throws NamingException
241     {
242     	// usa a template method to use users/groups/roles
243     	String query = "";
244         if (StringUtils.isEmpty(getSearchSuffix())) {
245         	query = "(" + getEntryPrefix() + "=" + (StringUtils.isEmpty(filter) ? "*" : filter) + ")";
246         } else {
247         	query = "(&(" + getEntryPrefix() + "=" + (StringUtils.isEmpty(filter) ? "*" : filter) + ")" + getSearchSuffix() + ")";
248         }
249         logger.debug("searchByWildCardedUid = " + query); 
250 
251 	    cons.setSearchScope(getSearchScope());
252 	    //TODO: added this here for OpenLDAP (when users are stored in ou=People,o=evenSeas)
253 	    String searchBase = StringUtils.replace(getSearchDomain(), "," + getRootContext(), "");
254 	    NamingEnumeration results = ((DirContext) ctx).search(searchBase,query , cons);	
255 
256         return results;
257     }
258     
259     /***
260      * <p>
261      * Search uid by wild card.
262      * </p>
263      * 
264      * @param filter The filter.
265      * @param cons The {@link SearchControls}
266      * @return The {@link NamingEnumeration}
267      * @throws NamingException Throws a {@link NamingEnumeration}.
268      */
269     protected NamingEnumeration searchGroupByWildcardedUid(final String filter, SearchControls cons) throws NamingException
270     {
271     	// usa a template method to use users/groups/roles
272         String query = "";
273         if (StringUtils.isEmpty(getGroupFilter())) {
274         	query = "(" + getGroupIdAttribute() + "=" + (StringUtils.isEmpty(filter) ? "*" : filter) + ")";
275         } else {
276         	query = "(&(" + getGroupIdAttribute() + "=" + (StringUtils.isEmpty(filter) ? "*" : filter) + ")" + getGroupFilter() + ")";
277         }        
278         
279 	    String searchBase = "";
280 	    if (!StringUtils.isEmpty(getGroupFilterBase()))
281 	    	searchBase+=getGroupFilterBase();
282 	    cons.setSearchScope(getSearchScope());
283 	    NamingEnumeration results = ((DirContext) ctx).search(searchBase,query , cons);	
284 
285         return results;
286     }   
287     
288     /***
289      * <p>
290      * Search uid by wild card.
291      * </p>
292      * 
293      * @param filter The filter.
294      * @param cons The {@link SearchControls}
295      * @return The {@link NamingEnumeration}
296      * @throws NamingException Throws a {@link NamingEnumeration}.
297      */
298     protected NamingEnumeration searchRoleByWildcardedUid(final String filter, SearchControls cons) throws NamingException
299     {
300         String query = "";
301         if (StringUtils.isEmpty(getRoleFilter())) {
302         	query = "(" + getRoleIdAttribute() + "=" + (StringUtils.isEmpty(filter) ? "*" : filter) + ")";
303         } else {
304         	query = "(&(" + getRoleIdAttribute() + "=" + (StringUtils.isEmpty(filter) ? "*" : filter) + ")" + getRoleFilter() + ")";
305         }  
306         
307 	    String searchBase = "";
308 	    if (!StringUtils.isEmpty(getRoleFilterBase()))
309 	    	searchBase+=getRoleFilterBase();
310 	    cons.setSearchScope(getSearchScope());
311 	    NamingEnumeration results = ((DirContext) ctx).search(searchBase,query , cons);	
312 
313         return results;
314     }      
315 
316     /***
317      * <p>
318      * Returns the default Group suffix dn.
319      * </p>
320      * 
321      * @return The defaultDnSuffix.
322      */
323     protected String getGroupFilterBase()
324     {
325         return this.ldapBindingConfig.getGroupFilterBase();
326     }
327     
328     /***
329      * <p>
330      * Returns the default Group suffix dn.
331      * </p>
332      * 
333      * @return The defaultDnSuffix.
334      */
335     protected String[] getGroupObjectClasses()
336     {
337         return this.ldapBindingConfig.getGroupObjectClasses();
338     }    
339     
340 
341     /***
342      * <p>
343      * Returns the default Group suffix dn.
344      * </p>
345      * 
346      * @return The defaultDnSuffix.
347      */
348     protected String getRoleFilterBase()
349     {
350         return this.ldapBindingConfig.getRoleFilterBase();
351     }
352     
353     /***
354      * <p>
355      * Returns the default Group suffix dn.
356      * </p>
357      * 
358      * @return The defaultDnSuffix.
359      */
360     protected String[] getRoleObjectClasses()
361     {
362         return this.ldapBindingConfig.getRoleObjectClasses();
363     }    
364     
365     /***
366      * <p>
367      * Returns the default Group suffix dn.
368      * </p>
369      * 
370      * @return The defaultDnSuffix.
371      */
372     protected String getUserFilterBase()
373     {
374         return this.ldapBindingConfig.getUserFilterBase();
375     }    
376     
377     /***
378      * <p>
379      * Returns the default Group suffix dn.
380      * </p>
381      * 
382      * @return The defaultDnSuffix.
383      */
384     protected String getGroupFilter()
385     {
386         return this.ldapBindingConfig.getGroupFilter();
387     }     
388     
389     
390     /***
391      * <p>
392      * Returns the default Group suffix dn.
393      * </p>
394      * 
395      * @return The defaultDnSuffix.
396      */
397     protected String getRoleFilter()
398     {
399         return this.ldapBindingConfig.getRoleFilter();
400     }     
401         
402     
403 
404     /***
405      * <p>
406      * Returns the root context.
407      * </p>
408      * 
409      * @return The root context.
410      */
411     protected String getRootContext()
412     {
413         return this.ldapBindingConfig.getRootContext();
414     }
415     
416     /***
417      * <p>
418      * A template method that returns the LDAP entry prefix of the concrete DAO.
419      * </p>
420      * 
421      * TODO : this should be in spring config
422      * 
423      * @return a String containing the LDAP entry prefix name.
424      */    
425     protected abstract String getEntryPrefix();
426     
427     /***
428      * <p>
429      * A template method that returns the LDAP entry prefix of the concrete DAO.
430      * </p>
431      * 
432      * TODO : this should be in spring config
433      * 
434      * @return a String containing the LDAP entry prefix name.
435      */    
436     protected abstract String getSearchSuffix();
437     
438     /***
439      * <p>
440      * The domain in wich to perform a search
441      * </p>
442      * 
443      * TODO : this should be in spring config
444      * 
445      * @return a String containing the LDAP entry prefix name.
446      */    
447     protected abstract String getSearchDomain();    
448         
449     protected  String getUserFilter()
450     {
451         return this.ldapBindingConfig.getUserFilter();
452     }
453 
454     protected String[] getUserObjectClasses()
455     {
456         return this.ldapBindingConfig.getUserObjectClasses();
457     }    
458 
459     protected  String getGroupMembershipAttribute()
460     {
461         return this.ldapBindingConfig.getGroupMembershipAttributes();
462     }   
463     
464     protected  String getUserGroupMembershipAttribute()
465     {
466         return this.ldapBindingConfig.getUserGroupMembershipAttributes();
467     }  
468      
469     
470     protected  String getGroupMembershipForRoleAttribute()
471     {
472         return this.ldapBindingConfig.getGroupMembershipForRoleAttributes();
473     }   
474     
475     protected  String getRoleGroupMembershipForRoleAttribute()
476     {
477         return this.ldapBindingConfig.getRoleGroupMembershipForRoleAttributes();
478     }    
479         
480     protected  String getRoleMembershipAttribute()
481     {
482         return this.ldapBindingConfig.getRoleMembershipAttributes();
483     }
484     
485     protected  String getUserRoleMembershipAttribute()
486     {
487         return this.ldapBindingConfig.getUserRoleMembershipAttributes();
488     }
489 
490     protected  String getRoleIdAttribute()
491     {
492         return this.ldapBindingConfig.getRoleIdAttribute();
493     }    
494 
495     protected  String getGroupIdAttribute()
496     {
497         return this.ldapBindingConfig.getGroupIdAttribute();
498     }    
499 
500     protected  String getUserIdAttribute()
501     {
502         return this.ldapBindingConfig.getUserIdAttribute();
503     }    
504 
505     protected  String getUidAttribute()
506     {
507         return this.ldapBindingConfig.getUidAttribute();
508     }        
509     
510     protected  int getSearchScope()
511     {
512         return Integer.parseInt(this.ldapBindingConfig.getMemberShipSearchScope());
513     }        
514     
515     protected String getRoleUidAttribute()
516     {
517         return this.ldapBindingConfig.getRoleUidAttribute();
518     }        
519     
520     protected String getGroupUidAttribute()
521     {
522         return this.ldapBindingConfig.getGroupUidAttribute();
523     }        
524     
525     protected String getUserUidAttribute()
526     {
527         return this.ldapBindingConfig.getUserUidAttribute();
528     }        
529     
530     protected String getGroupObjectRequiredAttributeClasses()
531     {
532         return this.ldapBindingConfig.getGroupObjectRequiredAttributeClasses();
533     }        
534     
535     protected String getRoleObjectRequiredAttributeClasses()
536     {
537         return this.ldapBindingConfig.getRoleObjectRequiredAttributeClasses();
538     }        
539         
540     protected String[] getUserAttributes()
541     {
542         return this.ldapBindingConfig.getUserAttributes();
543     }        
544     
545     protected String[] getGroupAttributes()
546     {
547         return this.ldapBindingConfig.getGroupAttributes();
548     }        
549     
550     protected String[] getRoleAttributes()
551     {
552         return this.ldapBindingConfig.getRoleAttributes();
553     }        
554     
555     protected String getUserPasswordAttribute() {
556     	return this.ldapBindingConfig.getUserPasswordAttribute();
557     }
558     
559     protected String[] getKnownAttributes() {
560     	return this.ldapBindingConfig.getKnownAttributes();
561     }    
562 
563     protected abstract String[] getObjectClasses();
564     protected abstract String[] getAttributes();
565 }