]> git.argeo.org Git - lgpl/argeo-commons.git/blob - LdapUserAdmin.java
22d9a54227b383d629f13e6be760731bd01b972d
[lgpl/argeo-commons.git] / LdapUserAdmin.java
1 package org.argeo.osgi.useradmin;
2
3 import static org.argeo.osgi.useradmin.LdifName.objectClass;
4
5 import java.util.ArrayList;
6 import java.util.Dictionary;
7 import java.util.Hashtable;
8 import java.util.List;
9
10 import javax.naming.Binding;
11 import javax.naming.Context;
12 import javax.naming.InvalidNameException;
13 import javax.naming.NameNotFoundException;
14 import javax.naming.NamingEnumeration;
15 import javax.naming.NamingException;
16 import javax.naming.directory.Attribute;
17 import javax.naming.directory.Attributes;
18 import javax.naming.directory.DirContext;
19 import javax.naming.directory.SearchControls;
20 import javax.naming.directory.SearchResult;
21 import javax.naming.ldap.InitialLdapContext;
22 import javax.naming.ldap.LdapName;
23 import javax.transaction.TransactionManager;
24
25 import org.apache.commons.logging.Log;
26 import org.apache.commons.logging.LogFactory;
27 import org.argeo.ArgeoException;
28 import org.osgi.framework.Filter;
29
30 /**
31 * A user admin based on a LDAP server. Requires a {@link TransactionManager}
32 * and an open transaction for write access.
33 */
34 public class LdapUserAdmin extends AbstractUserDirectory {
35 private final static Log log = LogFactory.getLog(LdapUserAdmin.class);
36
37 private InitialLdapContext initialLdapContext = null;
38
39 public LdapUserAdmin(Dictionary<String, ?> properties) {
40 super(properties);
41 try {
42 Hashtable<String, Object> connEnv = new Hashtable<String, Object>();
43 connEnv.put(Context.INITIAL_CONTEXT_FACTORY,
44 "com.sun.jndi.ldap.LdapCtxFactory");
45 connEnv.put(Context.PROVIDER_URL, getUri().toString());
46 connEnv.put("java.naming.ldap.attributes.binary",
47 LdifName.userPassword.name());
48
49 initialLdapContext = new InitialLdapContext(connEnv, null);
50 // StartTlsResponse tls = (StartTlsResponse) ctx
51 // .extendedOperation(new StartTlsRequest());
52 // tls.negotiate();
53 initialLdapContext.addToEnvironment(
54 Context.SECURITY_AUTHENTICATION, "simple");
55 Object principal = properties.get(Context.SECURITY_PRINCIPAL);
56 if (principal != null) {
57 initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL,
58 principal.toString());
59 Object creds = properties.get(Context.SECURITY_CREDENTIALS);
60 if (creds != null) {
61 initialLdapContext.addToEnvironment(
62 Context.SECURITY_CREDENTIALS, creds.toString());
63
64 }
65 }
66 // initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL,
67 // "uid=admin,ou=system");
68 // initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS,
69 // "secret");
70 } catch (Exception e) {
71 throw new UserDirectoryException("Cannot connect to LDAP", e);
72 }
73 }
74
75 public void destroy() {
76 try {
77 // tls.close();
78 initialLdapContext.close();
79 } catch (NamingException e) {
80 log.error("Cannot destroy LDAP user admin", e);
81 }
82 }
83
84 protected InitialLdapContext getLdapContext() {
85 return initialLdapContext;
86 }
87
88 @Override
89 protected Boolean daoHasRole(LdapName dn) {
90 return daoGetRole(dn) != null;
91 }
92
93 @Override
94 protected DirectoryUser daoGetRole(LdapName name) {
95 try {
96 Attributes attrs = getLdapContext().getAttributes(name);
97 if (attrs.size() == 0)
98 return null;
99 LdifUser res;
100 if (attrs.get(objectClass.name()).contains(getGroupObjectClass()))
101 res = new LdifGroup(this, name, attrs);
102 else if (attrs.get(objectClass.name()).contains(
103 getUserObjectClass()))
104 res = new LdifUser(this, name, attrs);
105 else
106 throw new UserDirectoryException("Unsupported LDAP type for "
107 + name);
108 return res;
109 } catch (NamingException e) {
110 return null;
111 }
112 }
113
114 @Override
115 protected List<DirectoryUser> doGetRoles(Filter f) {
116 try {
117 String searchFilter = f != null ? f.toString() : "(|("
118 + objectClass + "=" + getUserObjectClass() + ")("
119 + objectClass + "=" + getGroupObjectClass() + "))";
120 SearchControls searchControls = new SearchControls();
121 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
122
123 String searchBase = getBaseDn();
124 NamingEnumeration<SearchResult> results = getLdapContext().search(
125 searchBase, searchFilter, searchControls);
126
127 ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
128 results: while (results.hasMoreElements()) {
129 SearchResult searchResult = results.next();
130 Attributes attrs = searchResult.getAttributes();
131 Attribute objectClassAttr = attrs.get(objectClass.name());
132 LdapName dn = toDn(searchBase, searchResult);
133 LdifUser role;
134 if (objectClassAttr.contains(getGroupObjectClass()))
135 role = new LdifGroup(this, dn, attrs);
136 else if (objectClassAttr.contains(getUserObjectClass()))
137 role = new LdifUser(this, dn, attrs);
138 else {
139 log.warn("Unsupported LDAP type for "
140 + searchResult.getName());
141 continue results;
142 }
143 res.add(role);
144 }
145 return res;
146 } catch (Exception e) {
147 throw new UserDirectoryException(
148 "Cannot get roles for filter " + f, e);
149 }
150 }
151
152 private LdapName toDn(String baseDn, Binding binding)
153 throws InvalidNameException {
154 return new LdapName(binding.isRelative() ? binding.getName() + ","
155 + baseDn : binding.getName());
156 }
157
158 @Override
159 protected List<LdapName> getDirectGroups(LdapName dn) {
160 List<LdapName> directGroups = new ArrayList<LdapName>();
161 try {
162 String searchFilter = "(&(" + objectClass + "="
163 + getGroupObjectClass() + ")(" + getMemberAttributeId()
164 + "=" + dn + "))";
165
166 SearchControls searchControls = new SearchControls();
167 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
168
169 String searchBase = getBaseDn();
170 NamingEnumeration<SearchResult> results = getLdapContext().search(
171 searchBase, searchFilter, searchControls);
172
173 while (results.hasMoreElements()) {
174 SearchResult searchResult = (SearchResult) results
175 .nextElement();
176 directGroups.add(toDn(searchBase, searchResult));
177 }
178 return directGroups;
179 } catch (Exception e) {
180 throw new ArgeoException("Cannot populate direct members of " + dn,
181 e);
182 }
183 }
184
185 @Override
186 protected void prepare(UserDirectoryWorkingCopy wc) {
187 try {
188 getLdapContext().reconnect(getLdapContext().getConnectControls());
189 // delete
190 for (LdapName dn : wc.getDeletedUsers().keySet()) {
191 if (!entryExists(dn))
192 throw new UserDirectoryException("User to delete no found "
193 + dn);
194 }
195 // add
196 for (LdapName dn : wc.getNewUsers().keySet()) {
197 if (entryExists(dn))
198 throw new UserDirectoryException("User to create found "
199 + dn);
200 }
201 // modify
202 for (LdapName dn : wc.getModifiedUsers().keySet()) {
203 if (!entryExists(dn))
204 throw new UserDirectoryException("User to modify not found "
205 + dn);
206 }
207 } catch (NamingException e) {
208 throw new UserDirectoryException("Cannot prepare LDAP", e);
209 }
210 }
211
212 private boolean entryExists(LdapName dn) throws NamingException {
213 try {
214 return getLdapContext().getAttributes(dn).size() != 0;
215 } catch (NameNotFoundException e) {
216 return false;
217 }
218 }
219
220 @Override
221 protected void commit(UserDirectoryWorkingCopy wc) {
222 try {
223 // delete
224 for (LdapName dn : wc.getDeletedUsers().keySet()) {
225 getLdapContext().destroySubcontext(dn);
226 }
227 // add
228 for (LdapName dn : wc.getNewUsers().keySet()) {
229 DirectoryUser user = wc.getNewUsers().get(dn);
230 getLdapContext().createSubcontext(dn, user.getAttributes());
231 }
232 // modify
233 for (LdapName dn : wc.getModifiedUsers().keySet()) {
234 Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
235 getLdapContext().modifyAttributes(dn,
236 DirContext.REPLACE_ATTRIBUTE, modifiedAttrs);
237 }
238 } catch (NamingException e) {
239 throw new UserDirectoryException("Cannot commit LDAP", e);
240 }
241 }
242
243 @Override
244 protected void rollback(UserDirectoryWorkingCopy wc) {
245 // prepare not impacting
246 }
247
248 }