]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdapUserAdmin.java
Prepare next development cycle
[lgpl/argeo-commons.git] / org.argeo.enterprise / src / org / argeo / osgi / 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 String username = (String) credentials.get(SHARED_STATE_USERNAME);
86 if (username == null)
87 username = user.getName();
88 Dictionary<String, Object> properties = cloneProperties();
89 properties.put(Context.SECURITY_PRINCIPAL, username.toString());
90 Object pwdCred = credentials.get(SHARED_STATE_PASSWORD);
91 byte[] pwd = (byte[]) pwdCred;
92 if (pwd != null) {
93 char[] password = DigestUtils.bytesToChars(pwd);
94 properties.put(Context.SECURITY_CREDENTIALS, new String(password));
95 } else {
96 properties.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
97 }
98 return new LdapUserAdmin(properties);
99 }
100
101 protected InitialLdapContext getLdapContext() {
102 return initialLdapContext;
103 }
104
105 @Override
106 protected Boolean daoHasRole(LdapName dn) {
107 try {
108 return daoGetRole(dn) != null;
109 } catch (NameNotFoundException e) {
110 return false;
111 }
112 }
113
114 @Override
115 protected DirectoryUser daoGetRole(LdapName name) throws NameNotFoundException {
116 try {
117 Attributes attrs = getLdapContext().getAttributes(name);
118 if (attrs.size() == 0)
119 return null;
120 int roleType = roleType(name);
121 LdifUser res;
122 if (roleType == Role.GROUP)
123 res = new LdifGroup(this, name, attrs);
124 else if (roleType == Role.USER)
125 res = new LdifUser(this, name, attrs);
126 else
127 throw new UserDirectoryException("Unsupported LDAP type for " + name);
128 return res;
129 } catch (NameNotFoundException e) {
130 throw e;
131 } catch (NamingException e) {
132 if (log.isTraceEnabled())
133 log.error("Cannot get role: " + name, e);
134 return null;
135 }
136 }
137
138 @Override
139 protected List<DirectoryUser> doGetRoles(Filter f) {
140 try {
141 String searchFilter = f != null ? f.toString()
142 : "(|(" + objectClass + "=" + getUserObjectClass() + ")(" + objectClass + "="
143 + getGroupObjectClass() + "))";
144 SearchControls searchControls = new SearchControls();
145 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
146
147 LdapName searchBase = getBaseDn();
148 NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
149
150 ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
151 results: while (results.hasMoreElements()) {
152 SearchResult searchResult = results.next();
153 Attributes attrs = searchResult.getAttributes();
154 Attribute objectClassAttr = attrs.get(objectClass.name());
155 LdapName dn = toDn(searchBase, searchResult);
156 LdifUser role;
157 if (objectClassAttr.contains(getGroupObjectClass())
158 || objectClassAttr.contains(getGroupObjectClass().toLowerCase()))
159 role = new LdifGroup(this, dn, attrs);
160 else if (objectClassAttr.contains(getUserObjectClass())
161 || objectClassAttr.contains(getUserObjectClass().toLowerCase()))
162 role = new LdifUser(this, dn, attrs);
163 else {
164 log.warn("Unsupported LDAP type for " + searchResult.getName());
165 continue results;
166 }
167 res.add(role);
168 }
169 return res;
170 } catch (Exception e) {
171 throw new UserDirectoryException("Cannot get roles for filter " + f, e);
172 }
173 }
174
175 private LdapName toDn(LdapName baseDn, Binding binding) throws InvalidNameException {
176 return new LdapName(binding.isRelative() ? binding.getName() + "," + baseDn : binding.getName());
177 }
178
179 @Override
180 protected List<LdapName> getDirectGroups(LdapName dn) {
181 List<LdapName> directGroups = new ArrayList<LdapName>();
182 try {
183 String searchFilter = "(&(" + objectClass + "=" + getGroupObjectClass() + ")(" + getMemberAttributeId()
184 + "=" + dn + "))";
185
186 SearchControls searchControls = new SearchControls();
187 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
188
189 LdapName searchBase = getBaseDn();
190 NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
191
192 while (results.hasMoreElements()) {
193 SearchResult searchResult = (SearchResult) results.nextElement();
194 directGroups.add(toDn(searchBase, searchResult));
195 }
196 return directGroups;
197 } catch (Exception e) {
198 throw new UserDirectoryException("Cannot populate direct members of " + dn, e);
199 }
200 }
201
202 @Override
203 protected void prepare(UserDirectoryWorkingCopy wc) {
204 try {
205 getLdapContext().reconnect(getLdapContext().getConnectControls());
206 // delete
207 for (LdapName dn : wc.getDeletedUsers().keySet()) {
208 if (!entryExists(dn))
209 throw new UserDirectoryException("User to delete no found " + dn);
210 }
211 // add
212 for (LdapName dn : wc.getNewUsers().keySet()) {
213 if (entryExists(dn))
214 throw new UserDirectoryException("User to create found " + dn);
215 }
216 // modify
217 for (LdapName dn : wc.getModifiedUsers().keySet()) {
218 if (!wc.getNewUsers().containsKey(dn) && !entryExists(dn))
219 throw new UserDirectoryException("User to modify not found " + dn);
220 }
221 } catch (NamingException e) {
222 throw new UserDirectoryException("Cannot prepare LDAP", e);
223 }
224 }
225
226 private boolean entryExists(LdapName dn) throws NamingException {
227 try {
228 return getLdapContext().getAttributes(dn).size() != 0;
229 } catch (NameNotFoundException e) {
230 return false;
231 }
232 }
233
234 @Override
235 protected void commit(UserDirectoryWorkingCopy wc) {
236 try {
237 // delete
238 for (LdapName dn : wc.getDeletedUsers().keySet()) {
239 getLdapContext().destroySubcontext(dn);
240 }
241 // add
242 for (LdapName dn : wc.getNewUsers().keySet()) {
243 DirectoryUser user = wc.getNewUsers().get(dn);
244 getLdapContext().createSubcontext(dn, user.getAttributes());
245 }
246 // modify
247 for (LdapName dn : wc.getModifiedUsers().keySet()) {
248 Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
249 getLdapContext().modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, modifiedAttrs);
250 }
251 } catch (NamingException e) {
252 throw new UserDirectoryException("Cannot commit LDAP", e);
253 }
254 }
255
256 @Override
257 protected void rollback(UserDirectoryWorkingCopy wc) {
258 // prepare not impacting
259 }
260
261 }