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 LdapDao(AbstractLdapDirectory directory
) {
36 ldapConnection
= new LdapConnection(getDirectory().getUri().toString(), getDirectory().cloneConfigProperties());
39 public void destroy() {
40 ldapConnection
.destroy();
44 public boolean checkConnection() {
46 return ldapConnection
.entryExists(getDirectory().getBaseDn());
47 } catch (NamingException e
) {
53 public boolean entryExists(LdapName dn
) {
55 return ldapConnection
.entryExists(dn
);
56 } catch (NameNotFoundException e
) {
58 } catch (NamingException e
) {
59 throw new IllegalStateException("Cannot check " + dn
, e
);
64 public LdapEntry
doGetEntry(LdapName name
) throws NameNotFoundException
{
65 // if (!entryExists(name))
66 // throw new NameNotFoundException(name + " was not found in " + getDirectory().getBaseDn());
68 Attributes attrs
= ldapConnection
.getAttributes(name
);
71 Rdn technicalRdn
= LdapNameUtils
.getParentRdn(name
);
72 if (getDirectory().getGroupBaseRdn().equals(technicalRdn
)) {
73 if (attrs
.size() == 0) {// exists but not accessible
74 attrs
= new BasicAttributes();
75 attrs
.put(LdapAttrs
.objectClass
.name(), LdapObjs
.top
.name());
76 attrs
.put(LdapAttrs
.objectClass
.name(), getDirectory().getGroupObjectClass());
79 } else if (getDirectory().getSystemRoleBaseRdn().equals(technicalRdn
)) {
80 if (attrs
.size() == 0) {// exists but not accessible
81 attrs
= new BasicAttributes();
82 attrs
.put(LdapAttrs
.objectClass
.name(), LdapObjs
.top
.name());
83 attrs
.put(LdapAttrs
.objectClass
.name(), getDirectory().getGroupObjectClass());
86 } else if (getDirectory().getUserBaseRdn().equals(technicalRdn
)) {
87 if (attrs
.size() == 0) {// exists but not accessible
88 attrs
= new BasicAttributes();
89 attrs
.put(LdapAttrs
.objectClass
.name(), LdapObjs
.top
.name());
90 attrs
.put(LdapAttrs
.objectClass
.name(), getDirectory().getUserObjectClass());
94 res
= new DefaultLdapEntry(getDirectory(), name
);
97 } catch (NameNotFoundException e
) {
99 } catch (NamingException e
) {
100 throw new IllegalStateException("Cannot retrieve entry " + name
, e
);
105 public Attributes
doGetAttributes(LdapName name
) {
107 Attributes attrs
= ldapConnection
.getAttributes(name
);
109 } catch (NamingException e
) {
110 throw new IllegalStateException("Cannot get attributes for " + name
);
115 public List
<LdapEntry
> doGetEntries(LdapName searchBase
, String f
, boolean deep
) {
116 ArrayList
<LdapEntry
> res
= new ArrayList
<>();
118 String searchFilter
= f
!= null ? f
.toString()
119 : "(|(" + objectClass
.name() + "=" + getDirectory().getUserObjectClass() + ")(" + objectClass
.name()
120 + "=" + getDirectory().getGroupObjectClass() + "))";
121 SearchControls searchControls
= new SearchControls();
122 // only attribute needed is objectClass
123 searchControls
.setReturningAttributes(new String
[] { objectClass
.name() });
124 // FIXME make one level consistent with deep
125 searchControls
.setSearchScope(deep ? SearchControls
.SUBTREE_SCOPE
: SearchControls
.ONELEVEL_SCOPE
);
127 // LdapName searchBase = getBaseDn();
128 NamingEnumeration
<SearchResult
> results
= ldapConnection
.search(searchBase
, searchFilter
, searchControls
);
130 results
: while (results
.hasMoreElements()) {
131 SearchResult searchResult
= results
.next();
132 Attributes attrs
= searchResult
.getAttributes();
133 Attribute objectClassAttr
= attrs
.get(objectClass
.name());
134 LdapName dn
= toDn(searchBase
, searchResult
);
136 if (objectClassAttr
.contains(getDirectory().getGroupObjectClass())
137 || objectClassAttr
.contains(getDirectory().getGroupObjectClass().toLowerCase()))
139 else if (objectClassAttr
.contains(getDirectory().getUserObjectClass())
140 || objectClassAttr
.contains(getDirectory().getUserObjectClass().toLowerCase()))
143 // log.warn("Unsupported LDAP type for " + searchResult.getName());
149 } catch (AuthenticationNotSupportedException e
) {
150 // ignore (typically an unsupported anonymous bind)
151 // TODO better logging
153 } catch (NamingException e
) {
154 throw new IllegalStateException("Cannot get roles for filter " + f
, e
);
158 private LdapName
toDn(LdapName baseDn
, Binding binding
) throws InvalidNameException
{
159 return new LdapName(binding
.isRelative() ? binding
.getName() + "," + baseDn
: binding
.getName());
163 public List
<LdapName
> getDirectGroups(LdapName dn
) {
164 List
<LdapName
> directGroups
= new ArrayList
<LdapName
>();
166 String searchFilter
= "(&(" + objectClass
+ "=" + getDirectory().getGroupObjectClass() + ")("
167 + getDirectory().getMemberAttributeId() + "=" + dn
+ "))";
169 SearchControls searchControls
= new SearchControls();
170 searchControls
.setSearchScope(SearchControls
.SUBTREE_SCOPE
);
172 LdapName searchBase
= getDirectory().getBaseDn();
173 NamingEnumeration
<SearchResult
> results
= ldapConnection
.search(searchBase
, searchFilter
, searchControls
);
175 while (results
.hasMoreElements()) {
176 SearchResult searchResult
= (SearchResult
) results
.nextElement();
177 directGroups
.add(toDn(searchBase
, searchResult
));
180 } catch (NamingException e
) {
181 throw new IllegalStateException("Cannot populate direct members of " + dn
, e
);
186 public void prepare(LdapEntryWorkingCopy wc
) {
188 ldapConnection
.prepareChanges(wc
);
189 } catch (NamingException e
) {
190 throw new IllegalStateException("Cannot prepare LDAP", e
);
195 public void commit(LdapEntryWorkingCopy wc
) {
197 ldapConnection
.commitChanges(wc
);
198 } catch (NamingException e
) {
199 throw new IllegalStateException("Cannot commit LDAP", e
);
204 public void rollback(LdapEntryWorkingCopy wc
) {
205 // prepare not impacting
213 public Iterable
<HierarchyUnit
> doGetDirectHierarchyUnits(LdapName searchBase
, boolean functionalOnly
) {
214 List
<HierarchyUnit
> res
= new ArrayList
<>();
216 String searchFilter
= "(|(" + objectClass
+ "=" + LdapObjs
.organizationalUnit
.name() + ")(" + objectClass
217 + "=" + LdapObjs
.organization
.name() + "))";
218 // String searchFilter = "(|(" + objectClass + "=" + LdapObjs.organizationalUnit.name() + ")(" + objectClass
219 // + "=" + LdapObjs.organization.name() + ")(cn=accounts)(cn=users)(cn=groups))";
221 SearchControls searchControls
= new SearchControls();
222 searchControls
.setSearchScope(SearchControls
.ONELEVEL_SCOPE
);
223 // no attributes needed
224 searchControls
.setReturningAttributes(new String
[0]);
226 NamingEnumeration
<SearchResult
> results
= ldapConnection
.search(searchBase
, searchFilter
, searchControls
);
228 while (results
.hasMoreElements()) {
229 SearchResult searchResult
= (SearchResult
) results
.nextElement();
230 LdapName dn
= toDn(searchBase
, searchResult
);
231 // Attributes attrs = searchResult.getAttributes();
232 LdapHierarchyUnit hierarchyUnit
= new LdapHierarchyUnit(getDirectory(), dn
);
233 if (functionalOnly
) {
234 if (hierarchyUnit
.isFunctional())
235 res
.add(hierarchyUnit
);
237 res
.add(hierarchyUnit
);
241 } catch (NamingException e
) {
242 throw new IllegalStateException("Cannot get direct hierarchy units ", e
);
247 public HierarchyUnit
doGetHierarchyUnit(LdapName dn
) {
249 if (getDirectory().getBaseDn().equals(dn
))
250 return getDirectory();
251 if (!dn
.startsWith(getDirectory().getBaseDn()))
252 throw new IllegalArgumentException(dn
+ " does not start with base DN " + getDirectory().getBaseDn());
253 if (!ldapConnection
.entryExists(dn
))
255 return new LdapHierarchyUnit(getDirectory(), dn
);
256 } catch (NameNotFoundException e
) {
258 } catch (NamingException e
) {
259 throw new IllegalStateException("Cannot get hierarchy unit " + dn
, e
);