1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
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
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
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 }