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