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