]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java
0cb435f073e39e25b24efde6a49ac696badd0c85
[lgpl/argeo-commons.git] / org.argeo.security.core / src / org / argeo / osgi / useradmin / LdapUserAdmin.java
1 package org.argeo.osgi.useradmin;
2
3 import java.net.URI;
4 import java.util.ArrayList;
5 import java.util.Hashtable;
6 import java.util.List;
7
8 import javax.naming.Binding;
9 import javax.naming.Context;
10 import javax.naming.InvalidNameException;
11 import javax.naming.NamingEnumeration;
12 import javax.naming.NamingException;
13 import javax.naming.directory.Attributes;
14 import javax.naming.directory.DirContext;
15 import javax.naming.directory.ModificationItem;
16 import javax.naming.directory.SearchControls;
17 import javax.naming.directory.SearchResult;
18 import javax.naming.ldap.InitialLdapContext;
19 import javax.naming.ldap.LdapContext;
20 import javax.naming.ldap.LdapName;
21 import javax.transaction.xa.XAException;
22 import javax.transaction.xa.Xid;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26 import org.argeo.ArgeoException;
27 import org.osgi.framework.Filter;
28 import org.osgi.framework.InvalidSyntaxException;
29 import org.osgi.service.useradmin.Authorization;
30 import org.osgi.service.useradmin.Group;
31 import org.osgi.service.useradmin.Role;
32 import org.osgi.service.useradmin.User;
33
34 public class LdapUserAdmin extends AbstractUserDirectory {
35 private final static Log log = LogFactory.getLog(LdapUserAdmin.class);
36
37 private String baseDn = "dc=example,dc=com";
38 private InitialLdapContext initialLdapContext = null;
39
40 public LdapUserAdmin(String uri) {
41 try {
42 setUri(new URI(uri));
43 Hashtable<String, Object> connEnv = new Hashtable<String, Object>();
44 connEnv.put(Context.INITIAL_CONTEXT_FACTORY,
45 "com.sun.jndi.ldap.LdapCtxFactory");
46 connEnv.put(Context.PROVIDER_URL, getUri().toString());
47 connEnv.put("java.naming.ldap.attributes.binary", "userPassword");
48 // connEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
49 // connEnv.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
50 // connEnv.put(Context.SECURITY_CREDENTIALS, "secret");
51
52 initialLdapContext = new InitialLdapContext(connEnv, null);
53 // StartTlsResponse tls = (StartTlsResponse) ctx
54 // .extendedOperation(new StartTlsRequest());
55 // tls.negotiate();
56 initialLdapContext.addToEnvironment(
57 Context.SECURITY_AUTHENTICATION, "simple");
58 initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL,
59 "uid=admin,ou=system");
60 initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS,
61 "secret");
62 LdapContext ldapContext = (LdapContext) initialLdapContext
63 .lookup("uid=root,ou=users,dc=example,dc=com");
64 log.debug(initialLdapContext.getAttributes(
65 "uid=root,ou=users,dc=example,dc=com").get("cn"));
66 } catch (Exception e) {
67 throw new UserDirectoryException("Cannot connect to LDAP", e);
68 }
69 }
70
71 public void destroy() {
72 try {
73 // tls.close();
74 initialLdapContext.close();
75 } catch (NamingException e) {
76 log.error("Cannot destroy LDAP user admin", e);
77 }
78 }
79
80 @Override
81 protected Boolean daoHasRole(LdapName dn) {
82 return daoGetRole(dn) != null;
83 }
84
85 @Override
86 protected DirectoryUser daoGetRole(LdapName name) {
87 try {
88 Attributes attrs = initialLdapContext.getAttributes(name);
89 if (attrs.size() == 0)
90 return null;
91 LdifUser res;
92 if (attrs.get("objectClass").contains("groupOfNames"))
93 res = new LdifGroup(this, name, attrs);
94 else if (attrs.get("objectClass").contains("inetOrgPerson"))
95 res = new LdifUser(this, name, attrs);
96 else
97 throw new UserDirectoryException("Unsupported LDAP type for "
98 + name);
99 return res;
100 } catch (NamingException e) {
101 throw new UserDirectoryException("Cannot get role for " + name, e);
102 }
103 }
104
105 @Override
106 protected List<DirectoryUser> doGetRoles(Filter f) {
107 // TODO Auto-generated method stub
108 try {
109 String searchFilter = f != null ? f.toString()
110 : "(|(objectClass=inetOrgPerson)(objectClass=groupOfNames))";
111 SearchControls searchControls = new SearchControls();
112 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
113
114 String searchBase = baseDn;
115 NamingEnumeration<SearchResult> results = initialLdapContext
116 .search(searchBase, searchFilter, searchControls);
117
118 ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
119 while (results.hasMoreElements()) {
120 SearchResult searchResult = results.next();
121 Attributes attrs = searchResult.getAttributes();
122 LdifUser role;
123 if (attrs.get("objectClass").contains("groupOfNames"))
124 role = new LdifGroup(this, toDn(searchBase, searchResult),
125 attrs);
126 else if (attrs.get("objectClass").contains("inetOrgPerson"))
127 role = new LdifUser(this, toDn(searchBase, searchResult),
128 attrs);
129 else
130 throw new UserDirectoryException(
131 "Unsupported LDAP type for "
132 + searchResult.getName());
133 res.add(role);
134 }
135 return res;
136 } catch (Exception e) {
137 throw new UserDirectoryException(
138 "Cannot get roles for filter " + f, e);
139 }
140 }
141
142 @Override
143 protected void doGetUser(String key, String value,
144 List<DirectoryUser> collectedUsers) {
145 try {
146 String searchFilter = "(&(objectClass=inetOrgPerson)(" + key + "="
147 + value + "))";
148
149 SearchControls searchControls = new SearchControls();
150 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
151
152 String searchBase = baseDn;
153 NamingEnumeration<SearchResult> results = initialLdapContext
154 .search(searchBase, searchFilter, searchControls);
155
156 SearchResult searchResult = null;
157 if (results.hasMoreElements()) {
158 searchResult = (SearchResult) results.nextElement();
159 if (results.hasMoreElements())
160 searchResult = null;
161 }
162 if (searchResult != null)
163 collectedUsers.add(new LdifUser(this, toDn(searchBase,
164 searchResult), searchResult.getAttributes()));
165 } catch (Exception e) {
166 throw new UserDirectoryException("Cannot get user with " + key
167 + "=" + value, e);
168 }
169
170 }
171
172 @Override
173 public User getUser(String key, String value) {
174 if (key == null) {
175 List<User> users = new ArrayList<User>();
176 for (String prop : getIndexedUserProperties()) {
177 User user = getUser(prop, value);
178 if (user != null)
179 users.add(user);
180 }
181 if (users.size() == 1)
182 return users.get(0);
183 else
184 return null;
185 }
186
187 try {
188 String searchFilter = "(&(objectClass=inetOrgPerson)(" + key + "="
189 + value + "))";
190
191 SearchControls searchControls = new SearchControls();
192 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
193
194 String searchBase = baseDn;
195 NamingEnumeration<SearchResult> results = initialLdapContext
196 .search(searchBase, searchFilter, searchControls);
197
198 SearchResult searchResult = null;
199 if (results.hasMoreElements()) {
200 searchResult = (SearchResult) results.nextElement();
201 if (results.hasMoreElements())
202 searchResult = null;
203 }
204 if (searchResult == null)
205 return null;
206 return new LdifUser(this, toDn(searchBase, searchResult),
207 searchResult.getAttributes());
208 } catch (Exception e) {
209 throw new UserDirectoryException("Cannot get user with " + key
210 + "=" + value, e);
211 }
212 }
213
214 private LdapName toDn(String baseDn, Binding binding)
215 throws InvalidNameException {
216 return new LdapName(binding.isRelative() ? binding.getName() + ","
217 + baseDn : binding.getName());
218 }
219
220 // void populateDirectMemberOf(LdifUser user) {
221 //
222 // try {
223 // String searchFilter = "(&(objectClass=groupOfNames)(member="
224 // + user.getName() + "))";
225 //
226 // SearchControls searchControls = new SearchControls();
227 // searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
228 //
229 // String searchBase = "ou=node";
230 // NamingEnumeration<SearchResult> results = initialLdapContext
231 // .search(searchBase, searchFilter, searchControls);
232 //
233 // // TODO synchro
234 // //user.directMemberOf.clear();
235 // while (results.hasMoreElements()) {
236 // SearchResult searchResult = (SearchResult) results
237 // .nextElement();
238 // LdifGroup group = new LdifGroup(toDn(searchBase, searchResult),
239 // searchResult.getAttributes());
240 // populateDirectMemberOf(group);
241 // //user.directMemberOf.add(group);
242 // }
243 // } catch (Exception e) {
244 // throw new ArgeoException("Cannot populate direct members of "
245 // + user, e);
246 // }
247 // }
248
249 @Override
250 protected List<DirectoryGroup> getDirectGroups(User user) {
251 List<DirectoryGroup> directGroups = new ArrayList<DirectoryGroup>();
252 try {
253 String searchFilter = "(&(objectClass=groupOfNames)(member="
254 + user.getName() + "))";
255
256 SearchControls searchControls = new SearchControls();
257 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
258
259 String searchBase = getGroupsSearchBase();
260 NamingEnumeration<SearchResult> results = initialLdapContext
261 .search(searchBase, searchFilter, searchControls);
262
263 while (results.hasMoreElements()) {
264 SearchResult searchResult = (SearchResult) results
265 .nextElement();
266 LdifGroup group = new LdifGroup(this, toDn(searchBase,
267 searchResult), searchResult.getAttributes());
268 directGroups.add(group);
269 }
270 return directGroups;
271 } catch (Exception e) {
272 throw new ArgeoException("Cannot populate direct members of "
273 + user, e);
274 }
275 }
276
277 protected String getGroupsSearchBase() {
278 // TODO configure group search base
279 return baseDn;
280 }
281
282 @Override
283 protected void prepare(WorkingCopy wc) {
284 try {
285 initialLdapContext.reconnect(initialLdapContext
286 .getConnectControls());
287 // delete
288 for (LdapName dn : wc.getDeletedUsers().keySet()) {
289 if (!entryExists(dn))
290 throw new UserDirectoryException("User to delete no found "
291 + dn);
292 }
293 // add
294 for (LdapName dn : wc.getNewUsers().keySet()) {
295 if (!entryExists(dn))
296 throw new UserDirectoryException("User to create found "
297 + dn);
298 }
299 // modify
300 for (LdapName dn : wc.getModifiedUsers().keySet()) {
301 if (!entryExists(dn))
302 throw new UserDirectoryException("User to modify no found "
303 + dn);
304 }
305 } catch (NamingException e) {
306 throw new UserDirectoryException("Cannot prepare LDAP", e);
307 }
308 }
309
310 private boolean entryExists(LdapName dn) throws NamingException {
311 return initialLdapContext.getAttributes(dn).size() != 0;
312 }
313
314 @Override
315 protected void commit(WorkingCopy wc) {
316 try {
317 // delete
318 for (LdapName dn : wc.getDeletedUsers().keySet()) {
319 initialLdapContext.destroySubcontext(dn);
320 }
321 // add
322 for (LdapName dn : wc.getNewUsers().keySet()) {
323 DirectoryUser user = wc.getNewUsers().get(dn);
324 initialLdapContext.createSubcontext(dn, user.getAttributes());
325 }
326 // modify
327 for (LdapName dn : wc.getModifiedUsers().keySet()) {
328 Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
329 initialLdapContext.modifyAttributes(dn,
330 DirContext.REPLACE_ATTRIBUTE, modifiedAttrs);
331 }
332 } catch (NamingException e) {
333 throw new UserDirectoryException("Cannot commit LDAP", e);
334 }
335 }
336
337 @Override
338 protected void rollback(WorkingCopy wc) {
339 // TODO Auto-generated method stub
340 super.rollback(wc);
341 }
342
343 }