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