]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.security.core/src/org/argeo/osgi/useradmin/LdapUserAdmin.java
Factorize indexed properties, thus fixing issue when creating a new user.
[lgpl/argeo-commons.git] / org.argeo.security.core / src / org / argeo / osgi / useradmin / LdapUserAdmin.java
1 package org.argeo.osgi.useradmin;
2
3 import static org.argeo.osgi.useradmin.LdifName.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.NamingEnumeration;
14 import javax.naming.NamingException;
15 import javax.naming.directory.Attributes;
16 import javax.naming.directory.DirContext;
17 import javax.naming.directory.SearchControls;
18 import javax.naming.directory.SearchResult;
19 import javax.naming.ldap.InitialLdapContext;
20 import javax.naming.ldap.LdapName;
21 import javax.transaction.TransactionManager;
22
23 import org.apache.commons.logging.Log;
24 import org.apache.commons.logging.LogFactory;
25 import org.argeo.ArgeoException;
26 import org.osgi.framework.Filter;
27 import org.osgi.service.useradmin.User;
28
29 /**
30 * A user admin based on a LDAP server. Requires a {@link TransactionManager}
31 * and an open transaction for write access.
32 */
33 public class LdapUserAdmin extends AbstractUserDirectory {
34 private final static Log log = LogFactory.getLog(LdapUserAdmin.class);
35
36 private InitialLdapContext initialLdapContext = null;
37
38 public LdapUserAdmin(Dictionary<String, ?> properties) {
39 super(properties);
40 try {
41 Hashtable<String, Object> connEnv = new Hashtable<String, Object>();
42 connEnv.put(Context.INITIAL_CONTEXT_FACTORY,
43 "com.sun.jndi.ldap.LdapCtxFactory");
44 connEnv.put(Context.PROVIDER_URL, getUri().toString());
45 connEnv.put("java.naming.ldap.attributes.binary",
46 LdifName.userpassword.name());
47
48 initialLdapContext = new InitialLdapContext(connEnv, null);
49 // StartTlsResponse tls = (StartTlsResponse) ctx
50 // .extendedOperation(new StartTlsRequest());
51 // tls.negotiate();
52 initialLdapContext.addToEnvironment(
53 Context.SECURITY_AUTHENTICATION, "simple");
54 Object principal = properties.get(Context.SECURITY_PRINCIPAL);
55 if (principal != null) {
56 initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL,
57 principal.toString());
58 Object creds = properties.get(Context.SECURITY_CREDENTIALS);
59 if (creds != null) {
60 initialLdapContext.addToEnvironment(
61 Context.SECURITY_CREDENTIALS, creds.toString());
62
63 }
64 }
65 // initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL,
66 // "uid=admin,ou=system");
67 // initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS,
68 // "secret");
69 } catch (Exception e) {
70 throw new UserDirectoryException("Cannot connect to LDAP", e);
71 }
72 }
73
74 public void destroy() {
75 try {
76 // tls.close();
77 initialLdapContext.close();
78 } catch (NamingException e) {
79 log.error("Cannot destroy LDAP user admin", e);
80 }
81 }
82
83 protected InitialLdapContext getLdapContext() {
84 return initialLdapContext;
85 }
86
87 @Override
88 protected Boolean daoHasRole(LdapName dn) {
89 return daoGetRole(dn) != null;
90 }
91
92 @Override
93 protected DirectoryUser daoGetRole(LdapName name) {
94 try {
95 Attributes attrs = getLdapContext().getAttributes(name);
96 if (attrs.size() == 0)
97 return null;
98 LdifUser res;
99 if (attrs.get(objectClass.name()).contains(getGroupObjectClass()))
100 res = new LdifGroup(this, name, attrs);
101 else if (attrs.get(objectClass.name()).contains(
102 getUserObjectClass()))
103 res = new LdifUser(this, name, attrs);
104 else
105 throw new UserDirectoryException("Unsupported LDAP type for "
106 + name);
107 return res;
108 } catch (NamingException e) {
109 throw new UserDirectoryException("Cannot get role for " + name, e);
110 }
111 }
112
113 @Override
114 protected List<DirectoryUser> doGetRoles(Filter f) {
115 // TODO Auto-generated method stub
116 try {
117 String searchFilter = f != null ? f.toString() : "(|("
118 + objectClass + "=" + getUserObjectClass() + ")("
119 + objectClass + "=" + getGroupObjectClass() + "))";
120 SearchControls searchControls = new SearchControls();
121 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
122
123 String searchBase = getBaseDn();
124 NamingEnumeration<SearchResult> results = getLdapContext().search(
125 searchBase, searchFilter, searchControls);
126
127 ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
128 while (results.hasMoreElements()) {
129 SearchResult searchResult = results.next();
130 Attributes attrs = searchResult.getAttributes();
131 LdifUser role;
132 if (attrs.get(objectClass.name()).contains(
133 getGroupObjectClass()))
134 role = new LdifGroup(this, toDn(searchBase, searchResult),
135 attrs);
136 else if (attrs.get(objectClass.name()).contains(
137 getUserObjectClass()))
138 role = new LdifUser(this, toDn(searchBase, searchResult),
139 attrs);
140 else
141 throw new UserDirectoryException(
142 "Unsupported LDAP type for "
143 + searchResult.getName());
144 res.add(role);
145 }
146 return res;
147 } catch (Exception e) {
148 throw new UserDirectoryException(
149 "Cannot get roles for filter " + f, e);
150 }
151 }
152
153 private LdapName toDn(String baseDn, Binding binding)
154 throws InvalidNameException {
155 return new LdapName(binding.isRelative() ? binding.getName() + ","
156 + baseDn : binding.getName());
157 }
158
159 @Override
160 protected List<DirectoryGroup> getDirectGroups(User user) {
161 List<DirectoryGroup> directGroups = new ArrayList<DirectoryGroup>();
162 try {
163 String searchFilter = "(&(" + objectClass + "="
164 + getGroupObjectClass() + ")(" + getMemberAttributeId()
165 + "=" + user.getName() + "))";
166
167 SearchControls searchControls = new SearchControls();
168 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
169
170 String searchBase = getBaseDn();
171 NamingEnumeration<SearchResult> results = getLdapContext().search(
172 searchBase, searchFilter, searchControls);
173
174 while (results.hasMoreElements()) {
175 SearchResult searchResult = (SearchResult) results
176 .nextElement();
177 LdifGroup group = new LdifGroup(this, toDn(searchBase,
178 searchResult), searchResult.getAttributes());
179 directGroups.add(group);
180 }
181 return directGroups;
182 } catch (Exception e) {
183 throw new ArgeoException("Cannot populate direct members of "
184 + user, e);
185 }
186 }
187
188 @Override
189 protected void prepare(WorkingCopy wc) {
190 try {
191 getLdapContext().reconnect(getLdapContext().getConnectControls());
192 // delete
193 for (LdapName dn : wc.getDeletedUsers().keySet()) {
194 if (!entryExists(dn))
195 throw new UserDirectoryException("User to delete no found "
196 + dn);
197 }
198 // add
199 for (LdapName dn : wc.getNewUsers().keySet()) {
200 if (!entryExists(dn))
201 throw new UserDirectoryException("User to create found "
202 + dn);
203 }
204 // modify
205 for (LdapName dn : wc.getModifiedUsers().keySet()) {
206 if (!entryExists(dn))
207 throw new UserDirectoryException("User to modify no found "
208 + dn);
209 }
210 } catch (NamingException e) {
211 throw new UserDirectoryException("Cannot prepare LDAP", e);
212 }
213 }
214
215 private boolean entryExists(LdapName dn) throws NamingException {
216 return getLdapContext().getAttributes(dn).size() != 0;
217 }
218
219 @Override
220 protected void commit(WorkingCopy wc) {
221 try {
222 // delete
223 for (LdapName dn : wc.getDeletedUsers().keySet()) {
224 getLdapContext().destroySubcontext(dn);
225 }
226 // add
227 for (LdapName dn : wc.getNewUsers().keySet()) {
228 DirectoryUser user = wc.getNewUsers().get(dn);
229 getLdapContext().createSubcontext(dn, user.getAttributes());
230 }
231 // modify
232 for (LdapName dn : wc.getModifiedUsers().keySet()) {
233 Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
234 getLdapContext().modifyAttributes(dn,
235 DirContext.REPLACE_ATTRIBUTE, modifiedAttrs);
236 }
237 } catch (NamingException e) {
238 throw new UserDirectoryException("Cannot commit LDAP", e);
239 }
240 }
241
242 @Override
243 protected void rollback(WorkingCopy wc) {
244 // prepare not impacting
245 }
246
247 }