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