1 package org
.argeo
.cms
.directory
.ldap
;
3 import static org
.argeo
.api
.acr
.ldap
.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
.api
.acr
.ldap
.LdapAttrs
;
23 import org
.argeo
.api
.acr
.ldap
.LdapObjs
;
24 import org
.argeo
.api
.cms
.directory
.HierarchyUnit
;
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 structuralFilter
= functionalOnly ?
""
217 : "(" + getDirectory().getUserBaseRdn() + ")(" + getDirectory().getGroupBaseRdn() + ")("
218 + getDirectory().getSystemRoleBaseRdn() + ")";
219 String searchFilter
= "(|(" + objectClass
+ "=" + LdapObjs
.organizationalUnit
.name() + ")(" + objectClass
220 + "=" + LdapObjs
.organization
.name() + ")" + structuralFilter
+ ")";
222 SearchControls searchControls
= new SearchControls();
223 searchControls
.setSearchScope(SearchControls
.ONELEVEL_SCOPE
);
224 // no attributes needed
225 searchControls
.setReturningAttributes(new String
[0]);
227 NamingEnumeration
<SearchResult
> results
= ldapConnection
.search(searchBase
, searchFilter
, searchControls
);
229 while (results
.hasMoreElements()) {
230 SearchResult searchResult
= (SearchResult
) results
.nextElement();
231 LdapName dn
= toDn(searchBase
, searchResult
);
232 // Attributes attrs = searchResult.getAttributes();
233 LdapHierarchyUnit hierarchyUnit
= new LdapHierarchyUnit(getDirectory(), dn
);
234 if (functionalOnly
) {
235 if (hierarchyUnit
.isFunctional())
236 res
.add(hierarchyUnit
);
238 res
.add(hierarchyUnit
);
242 } catch (NamingException e
) {
243 throw new IllegalStateException("Cannot get direct hierarchy units ", e
);
248 public HierarchyUnit
doGetHierarchyUnit(LdapName dn
) {
250 if (getDirectory().getBaseDn().equals(dn
))
251 return getDirectory();
252 if (!dn
.startsWith(getDirectory().getBaseDn()))
253 throw new IllegalArgumentException(dn
+ " does not start with base DN " + getDirectory().getBaseDn());
254 if (!ldapConnection
.entryExists(dn
))
256 return new LdapHierarchyUnit(getDirectory(), dn
);
257 } catch (NameNotFoundException e
) {
259 } catch (NamingException e
) {
260 throw new IllegalStateException("Cannot get hierarchy unit " + dn
, e
);