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