]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/directory/ldap/LdapDao.java
Improve ACR attribute typing.
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / directory / ldap / LdapDao.java
1 package org.argeo.cms.directory.ldap;
2
3 import static org.argeo.api.acr.ldap.LdapAttr.objectClass;
4
5 import java.util.ArrayList;
6 import java.util.List;
7
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;
21
22 import org.argeo.api.acr.ldap.LdapAttr;
23 import org.argeo.api.acr.ldap.LdapObj;
24 import org.argeo.api.cms.directory.HierarchyUnit;
25
26 /** A user admin based on a LDAP server. */
27 public class LdapDao extends AbstractLdapDirectoryDao {
28 private LdapConnection ldapConnection;
29
30 public LdapDao(AbstractLdapDirectory directory) {
31 super(directory);
32 }
33
34 @Override
35 public void init() {
36 ldapConnection = new LdapConnection(getDirectory().getUri().toString(), getDirectory().cloneConfigProperties());
37 }
38
39 public void destroy() {
40 ldapConnection.destroy();
41 }
42
43 @Override
44 public boolean checkConnection() {
45 try {
46 return ldapConnection.entryExists(getDirectory().getBaseDn());
47 } catch (NamingException e) {
48 return false;
49 }
50 }
51
52 @Override
53 public boolean entryExists(LdapName dn) {
54 try {
55 return ldapConnection.entryExists(dn);
56 } catch (NameNotFoundException e) {
57 return false;
58 } catch (NamingException e) {
59 throw new IllegalStateException("Cannot check " + dn, e);
60 }
61 }
62
63 @Override
64 public LdapEntry doGetEntry(LdapName name) throws NameNotFoundException {
65 // if (!entryExists(name))
66 // throw new NameNotFoundException(name + " was not found in " + getDirectory().getBaseDn());
67 try {
68 Attributes attrs = ldapConnection.getAttributes(name);
69
70 LdapEntry res;
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(LdapAttr.objectClass.name(), LdapObj.top.name());
76 attrs.put(LdapAttr.objectClass.name(), getDirectory().getGroupObjectClass());
77 }
78 res = newGroup(name);
79 } else if (getDirectory().getSystemRoleBaseRdn().equals(technicalRdn)) {
80 if (attrs.size() == 0) {// exists but not accessible
81 attrs = new BasicAttributes();
82 attrs.put(LdapAttr.objectClass.name(), LdapObj.top.name());
83 attrs.put(LdapAttr.objectClass.name(), getDirectory().getGroupObjectClass());
84 }
85 res = newGroup(name);
86 } else if (getDirectory().getUserBaseRdn().equals(technicalRdn)) {
87 if (attrs.size() == 0) {// exists but not accessible
88 attrs = new BasicAttributes();
89 attrs.put(LdapAttr.objectClass.name(), LdapObj.top.name());
90 attrs.put(LdapAttr.objectClass.name(), getDirectory().getUserObjectClass());
91 }
92 res = newUser(name);
93 } else {
94 res = new DefaultLdapEntry(getDirectory(), name);
95 }
96 return res;
97 } catch (NameNotFoundException e) {
98 throw e;
99 } catch (NamingException e) {
100 throw new IllegalStateException("Cannot retrieve entry " + name, e);
101 }
102 }
103
104 @Override
105 public Attributes doGetAttributes(LdapName name) {
106 try {
107 Attributes attrs = ldapConnection.getAttributes(name);
108 return attrs;
109 } catch (NamingException e) {
110 throw new IllegalStateException("Cannot get attributes for " + name);
111 }
112 }
113
114 @Override
115 public List<LdapEntry> doGetEntries(LdapName searchBase, String f, boolean deep) {
116 ArrayList<LdapEntry> res = new ArrayList<>();
117 try {
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);
126
127 // LdapName searchBase = getBaseDn();
128 NamingEnumeration<SearchResult> results = ldapConnection.search(searchBase, searchFilter, searchControls);
129
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);
135 LdapEntry role;
136 if (objectClassAttr.contains(getDirectory().getGroupObjectClass())
137 || objectClassAttr.contains(getDirectory().getGroupObjectClass().toLowerCase()))
138 role = newGroup(dn);
139 else if (objectClassAttr.contains(getDirectory().getUserObjectClass())
140 || objectClassAttr.contains(getDirectory().getUserObjectClass().toLowerCase()))
141 role = newUser(dn);
142 else {
143 // log.warn("Unsupported LDAP type for " + searchResult.getName());
144 continue results;
145 }
146 res.add(role);
147 }
148 return res;
149 } catch (AuthenticationNotSupportedException e) {
150 // ignore (typically an unsupported anonymous bind)
151 // TODO better logging
152 return res;
153 } catch (NamingException e) {
154 throw new IllegalStateException("Cannot get roles for filter " + f, e);
155 }
156 }
157
158 private LdapName toDn(LdapName baseDn, Binding binding) throws InvalidNameException {
159 return new LdapName(binding.isRelative() ? binding.getName() + "," + baseDn : binding.getName());
160 }
161
162 @Override
163 public List<LdapName> getDirectGroups(LdapName dn) {
164 List<LdapName> directGroups = new ArrayList<LdapName>();
165 try {
166 String searchFilter = "(&(" + objectClass + "=" + getDirectory().getGroupObjectClass() + ")("
167 + getDirectory().getMemberAttributeId() + "=" + dn + "))";
168
169 SearchControls searchControls = new SearchControls();
170 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
171
172 LdapName searchBase = getDirectory().getBaseDn();
173 NamingEnumeration<SearchResult> results = ldapConnection.search(searchBase, searchFilter, searchControls);
174
175 while (results.hasMoreElements()) {
176 SearchResult searchResult = (SearchResult) results.nextElement();
177 directGroups.add(toDn(searchBase, searchResult));
178 }
179 return directGroups;
180 } catch (NamingException e) {
181 throw new IllegalStateException("Cannot populate direct members of " + dn, e);
182 }
183 }
184
185 @Override
186 public void prepare(LdapEntryWorkingCopy wc) {
187 try {
188 ldapConnection.prepareChanges(wc);
189 } catch (NamingException e) {
190 throw new IllegalStateException("Cannot prepare LDAP", e);
191 }
192 }
193
194 @Override
195 public void commit(LdapEntryWorkingCopy wc) {
196 try {
197 ldapConnection.commitChanges(wc);
198 } catch (NamingException e) {
199 throw new IllegalStateException("Cannot commit LDAP", e);
200 }
201 }
202
203 @Override
204 public void rollback(LdapEntryWorkingCopy wc) {
205 // prepare not impacting
206 }
207
208 /*
209 * HIERARCHY
210 */
211
212 @Override
213 public Iterable<HierarchyUnit> doGetDirectHierarchyUnits(LdapName searchBase, boolean functionalOnly) {
214 List<HierarchyUnit> res = new ArrayList<>();
215 try {
216 String structuralFilter = functionalOnly ? ""
217 : "(" + getDirectory().getUserBaseRdn() + ")(" + getDirectory().getGroupBaseRdn() + ")("
218 + getDirectory().getSystemRoleBaseRdn() + ")";
219 String searchFilter = "(|(" + objectClass + "=" + LdapObj.organizationalUnit.name() + ")(" + objectClass
220 + "=" + LdapObj.organization.name() + ")" + structuralFilter + ")";
221
222 SearchControls searchControls = new SearchControls();
223 searchControls.setSearchScope(SearchControls.ONELEVEL_SCOPE);
224 // no attributes needed
225 searchControls.setReturningAttributes(new String[0]);
226
227 NamingEnumeration<SearchResult> results = ldapConnection.search(searchBase, searchFilter, searchControls);
228
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);
237 } else {
238 res.add(hierarchyUnit);
239 }
240 }
241 return res;
242 } catch (NamingException e) {
243 throw new IllegalStateException("Cannot get direct hierarchy units ", e);
244 }
245 }
246
247 @Override
248 public HierarchyUnit doGetHierarchyUnit(LdapName dn) {
249 try {
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))
255 return null;
256 return new LdapHierarchyUnit(getDirectory(), dn);
257 } catch (NameNotFoundException e) {
258 return null;
259 } catch (NamingException e) {
260 throw new IllegalStateException("Cannot get hierarchy unit " + dn, e);
261 }
262 }
263
264 }