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