]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.util/src/org/argeo/osgi/useradmin/AbstractUserDirectory.java
Introduce system roles
[lgpl/argeo-commons.git] / org.argeo.util / src / org / argeo / osgi / useradmin / AbstractUserDirectory.java
1 package org.argeo.osgi.useradmin;
2
3 import static org.argeo.util.naming.LdapAttrs.objectClass;
4 import static org.argeo.util.naming.LdapObjs.extensibleObject;
5 import static org.argeo.util.naming.LdapObjs.inetOrgPerson;
6 import static org.argeo.util.naming.LdapObjs.organizationalPerson;
7 import static org.argeo.util.naming.LdapObjs.person;
8 import static org.argeo.util.naming.LdapObjs.top;
9
10 import java.io.File;
11 import java.net.URI;
12 import java.net.URISyntaxException;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Dictionary;
16 import java.util.Enumeration;
17 import java.util.Hashtable;
18 import java.util.Iterator;
19 import java.util.List;
20 import java.util.Optional;
21 import java.util.StringJoiner;
22
23 import javax.naming.InvalidNameException;
24 import javax.naming.NameNotFoundException;
25 import javax.naming.NamingEnumeration;
26 import javax.naming.NamingException;
27 import javax.naming.directory.Attribute;
28 import javax.naming.directory.Attributes;
29 import javax.naming.directory.BasicAttribute;
30 import javax.naming.directory.BasicAttributes;
31 import javax.naming.ldap.LdapName;
32 import javax.naming.ldap.Rdn;
33
34 import org.argeo.osgi.transaction.WorkControl;
35 import org.argeo.util.naming.LdapAttrs;
36 import org.argeo.util.naming.LdapObjs;
37 import org.osgi.framework.Filter;
38 import org.osgi.framework.FrameworkUtil;
39 import org.osgi.framework.InvalidSyntaxException;
40 import org.osgi.service.useradmin.Authorization;
41 import org.osgi.service.useradmin.Role;
42 import org.osgi.service.useradmin.User;
43 import org.osgi.service.useradmin.UserAdmin;
44
45 /** Base class for a {@link UserDirectory}. */
46 abstract class AbstractUserDirectory implements UserAdmin, UserDirectory {
47 static final String SHARED_STATE_USERNAME = "javax.security.auth.login.name";
48 static final String SHARED_STATE_PASSWORD = "javax.security.auth.login.password";
49
50 private final Hashtable<String, Object> properties;
51 private final LdapName baseDn;
52 // private final LdapName userBaseDn, groupBaseDn;
53 private final Rdn userBaseRdn, groupBaseRdn, systemRoleBaseRdn;
54 private final String userObjectClass, groupObjectClass;
55
56 private final boolean readOnly;
57 private final boolean disabled;
58 private final String uri;
59
60 private UserAdmin externalRoles;
61 // private List<String> indexedUserProperties = Arrays
62 // .asList(new String[] { LdapAttrs.uid.name(), LdapAttrs.mail.name(),
63 // LdapAttrs.cn.name() });
64
65 private final boolean scoped;
66
67 private String memberAttributeId = "member";
68 private List<String> credentialAttributeIds = Arrays
69 .asList(new String[] { LdapAttrs.userPassword.name(), LdapAttrs.authPassword.name() });
70
71 // Transaction
72 // private TransactionManager transactionManager;
73 private WorkControl transactionControl;
74 private WcXaResource xaResource = new WcXaResource(this);
75
76 private String forcedPassword;
77
78 AbstractUserDirectory(URI uriArg, Dictionary<String, ?> props, boolean scoped) {
79 this.scoped = scoped;
80 properties = new Hashtable<String, Object>();
81 for (Enumeration<String> keys = props.keys(); keys.hasMoreElements();) {
82 String key = keys.nextElement();
83 properties.put(key, props.get(key));
84 }
85
86 if (uriArg != null) {
87 uri = uriArg.toString();
88 // uri from properties is ignored
89 } else {
90 String uriStr = UserAdminConf.uri.getValue(properties);
91 if (uriStr == null)
92 uri = null;
93 else
94 uri = uriStr;
95 }
96
97 forcedPassword = UserAdminConf.forcedPassword.getValue(properties);
98
99 userObjectClass = UserAdminConf.userObjectClass.getValue(properties);
100 String userBase = UserAdminConf.userBase.getValue(properties);
101 groupObjectClass = UserAdminConf.groupObjectClass.getValue(properties);
102 String groupBase = UserAdminConf.groupBase.getValue(properties);
103 String systemRoleBase = UserAdminConf.systemRoleBase.getValue(properties);
104 try {
105 baseDn = new LdapName(UserAdminConf.baseDn.getValue(properties));
106 userBaseRdn = new Rdn(userBase);
107 // userBaseDn = new LdapName(userBase + "," + baseDn);
108 groupBaseRdn = new Rdn(groupBase);
109 // groupBaseDn = new LdapName(groupBase + "," + baseDn);
110 systemRoleBaseRdn = new Rdn(systemRoleBase);
111 } catch (InvalidNameException e) {
112 throw new IllegalArgumentException("Badly formated base DN " + UserAdminConf.baseDn.getValue(properties),
113 e);
114 }
115
116 // read only
117 String readOnlyStr = UserAdminConf.readOnly.getValue(properties);
118 if (readOnlyStr == null) {
119 readOnly = readOnlyDefault(uri);
120 properties.put(UserAdminConf.readOnly.name(), Boolean.toString(readOnly));
121 } else
122 readOnly = Boolean.parseBoolean(readOnlyStr);
123
124 // disabled
125 String disabledStr = UserAdminConf.disabled.getValue(properties);
126 if (disabledStr != null)
127 disabled = Boolean.parseBoolean(disabledStr);
128 else
129 disabled = false;
130 }
131
132 /** Returns the groups this user is a direct member of. */
133 protected abstract List<LdapName> getDirectGroups(LdapName dn);
134
135 protected abstract Boolean daoHasRole(LdapName dn);
136
137 protected abstract DirectoryUser daoGetRole(LdapName key) throws NameNotFoundException;
138
139 protected abstract List<DirectoryUser> doGetRoles(LdapName searchBase, Filter f, boolean deep);
140
141 protected abstract AbstractUserDirectory scope(User user);
142
143 public void init() {
144
145 }
146
147 public void destroy() {
148
149 }
150
151 /*
152 * PATHS
153 */
154
155 @Override
156 public String getContext() {
157 return getBaseDn().toString();
158 }
159
160 @Override
161 public String getName() {
162 return nameToSimple(getBaseDn(), ".");
163 }
164
165 @Override
166 public String getRolePath(Role role) {
167 return nameToRelativePath(((DirectoryUser) role).getDn());
168 }
169
170 @Override
171 public String getRoleSimpleName(Role role) {
172 LdapName dn = LdapNameUtils.toLdapName(role.getName());
173 String name = LdapNameUtils.getLastRdnValue(dn);
174 return name;
175 }
176
177 protected String nameToRelativePath(LdapName dn) {
178 LdapName name = LdapNameUtils.relativeName(getBaseDn(), dn);
179 return nameToSimple(name, "/");
180 }
181
182 protected String nameToSimple(LdapName name, String separator) {
183 StringJoiner path = new StringJoiner(separator);
184 for (int i = 0; i < name.size(); i++) {
185 path.add(name.getRdn(i).getValue().toString());
186 }
187 return path.toString();
188
189 }
190
191 protected LdapName pathToName(String path) {
192 try {
193 LdapName name = (LdapName) getBaseDn().clone();
194 String[] segments = path.split("/");
195 Rdn parentRdn = null;
196 for (String segment : segments) {
197 // TODO make attr names configurable ?
198 String attr = LdapAttrs.ou.name();
199 if (parentRdn != null) {
200 if (getUserBaseRdn().equals(parentRdn))
201 attr = LdapAttrs.uid.name();
202 else if (getGroupBaseRdn().equals(parentRdn))
203 attr = LdapAttrs.cn.name();
204 else if (getSystemRoleBaseRdn().equals(parentRdn))
205 attr = LdapAttrs.cn.name();
206 }
207 Rdn rdn = new Rdn(attr, segment);
208 name.add(rdn);
209 parentRdn = rdn;
210 }
211 return name;
212 } catch (InvalidNameException e) {
213 throw new IllegalStateException("Cannot get role " + path, e);
214 }
215
216 }
217
218 @Override
219 public Role getRoleByPath(String path) {
220 return doGetRole(pathToName(path));
221 }
222
223 @Override
224 public Optional<String> getRealm() {
225 Object realm = getProperties().get(UserAdminConf.realm.name());
226 if (realm == null)
227 return Optional.empty();
228 return Optional.of(realm.toString());
229 }
230
231 /*
232 * EDITION
233 */
234
235 protected boolean isEditing() {
236 return xaResource.wc() != null;
237 }
238
239 protected UserDirectoryWorkingCopy getWorkingCopy() {
240 UserDirectoryWorkingCopy wc = xaResource.wc();
241 if (wc == null)
242 return null;
243 return wc;
244 }
245
246 protected void checkEdit() {
247 if (xaResource.wc() == null) {
248 try {
249 transactionControl.getWorkContext().registerXAResource(xaResource, null);
250 } catch (Exception e) {
251 throw new IllegalStateException("Cannot enlist " + xaResource, e);
252 }
253 } else {
254 }
255 }
256
257 protected List<Role> getAllRoles(DirectoryUser user) {
258 List<Role> allRoles = new ArrayList<Role>();
259 if (user != null) {
260 collectRoles(user, allRoles);
261 allRoles.add(user);
262 } else
263 collectAnonymousRoles(allRoles);
264 return allRoles;
265 }
266
267 private void collectRoles(DirectoryUser user, List<Role> allRoles) {
268 Attributes attrs = user.getAttributes();
269 // TODO centralize attribute name
270 Attribute memberOf = attrs.get(LdapAttrs.memberOf.name());
271 // if user belongs to this directory, we only check meberOf
272 if (memberOf != null && user.getDn().startsWith(getBaseDn())) {
273 try {
274 NamingEnumeration<?> values = memberOf.getAll();
275 while (values.hasMore()) {
276 Object value = values.next();
277 LdapName groupDn = new LdapName(value.toString());
278 DirectoryUser group = doGetRole(groupDn);
279 if (group != null)
280 allRoles.add(group);
281 }
282 } catch (NamingException e) {
283 throw new IllegalStateException("Cannot get memberOf groups for " + user, e);
284 }
285 } else {
286 for (LdapName groupDn : getDirectGroups(user.getDn())) {
287 // TODO check for loops
288 DirectoryUser group = doGetRole(groupDn);
289 if (group != null) {
290 allRoles.add(group);
291 collectRoles(group, allRoles);
292 }
293 }
294 }
295 }
296
297 private void collectAnonymousRoles(List<Role> allRoles) {
298 // TODO gather anonymous roles
299 }
300
301 // USER ADMIN
302 @Override
303 public Role getRole(String name) {
304 return doGetRole(toLdapName(name));
305 }
306
307 protected DirectoryUser doGetRole(LdapName dn) {
308 UserDirectoryWorkingCopy wc = getWorkingCopy();
309 DirectoryUser user;
310 try {
311 user = daoGetRole(dn);
312 } catch (NameNotFoundException e) {
313 user = null;
314 }
315 if (wc != null) {
316 if (user == null && wc.getNewUsers().containsKey(dn))
317 user = wc.getNewUsers().get(dn);
318 else if (wc.getDeletedUsers().containsKey(dn))
319 user = null;
320 }
321 return user;
322 }
323
324 @Override
325 public Role[] getRoles(String filter) throws InvalidSyntaxException {
326 List<? extends Role> res = getRoles(getBaseDn(), filter, true);
327 return res.toArray(new Role[res.size()]);
328 }
329
330 List<DirectoryUser> getRoles(LdapName searchBase, String filter, boolean deep) throws InvalidSyntaxException {
331 UserDirectoryWorkingCopy wc = getWorkingCopy();
332 Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null;
333 List<DirectoryUser> res = doGetRoles(searchBase, f, deep);
334 if (wc != null) {
335 for (Iterator<DirectoryUser> it = res.iterator(); it.hasNext();) {
336 DirectoryUser user = it.next();
337 LdapName dn = user.getDn();
338 if (wc.getDeletedUsers().containsKey(dn))
339 it.remove();
340 }
341 for (DirectoryUser user : wc.getNewUsers().values()) {
342 if (f == null || f.match(user.getProperties()))
343 res.add(user);
344 }
345 // no need to check modified users,
346 // since doGetRoles was already based on the modified attributes
347 }
348
349 // if non deep we also search users and groups
350 // if (!deep) {
351 // try {
352 // if (!(searchBase.endsWith(new LdapName(getUserBase()))
353 // || searchBase.endsWith(new LdapName(getGroupBase())))) {
354 // LdapName usersBase = (LdapName) ((LdapName) searchBase.clone()).add(getUserBase());
355 // res.addAll(getRoles(usersBase, filter, false));
356 // LdapName groupsBase = (LdapName) ((LdapName) searchBase.clone()).add(getGroupBase());
357 // res.addAll(getRoles(groupsBase, filter, false));
358 // }
359 // } catch (InvalidNameException e) {
360 // throw new IllegalStateException("Cannot search users and groups", e);
361 // }
362 // }
363 return res;
364 }
365
366 @Override
367 public User getUser(String key, String value) {
368 // TODO check value null or empty
369 List<DirectoryUser> collectedUsers = new ArrayList<DirectoryUser>();
370 if (key != null) {
371 doGetUser(key, value, collectedUsers);
372 } else {
373 throw new IllegalArgumentException("Key cannot be null");
374 }
375
376 if (collectedUsers.size() == 1) {
377 return collectedUsers.get(0);
378 } else if (collectedUsers.size() > 1) {
379 // log.warn(collectedUsers.size() + " users for " + (key != null ? key + "=" :
380 // "") + value);
381 }
382 return null;
383 }
384
385 protected void doGetUser(String key, String value, List<DirectoryUser> collectedUsers) {
386 try {
387 Filter f = FrameworkUtil.createFilter("(" + key + "=" + value + ")");
388 List<DirectoryUser> users = doGetRoles(getBaseDn(), f, true);
389 collectedUsers.addAll(users);
390 } catch (InvalidSyntaxException e) {
391 throw new IllegalArgumentException("Cannot get user with " + key + "=" + value, e);
392 }
393 }
394
395 @Override
396 public Authorization getAuthorization(User user) {
397 if (user == null || user instanceof DirectoryUser) {
398 return new LdifAuthorization(user, getAllRoles((DirectoryUser) user));
399 } else {
400 // bind
401 AbstractUserDirectory scopedUserAdmin = scope(user);
402 try {
403 DirectoryUser directoryUser = (DirectoryUser) scopedUserAdmin.getRole(user.getName());
404 if (directoryUser == null)
405 throw new IllegalStateException("No scoped user found for " + user);
406 LdifAuthorization authorization = new LdifAuthorization(directoryUser,
407 scopedUserAdmin.getAllRoles(directoryUser));
408 return authorization;
409 } finally {
410 scopedUserAdmin.destroy();
411 }
412 }
413 }
414
415 @Override
416 public Role createRole(String name, int type) {
417 checkEdit();
418 UserDirectoryWorkingCopy wc = getWorkingCopy();
419 LdapName dn = toLdapName(name);
420 if ((daoHasRole(dn) && !wc.getDeletedUsers().containsKey(dn)) || wc.getNewUsers().containsKey(dn))
421 throw new IllegalArgumentException("Already a role " + name);
422 BasicAttributes attrs = new BasicAttributes(true);
423 // attrs.put(LdifName.dn.name(), dn.toString());
424 Rdn nameRdn = dn.getRdn(dn.size() - 1);
425 // TODO deal with multiple attr RDN
426 attrs.put(nameRdn.getType(), nameRdn.getValue());
427 if (wc.getDeletedUsers().containsKey(dn)) {
428 wc.getDeletedUsers().remove(dn);
429 wc.getModifiedUsers().put(dn, attrs);
430 return getRole(name);
431 } else {
432 wc.getModifiedUsers().put(dn, attrs);
433 DirectoryUser newRole = newRole(dn, type, attrs);
434 wc.getNewUsers().put(dn, newRole);
435 return newRole;
436 }
437 }
438
439 protected DirectoryUser newRole(LdapName dn, int type, Attributes attrs) {
440 DirectoryUser newRole;
441 BasicAttribute objClass = new BasicAttribute(objectClass.name());
442 if (type == Role.USER) {
443 String userObjClass = newUserObjectClass(dn);
444 objClass.add(userObjClass);
445 if (inetOrgPerson.name().equals(userObjClass)) {
446 objClass.add(organizationalPerson.name());
447 objClass.add(person.name());
448 } else if (organizationalPerson.name().equals(userObjClass)) {
449 objClass.add(person.name());
450 }
451 objClass.add(top.name());
452 objClass.add(extensibleObject.name());
453 attrs.put(objClass);
454 newRole = newUser(dn, attrs);
455 } else if (type == Role.GROUP) {
456 String groupObjClass = getGroupObjectClass();
457 objClass.add(groupObjClass);
458 // objClass.add(LdifName.extensibleObject.name());
459 objClass.add(top.name());
460 attrs.put(objClass);
461 newRole = newGroup(dn, attrs);
462 } else
463 throw new IllegalArgumentException("Unsupported type " + type);
464 return newRole;
465 }
466
467 @Override
468 public boolean removeRole(String name) {
469 checkEdit();
470 UserDirectoryWorkingCopy wc = getWorkingCopy();
471 LdapName dn = toLdapName(name);
472 boolean actuallyDeleted;
473 if (daoHasRole(dn) || wc.getNewUsers().containsKey(dn)) {
474 DirectoryUser user = (DirectoryUser) getRole(name);
475 wc.getDeletedUsers().put(dn, user);
476 actuallyDeleted = true;
477 } else {// just removing from groups (e.g. system roles)
478 actuallyDeleted = false;
479 }
480 for (LdapName groupDn : getDirectGroups(dn)) {
481 DirectoryUser group = doGetRole(groupDn);
482 group.getAttributes().get(getMemberAttributeId()).remove(dn.toString());
483 }
484 return actuallyDeleted;
485 }
486
487 // TRANSACTION
488 protected void prepare(UserDirectoryWorkingCopy wc) {
489
490 }
491
492 protected void commit(UserDirectoryWorkingCopy wc) {
493
494 }
495
496 protected void rollback(UserDirectoryWorkingCopy wc) {
497
498 }
499
500 /*
501 * HIERARCHY
502 */
503 // @Override
504 // public int getHierarchyChildCount() {
505 // return 0;
506 // }
507 //
508 // @Override
509 // public HierarchyUnit getHierarchyChild(int i) {
510 // throw new IllegalArgumentException("No child hierarchy unit available");
511 // }
512 //
513 // @Override
514 // public HierarchyUnit getParent() {
515 // return null;
516 // }
517 //
518 // @Override
519 // public int getHierarchyUnitType() {
520 // return 0;
521 // }
522 //
523 // @Override
524 // public String getHierarchyUnitName() {
525 // String name = LdapNameUtils.getLastRdnValue(baseDn);
526 // // TODO check ou, o, etc.
527 // return name;
528 // }
529
530 @Override
531 public HierarchyUnit getHierarchyUnit(String path) {
532 throw new UnsupportedOperationException();
533 }
534
535 @Override
536 public HierarchyUnit getHierarchyUnit(Role role) {
537 throw new UnsupportedOperationException();
538 }
539
540 // @Override
541 // public List<? extends Role> getHierarchyUnitRoles(String filter, boolean deep) {
542 // try {
543 // return getRoles(getBaseDn(), filter, deep);
544 // } catch (InvalidSyntaxException e) {
545 // throw new IllegalArgumentException("Cannot filter " + filter + " " + getBaseDn(), e);
546 // }
547 // }
548
549 @Override
550 public Iterable<HierarchyUnit> getDirectHierarchyUnits(boolean functionalOnly) {
551 throw new UnsupportedOperationException();
552 }
553
554 /*
555 * ROLES CREATION
556 */
557 protected DirectoryUser newUser(LdapName name, Attributes attrs) {
558 // TODO support devices, applications, etc.
559 return new LdifUser.LdifPerson(this, name, attrs);
560 }
561
562 protected DirectoryGroup newGroup(LdapName name, Attributes attrs) {
563 if (LdapNameUtils.getParentRdn(name).equals(getSystemRoleBaseRdn()))
564 return new LdifGroup.LdifSystemPermissions(this, name, attrs);
565
566 if (hasObjectClass(attrs, LdapObjs.organization))
567 return new LdifGroup.LdifOrganization(this, name, attrs);
568 else
569 return new LdifGroup.LdifFunctionalGroup(this, name, attrs);
570
571 }
572
573 private boolean hasObjectClass(Attributes attrs, LdapObjs objectClass) {
574 try {
575 Attribute attr = attrs.get(LdapAttrs.objectClass.name());
576 NamingEnumeration<?> en = attr.getAll();
577 while (en.hasMore()) {
578 String v = en.next().toString();
579 if (v.equalsIgnoreCase(objectClass.name()))
580 return true;
581
582 }
583 return false;
584 } catch (NamingException e) {
585 throw new IllegalStateException("Cannot search for objectClass " + objectClass.name(), e);
586 }
587 }
588
589 // GETTERS
590 protected String getMemberAttributeId() {
591 return memberAttributeId;
592 }
593
594 protected List<String> getCredentialAttributeIds() {
595 return credentialAttributeIds;
596 }
597
598 protected String getUri() {
599 return uri;
600 }
601
602 private static boolean readOnlyDefault(String uriStr) {
603 if (uriStr == null)
604 return true;
605 /// TODO make it more generic
606 URI uri;
607 try {
608 uri = new URI(uriStr.split(" ")[0]);
609 } catch (URISyntaxException e) {
610 throw new IllegalArgumentException(e);
611 }
612 if (uri.getScheme() == null)
613 return false;// assume relative file to be writable
614 if (uri.getScheme().equals(UserAdminConf.SCHEME_FILE)) {
615 File file = new File(uri);
616 if (file.exists())
617 return !file.canWrite();
618 else
619 return !file.getParentFile().canWrite();
620 } else if (uri.getScheme().equals(UserAdminConf.SCHEME_LDAP)) {
621 if (uri.getAuthority() != null)// assume writable if authenticated
622 return false;
623 } else if (uri.getScheme().equals(UserAdminConf.SCHEME_OS)) {
624 return true;
625 }
626 return true;// read only by default
627 }
628
629 public boolean isReadOnly() {
630 return readOnly;
631 }
632
633 public boolean isDisabled() {
634 return disabled;
635 }
636
637 protected UserAdmin getExternalRoles() {
638 return externalRoles;
639 }
640
641 protected int roleType(LdapName dn) {
642 Rdn technicalRdn = LdapNameUtils.getParentRdn(dn);
643 if (getGroupBaseRdn().equals(technicalRdn) || getSystemRoleBaseRdn().equals(technicalRdn))
644 return Role.GROUP;
645 else if (userBaseRdn.equals(technicalRdn))
646 return Role.USER;
647 else
648 throw new IllegalArgumentException(
649 "Cannot dind role type, " + technicalRdn + " is not a technical RDN for " + dn);
650 }
651
652 /** dn can be null, in that case a default should be returned. */
653 public String getUserObjectClass() {
654 return userObjectClass;
655 }
656
657 Rdn getUserBaseRdn() {
658 return userBaseRdn;
659 }
660
661 protected String newUserObjectClass(LdapName dn) {
662 return getUserObjectClass();
663 }
664
665 public String getGroupObjectClass() {
666 return groupObjectClass;
667 }
668
669 Rdn getGroupBaseRdn() {
670 return groupBaseRdn;
671 }
672
673 Rdn getSystemRoleBaseRdn() {
674 return systemRoleBaseRdn;
675 }
676
677 LdapName getBaseDn() {
678 return (LdapName) baseDn.clone();
679 }
680
681 public Dictionary<String, Object> getProperties() {
682 return properties;
683 }
684
685 public Dictionary<String, Object> cloneProperties() {
686 return new Hashtable<>(properties);
687 }
688
689 public void setExternalRoles(UserAdmin externalRoles) {
690 this.externalRoles = externalRoles;
691 }
692
693 // public void setTransactionManager(TransactionManager transactionManager) {
694 // this.transactionManager = transactionManager;
695 // }
696
697 public String getForcedPassword() {
698 return forcedPassword;
699 }
700
701 public void setTransactionControl(WorkControl transactionControl) {
702 this.transactionControl = transactionControl;
703 }
704
705 public WcXaResource getXaResource() {
706 return xaResource;
707 }
708
709 public boolean isScoped() {
710 return scoped;
711 }
712
713 @Override
714 public int hashCode() {
715 return baseDn.hashCode();
716 }
717
718 @Override
719 public String toString() {
720 return "User Directory " + baseDn.toString();
721 }
722
723 /*
724 * STATIC UTILITIES
725 */
726 static LdapName toLdapName(String name) {
727 try {
728 return new LdapName(name);
729 } catch (InvalidNameException e) {
730 throw new IllegalArgumentException(name + " is not an LDAP name", e);
731 }
732 }
733 }