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             String searchDomain = getSearchDomain();
211             if (searchDomain.length() > 0)
212             {
213                 userDn += "," + StringUtils.replace(searchDomain, "," + getRootContext(), "");
214             }
215         }
216         return userDn;
217     }
218 
219     /***
220      * <p>
221      * Validate the uid.
222      * </p>
223      * 
224      * @param uid The uid.
225      */
226     protected void validateUid(String uid) throws SecurityException
227     {
228         String pattern = ".*//(.*|.*//[.*|.*//{.*|.*////.*|.*//^.*|.*//$.*|.*//|.*|.*//).*|.*//?.*|.*//*.*|.*//+.*|.*//..*";
229         if (StringUtils.isEmpty(uid) || uid.matches(pattern))
230         {
231             throw new InvalidUidException();
232         }
233     }
234 
235     /***
236      * <p>
237      * Search uid by wild card.
238      * </p>
239      * 
240      * @param filter The filter.
241      * @param cons The {@link SearchControls}
242      * @return The {@link NamingEnumeration}
243      * @throws NamingException Throws a {@link NamingEnumeration}.
244      */
245     protected NamingEnumeration searchByWildcardedUid(final String filter, SearchControls cons) throws NamingException
246     {
247     	// usa a template method to use users/groups/roles
248     	String query = "";
249         if (StringUtils.isEmpty(getSearchSuffix())) {
250         	query = "(" + getEntryPrefix() + "=" + (StringUtils.isEmpty(filter) ? "*" : filter) + ")";
251         } else {
252         	query = "(&(" + getEntryPrefix() + "=" + (StringUtils.isEmpty(filter) ? "*" : filter) + ")" + getSearchSuffix() + ")";
253         }
254         logger.debug("searchByWildCardedUid = " + query); 
255 
256 	    cons.setSearchScope(getSearchScope());
257 	    //TODO: added this here for OpenLDAP (when users are stored in ou=People,o=evenSeas)
258 	    String searchBase = StringUtils.replace(getSearchDomain(), "," + getRootContext(), "");
259 	    NamingEnumeration results = ((DirContext) ctx).search(searchBase,query , cons);	
260 
261         return results;
262     }
263     
264     /***
265      * <p>
266      * Search uid by wild card.
267      * </p>
268      * 
269      * @param filter The filter.
270      * @param cons The {@link SearchControls}
271      * @return The {@link NamingEnumeration}
272      * @throws NamingException Throws a {@link NamingEnumeration}.
273      */
274     protected NamingEnumeration searchGroupByWildcardedUid(final String filter, SearchControls cons) throws NamingException
275     {
276     	// usa a template method to use users/groups/roles
277         String query = "";
278         if (StringUtils.isEmpty(getGroupFilter())) {
279         	query = "(" + getGroupIdAttribute() + "=" + (StringUtils.isEmpty(filter) ? "*" : filter) + ")";
280         } else {
281         	query = "(&(" + getGroupIdAttribute() + "=" + (StringUtils.isEmpty(filter) ? "*" : filter) + ")" + getGroupFilter() + ")";
282         }        
283         
284 	    String searchBase = "";
285 	    if (!StringUtils.isEmpty(getGroupFilterBase()))
286 	    	searchBase+=getGroupFilterBase();
287 	    cons.setSearchScope(getSearchScope());
288 	    NamingEnumeration results = ((DirContext) ctx).search(searchBase,query , cons);	
289 
290         return results;
291     }   
292     
293     /***
294      * <p>
295      * Search uid by wild card.
296      * </p>
297      * 
298      * @param filter The filter.
299      * @param cons The {@link SearchControls}
300      * @return The {@link NamingEnumeration}
301      * @throws NamingException Throws a {@link NamingEnumeration}.
302      */
303     protected NamingEnumeration searchRoleByWildcardedUid(final String filter, SearchControls cons) throws NamingException
304     {
305         String query = "";
306         if (StringUtils.isEmpty(getRoleFilter())) {
307         	query = "(" + getRoleIdAttribute() + "=" + (StringUtils.isEmpty(filter) ? "*" : filter) + ")";
308         } else {
309         	query = "(&(" + getRoleIdAttribute() + "=" + (StringUtils.isEmpty(filter) ? "*" : filter) + ")" + getRoleFilter() + ")";
310         }  
311         
312 	    String searchBase = "";
313 	    if (!StringUtils.isEmpty(getRoleFilterBase()))
314 	    	searchBase+=getRoleFilterBase();
315 	    cons.setSearchScope(getSearchScope());
316 	    NamingEnumeration results = ((DirContext) ctx).search(searchBase,query , cons);	
317 
318         return results;
319     }      
320 
321     /***
322      * <p>
323      * Returns the default Group suffix dn.
324      * </p>
325      * 
326      * @return The defaultDnSuffix.
327      */
328     protected String getGroupFilterBase()
329     {
330         return this.ldapBindingConfig.getGroupFilterBase();
331     }
332     
333     /***
334      * <p>
335      * Returns the default Group suffix dn.
336      * </p>
337      * 
338      * @return The defaultDnSuffix.
339      */
340     protected String[] getGroupObjectClasses()
341     {
342         return this.ldapBindingConfig.getGroupObjectClasses();
343     }    
344     
345 
346     /***
347      * <p>
348      * Returns the default Group suffix dn.
349      * </p>
350      * 
351      * @return The defaultDnSuffix.
352      */
353     protected String getRoleFilterBase()
354     {
355         return this.ldapBindingConfig.getRoleFilterBase();
356     }
357     
358     /***
359      * <p>
360      * Returns the default Group suffix dn.
361      * </p>
362      * 
363      * @return The defaultDnSuffix.
364      */
365     protected String[] getRoleObjectClasses()
366     {
367         return this.ldapBindingConfig.getRoleObjectClasses();
368     }    
369     
370     /***
371      * <p>
372      * Returns the default Group suffix dn.
373      * </p>
374      * 
375      * @return The defaultDnSuffix.
376      */
377     protected String getUserFilterBase()
378     {
379         return this.ldapBindingConfig.getUserFilterBase();
380     }    
381     
382     /***
383      * <p>
384      * Returns the default Group suffix dn.
385      * </p>
386      * 
387      * @return The defaultDnSuffix.
388      */
389     protected String getGroupFilter()
390     {
391         return this.ldapBindingConfig.getGroupFilter();
392     }     
393     
394     
395     /***
396      * <p>
397      * Returns the default Group suffix dn.
398      * </p>
399      * 
400      * @return The defaultDnSuffix.
401      */
402     protected String getRoleFilter()
403     {
404         return this.ldapBindingConfig.getRoleFilter();
405     }     
406         
407     
408 
409     /***
410      * <p>
411      * Returns the root context.
412      * </p>
413      * 
414      * @return The root context.
415      */
416     protected String getRootContext()
417     {
418         return this.ldapBindingConfig.getRootContext();
419     }
420     
421     /***
422      * <p>
423      * A template method that returns the LDAP entry prefix of the concrete DAO.
424      * </p>
425      * 
426      * TODO : this should be in spring config
427      * 
428      * @return a String containing the LDAP entry prefix name.
429      */    
430     protected abstract String getEntryPrefix();
431     
432     /***
433      * <p>
434      * A template method that returns the LDAP entry prefix of the concrete DAO.
435      * </p>
436      * 
437      * TODO : this should be in spring config
438      * 
439      * @return a String containing the LDAP entry prefix name.
440      */    
441     protected abstract String getSearchSuffix();
442     
443     /***
444      * <p>
445      * The domain in wich to perform a search
446      * </p>
447      * 
448      * TODO : this should be in spring config
449      * 
450      * @return a String containing the LDAP entry prefix name.
451      */    
452     protected abstract String getSearchDomain();    
453         
454     protected  String getUserFilter()
455     {
456         return this.ldapBindingConfig.getUserFilter();
457     }
458 
459     protected String[] getUserObjectClasses()
460     {
461         return this.ldapBindingConfig.getUserObjectClasses();
462     }    
463 
464     protected  String getGroupMembershipAttribute()
465     {
466         return this.ldapBindingConfig.getGroupMembershipAttributes();
467     }   
468     
469     protected  String getUserGroupMembershipAttribute()
470     {
471         return this.ldapBindingConfig.getUserGroupMembershipAttributes();
472     }  
473      
474     
475     protected  String getGroupMembershipForRoleAttribute()
476     {
477         return this.ldapBindingConfig.getGroupMembershipForRoleAttributes();
478     }   
479     
480     protected  String getRoleGroupMembershipForRoleAttribute()
481     {
482         return this.ldapBindingConfig.getRoleGroupMembershipForRoleAttributes();
483     }    
484         
485     protected  String getRoleMembershipAttribute()
486     {
487         return this.ldapBindingConfig.getRoleMembershipAttributes();
488     }
489     
490     protected  String getUserRoleMembershipAttribute()
491     {
492         return this.ldapBindingConfig.getUserRoleMembershipAttributes();
493     }
494 
495     protected  String getRoleIdAttribute()
496     {
497         return this.ldapBindingConfig.getRoleIdAttribute();
498     }    
499 
500     protected  String getGroupIdAttribute()
501     {
502         return this.ldapBindingConfig.getGroupIdAttribute();
503     }    
504 
505     protected  String getUserIdAttribute()
506     {
507         return this.ldapBindingConfig.getUserIdAttribute();
508     }    
509 
510     protected  String getUidAttribute()
511     {
512         return this.ldapBindingConfig.getUidAttribute();
513     }        
514     
515     protected  int getSearchScope()
516     {
517         return Integer.parseInt(this.ldapBindingConfig.getMemberShipSearchScope());
518     }        
519     
520     protected String getRoleUidAttribute()
521     {
522         return this.ldapBindingConfig.getRoleUidAttribute();
523     }        
524     
525     protected String getGroupUidAttribute()
526     {
527         return this.ldapBindingConfig.getGroupUidAttribute();
528     }        
529     
530     protected String getUserUidAttribute()
531     {
532         return this.ldapBindingConfig.getUserUidAttribute();
533     }        
534     
535     protected String getGroupObjectRequiredAttributeClasses()
536     {
537         return this.ldapBindingConfig.getGroupObjectRequiredAttributeClasses();
538     }        
539     
540     protected String getRoleObjectRequiredAttributeClasses()
541     {
542         return this.ldapBindingConfig.getRoleObjectRequiredAttributeClasses();
543     }        
544         
545     protected String[] getUserAttributes()
546     {
547         return this.ldapBindingConfig.getUserAttributes();
548     }        
549     
550     protected String[] getGroupAttributes()
551     {
552         return this.ldapBindingConfig.getGroupAttributes();
553     }        
554     
555     protected String[] getRoleAttributes()
556     {
557         return this.ldapBindingConfig.getRoleAttributes();
558     }        
559     
560     protected String getUserPasswordAttribute() {
561     	return this.ldapBindingConfig.getUserPasswordAttribute();
562     }
563     
564     protected String[] getKnownAttributes() {
565     	return this.ldapBindingConfig.getKnownAttributes();
566     }    
567 
568     protected abstract String[] getObjectClasses();
569     protected abstract String[] getAttributes();
570 }