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