1 package org
.argeo
.util
.directory
.ldap
;
3 import static org
.argeo
.util
.naming
.LdapAttrs
.objectClass
;
5 import java
.util
.ArrayList
;
8 import javax
.naming
.AuthenticationNotSupportedException
;
9 import javax
.naming
.Binding
;
10 import javax
.naming
.InvalidNameException
;
11 import javax
.naming
.NameNotFoundException
;
12 import javax
.naming
.NamingEnumeration
;
13 import javax
.naming
.NamingException
;
14 import javax
.naming
.directory
.Attribute
;
15 import javax
.naming
.directory
.Attributes
;
16 import javax
.naming
.directory
.BasicAttributes
;
17 import javax
.naming
.directory
.SearchControls
;
18 import javax
.naming
.directory
.SearchResult
;
19 import javax
.naming
.ldap
.LdapName
;
20 import javax
.naming
.ldap
.Rdn
;
22 import org
.argeo
.util
.directory
.HierarchyUnit
;
23 import org
.argeo
.util
.naming
.LdapAttrs
;
24 import org
.argeo
.util
.naming
.LdapObjs
;
26 /** A user admin based on a LDAP server. */
27 public class LdapDao
extends AbstractLdapDirectoryDao
{
28 private LdapConnection ldapConnection
;
30 // public LdapUserAdmin(Dictionary<String, ?> properties) {
31 // this(properties, false);
34 public LdapDao(AbstractLdapDirectory directory
) {
40 ldapConnection
= new LdapConnection(getDirectory().getUri().toString(), getDirectory().cloneConfigProperties());
43 public void destroy() {
44 ldapConnection
.destroy();
48 // protected AbstractUserDirectory scope(User user) {
49 // Dictionary<String, Object> credentials = user.getCredentials();
50 // String username = (String) credentials.get(SHARED_STATE_USERNAME);
51 // if (username == null)
52 // username = user.getName();
53 // Dictionary<String, Object> properties = cloneProperties();
54 // properties.put(Context.SECURITY_PRINCIPAL, username.toString());
55 // Object pwdCred = credentials.get(SHARED_STATE_PASSWORD);
56 // byte[] pwd = (byte[]) pwdCred;
58 // char[] password = DirectoryDigestUtils.bytesToChars(pwd);
59 // properties.put(Context.SECURITY_CREDENTIALS, new String(password));
61 // properties.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
63 // return new LdapUserAdmin(properties, true);
66 // protected InitialLdapContext getLdapContext() {
67 // return initialLdapContext;
71 public Boolean
entryExists(LdapName dn
) {
73 return ldapConnection
.entryExists(dn
);
74 } catch (NameNotFoundException e
) {
76 } catch (NamingException e
) {
77 throw new IllegalStateException("Cannot check " + dn
, e
);
82 public LdapEntry
doGetEntry(LdapName name
) throws NameNotFoundException
{
83 // if (!entryExists(name))
84 // throw new NameNotFoundException(name + " was not found in " + getDirectory().getBaseDn());
86 Attributes attrs
= ldapConnection
.getAttributes(name
);
89 Rdn technicalRdn
= LdapNameUtils
.getParentRdn(name
);
90 if (getDirectory().getGroupBaseRdn().equals(technicalRdn
)) {
91 if (attrs
.size() == 0) {// exists but not accessible
92 attrs
= new BasicAttributes();
93 attrs
.put(LdapAttrs
.objectClass
.name(), LdapObjs
.top
.name());
94 attrs
.put(LdapAttrs
.objectClass
.name(), getDirectory().getGroupObjectClass());
97 } else if (getDirectory().getSystemRoleBaseRdn().equals(technicalRdn
)) {
98 if (attrs
.size() == 0) {// exists but not accessible
99 attrs
= new BasicAttributes();
100 attrs
.put(LdapAttrs
.objectClass
.name(), LdapObjs
.top
.name());
101 attrs
.put(LdapAttrs
.objectClass
.name(), getDirectory().getGroupObjectClass());
103 res
= newGroup(name
);
104 } else if (getDirectory().getUserBaseRdn().equals(technicalRdn
)) {
105 if (attrs
.size() == 0) {// exists but not accessible
106 attrs
= new BasicAttributes();
107 attrs
.put(LdapAttrs
.objectClass
.name(), LdapObjs
.top
.name());
108 attrs
.put(LdapAttrs
.objectClass
.name(), getDirectory().getUserObjectClass());
112 res
= new DefaultLdapEntry(getDirectory(), name
);
115 } catch (NameNotFoundException e
) {
117 } catch (NamingException e
) {
118 throw new IllegalStateException("Cannot retrieve entry " + name
, e
);
122 // protected boolean isGroup(LdapName dn) {
123 // Rdn technicalRdn = LdapNameUtils.getParentRdn(dn);
124 // if (getDirectory().getGroupBaseRdn().equals(technicalRdn)
125 // || getDirectory().getSystemRoleBaseRdn().equals(technicalRdn))
127 // else if (getDirectory().getUserBaseRdn().equals(technicalRdn))
130 // throw new IllegalArgumentException(
131 // "Cannot find role type, " + technicalRdn + " is not a technical RDN for " + dn);
135 public Attributes
doGetAttributes(LdapName name
) {
137 Attributes attrs
= ldapConnection
.getAttributes(name
);
139 } catch (NamingException e
) {
140 throw new IllegalStateException("Cannot get attributes for " + name
);
145 public List
<LdapEntry
> doGetEntries(LdapName searchBase
, String f
, boolean deep
) {
146 ArrayList
<LdapEntry
> res
= new ArrayList
<>();
148 String searchFilter
= f
!= null ? f
.toString()
149 : "(|(" + objectClass
.name() + "=" + getDirectory().getUserObjectClass() + ")(" + objectClass
.name()
150 + "=" + getDirectory().getGroupObjectClass() + "))";
151 SearchControls searchControls
= new SearchControls();
152 // only attribute needed is objectClass
153 searchControls
.setReturningAttributes(new String
[] { objectClass
.name() });
154 // FIXME make one level consistent with deep
155 searchControls
.setSearchScope(deep ? SearchControls
.SUBTREE_SCOPE
: SearchControls
.ONELEVEL_SCOPE
);
157 // LdapName searchBase = getBaseDn();
158 NamingEnumeration
<SearchResult
> results
= ldapConnection
.search(searchBase
, searchFilter
, searchControls
);
160 results
: while (results
.hasMoreElements()) {
161 SearchResult searchResult
= results
.next();
162 Attributes attrs
= searchResult
.getAttributes();
163 Attribute objectClassAttr
= attrs
.get(objectClass
.name());
164 LdapName dn
= toDn(searchBase
, searchResult
);
166 if (objectClassAttr
.contains(getDirectory().getGroupObjectClass())
167 || objectClassAttr
.contains(getDirectory().getGroupObjectClass().toLowerCase()))
169 else if (objectClassAttr
.contains(getDirectory().getUserObjectClass())
170 || objectClassAttr
.contains(getDirectory().getUserObjectClass().toLowerCase()))
173 // log.warn("Unsupported LDAP type for " + searchResult.getName());
179 } catch (AuthenticationNotSupportedException e
) {
180 // ignore (typically an unsupported anonymous bind)
181 // TODO better logging
183 } catch (NamingException e
) {
184 throw new IllegalStateException("Cannot get roles for filter " + f
, e
);
188 private LdapName
toDn(LdapName baseDn
, Binding binding
) throws InvalidNameException
{
189 return new LdapName(binding
.isRelative() ? binding
.getName() + "," + baseDn
: binding
.getName());
193 public List
<LdapName
> getDirectGroups(LdapName dn
) {
194 List
<LdapName
> directGroups
= new ArrayList
<LdapName
>();
196 String searchFilter
= "(&(" + objectClass
+ "=" + getDirectory().getGroupObjectClass() + ")("
197 + getDirectory().getMemberAttributeId() + "=" + dn
+ "))";
199 SearchControls searchControls
= new SearchControls();
200 searchControls
.setSearchScope(SearchControls
.SUBTREE_SCOPE
);
202 LdapName searchBase
= getDirectory().getBaseDn();
203 NamingEnumeration
<SearchResult
> results
= ldapConnection
.search(searchBase
, searchFilter
, searchControls
);
205 while (results
.hasMoreElements()) {
206 SearchResult searchResult
= (SearchResult
) results
.nextElement();
207 directGroups
.add(toDn(searchBase
, searchResult
));
210 } catch (NamingException e
) {
211 throw new IllegalStateException("Cannot populate direct members of " + dn
, e
);
216 public void prepare(LdapEntryWorkingCopy wc
) {
218 ldapConnection
.prepareChanges(wc
);
219 } catch (NamingException e
) {
220 throw new IllegalStateException("Cannot prepare LDAP", e
);
225 public void commit(LdapEntryWorkingCopy wc
) {
227 ldapConnection
.commitChanges(wc
);
228 } catch (NamingException e
) {
229 throw new IllegalStateException("Cannot commit LDAP", e
);
234 public void rollback(LdapEntryWorkingCopy wc
) {
235 // prepare not impacting
243 public Iterable
<HierarchyUnit
> doGetDirectHierarchyUnits(LdapName searchBase
, boolean functionalOnly
) {
244 List
<HierarchyUnit
> res
= new ArrayList
<>();
246 String searchFilter
= "(|(" + objectClass
+ "=" + LdapObjs
.organizationalUnit
.name() + ")(" + objectClass
247 + "=" + LdapObjs
.organization
.name() + "))";
248 // String searchFilter = "(|(" + objectClass + "=" + LdapObjs.organizationalUnit.name() + ")(" + objectClass
249 // + "=" + LdapObjs.organization.name() + ")(cn=accounts)(cn=users)(cn=groups))";
251 SearchControls searchControls
= new SearchControls();
252 searchControls
.setSearchScope(SearchControls
.ONELEVEL_SCOPE
);
253 // no attributes needed
254 searchControls
.setReturningAttributes(new String
[0]);
256 NamingEnumeration
<SearchResult
> results
= ldapConnection
.search(searchBase
, searchFilter
, searchControls
);
258 while (results
.hasMoreElements()) {
259 SearchResult searchResult
= (SearchResult
) results
.nextElement();
260 LdapName dn
= toDn(searchBase
, searchResult
);
261 // Attributes attrs = searchResult.getAttributes();
262 LdapHierarchyUnit hierarchyUnit
= new LdapHierarchyUnit(getDirectory(), dn
);
263 if (functionalOnly
) {
264 if (hierarchyUnit
.isFunctional())
265 res
.add(hierarchyUnit
);
267 res
.add(hierarchyUnit
);
271 } catch (NamingException e
) {
272 throw new IllegalStateException("Cannot get direct hierarchy units ", e
);
277 public HierarchyUnit
doGetHierarchyUnit(LdapName dn
) {
279 if (getDirectory().getBaseDn().equals(dn
))
280 return getDirectory();
281 if (!dn
.startsWith(getDirectory().getBaseDn()))
282 throw new IllegalArgumentException(dn
+ " does not start with base DN " + getDirectory().getBaseDn());
283 if (!ldapConnection
.entryExists(dn
))
285 return new LdapHierarchyUnit(getDirectory(), dn
);
286 } catch (NameNotFoundException e
) {
288 } catch (NamingException e
) {
289 throw new IllegalStateException("Cannot get hierarchy unit " + dn
, e
);