]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.enterprise/src/org/argeo/osgi/useradmin/LdapUserAdmin.java
[maven-release-plugin] prepare for next development iteration
[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.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.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.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, "com.sun.jndi.ldap.LdapCtxFactory");
43 connEnv.put(Context.PROVIDER_URL, getUri().toString());
44 connEnv.put("java.naming.ldap.attributes.binary", LdifName.userPassword.name());
45
46 initialLdapContext = new InitialLdapContext(connEnv, null);
47 // StartTlsResponse tls = (StartTlsResponse) ctx
48 // .extendedOperation(new StartTlsRequest());
49 // tls.negotiate();
50 initialLdapContext.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
51 Object principal = properties.get(Context.SECURITY_PRINCIPAL);
52 if (principal != null) {
53 initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL, principal.toString());
54 Object creds = properties.get(Context.SECURITY_CREDENTIALS);
55 if (creds != null) {
56 initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS, creds.toString());
57
58 }
59 }
60 // initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL,
61 // "uid=admin,ou=system");
62 // initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS,
63 // "secret");
64 } catch (Exception e) {
65 throw new UserDirectoryException("Cannot connect to LDAP", e);
66 }
67 }
68
69 public void destroy() {
70 try {
71 // tls.close();
72 initialLdapContext.close();
73 } catch (NamingException e) {
74 log.error("Cannot destroy LDAP user admin", e);
75 }
76 }
77
78 protected InitialLdapContext getLdapContext() {
79 return initialLdapContext;
80 }
81
82 @Override
83 protected Boolean daoHasRole(LdapName dn) {
84 return daoGetRole(dn) != null;
85 }
86
87 @Override
88 protected DirectoryUser daoGetRole(LdapName name) {
89 try {
90 Attributes attrs = getLdapContext().getAttributes(name);
91 if (attrs.size() == 0)
92 return null;
93 LdifUser res;
94 if (attrs.get(objectClass.name()).contains(getGroupObjectClass()))
95 res = new LdifGroup(this, name, attrs);
96 else if (attrs.get(objectClass.name()).contains(getUserObjectClass()))
97 res = new LdifUser(this, name, attrs);
98 else
99 throw new UserDirectoryException("Unsupported LDAP type for " + name);
100 return res;
101 } catch (NamingException e) {
102 return null;
103 }
104 }
105
106 @Override
107 protected List<DirectoryUser> doGetRoles(Filter f) {
108 try {
109 String searchFilter = f != null ? f.toString()
110 : "(|(" + objectClass + "=" + getUserObjectClass() + ")(" + objectClass + "="
111 + getGroupObjectClass() + "))";
112 SearchControls searchControls = new SearchControls();
113 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
114
115 LdapName searchBase = getBaseDn();
116 NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
117
118 ArrayList<DirectoryUser> res = new ArrayList<DirectoryUser>();
119 results: while (results.hasMoreElements()) {
120 SearchResult searchResult = results.next();
121 Attributes attrs = searchResult.getAttributes();
122 Attribute objectClassAttr = attrs.get(objectClass.name());
123 LdapName dn = toDn(searchBase, searchResult);
124 LdifUser role;
125 if (objectClassAttr.contains(getGroupObjectClass()))
126 role = new LdifGroup(this, dn, attrs);
127 else if (objectClassAttr.contains(getUserObjectClass()))
128 role = new LdifUser(this, dn, attrs);
129 else {
130 log.warn("Unsupported LDAP type for " + searchResult.getName());
131 continue results;
132 }
133 res.add(role);
134 }
135 return res;
136 } catch (Exception e) {
137 throw new UserDirectoryException("Cannot get roles for filter " + f, e);
138 }
139 }
140
141 private LdapName toDn(LdapName baseDn, Binding binding) throws InvalidNameException {
142 return new LdapName(binding.isRelative() ? binding.getName() + "," + baseDn : binding.getName());
143 }
144
145 @Override
146 protected List<LdapName> getDirectGroups(LdapName dn) {
147 List<LdapName> directGroups = new ArrayList<LdapName>();
148 try {
149 String searchFilter = "(&(" + objectClass + "=" + getGroupObjectClass() + ")(" + getMemberAttributeId()
150 + "=" + dn + "))";
151
152 SearchControls searchControls = new SearchControls();
153 searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
154
155 LdapName searchBase = getBaseDn();
156 NamingEnumeration<SearchResult> results = getLdapContext().search(searchBase, searchFilter, searchControls);
157
158 while (results.hasMoreElements()) {
159 SearchResult searchResult = (SearchResult) results.nextElement();
160 directGroups.add(toDn(searchBase, searchResult));
161 }
162 return directGroups;
163 } catch (Exception e) {
164 throw new UserDirectoryException("Cannot populate direct members of " + dn, e);
165 }
166 }
167
168 @Override
169 protected void prepare(UserDirectoryWorkingCopy wc) {
170 try {
171 getLdapContext().reconnect(getLdapContext().getConnectControls());
172 // delete
173 for (LdapName dn : wc.getDeletedUsers().keySet()) {
174 if (!entryExists(dn))
175 throw new UserDirectoryException("User to delete no found " + dn);
176 }
177 // add
178 for (LdapName dn : wc.getNewUsers().keySet()) {
179 if (entryExists(dn))
180 throw new UserDirectoryException("User to create found " + dn);
181 }
182 // modify
183 for (LdapName dn : wc.getModifiedUsers().keySet()) {
184 if (!wc.getNewUsers().containsKey(dn) && !entryExists(dn))
185 throw new UserDirectoryException("User to modify not found " + dn);
186 }
187 } catch (NamingException e) {
188 throw new UserDirectoryException("Cannot prepare LDAP", e);
189 }
190 }
191
192 private boolean entryExists(LdapName dn) throws NamingException {
193 try {
194 return getLdapContext().getAttributes(dn).size() != 0;
195 } catch (NameNotFoundException e) {
196 return false;
197 }
198 }
199
200 @Override
201 protected void commit(UserDirectoryWorkingCopy wc) {
202 try {
203 // delete
204 for (LdapName dn : wc.getDeletedUsers().keySet()) {
205 getLdapContext().destroySubcontext(dn);
206 }
207 // add
208 for (LdapName dn : wc.getNewUsers().keySet()) {
209 DirectoryUser user = wc.getNewUsers().get(dn);
210 getLdapContext().createSubcontext(dn, user.getAttributes());
211 }
212 // modify
213 for (LdapName dn : wc.getModifiedUsers().keySet()) {
214 Attributes modifiedAttrs = wc.getModifiedUsers().get(dn);
215 getLdapContext().modifyAttributes(dn, DirContext.REPLACE_ATTRIBUTE, modifiedAttrs);
216 }
217 } catch (NamingException e) {
218 throw new UserDirectoryException("Cannot commit LDAP", e);
219 }
220 }
221
222 @Override
223 protected void rollback(UserDirectoryWorkingCopy wc) {
224 // prepare not impacting
225 }
226
227 }