1 package org
.argeo
.osgi
.useradmin
;
4 import java
.util
.ArrayList
;
5 import java
.util
.Hashtable
;
8 import javax
.naming
.Binding
;
9 import javax
.naming
.Context
;
10 import javax
.naming
.InvalidNameException
;
11 import javax
.naming
.NamingEnumeration
;
12 import javax
.naming
.NamingException
;
13 import javax
.naming
.directory
.Attributes
;
14 import javax
.naming
.directory
.DirContext
;
15 import javax
.naming
.directory
.ModificationItem
;
16 import javax
.naming
.directory
.SearchControls
;
17 import javax
.naming
.directory
.SearchResult
;
18 import javax
.naming
.ldap
.InitialLdapContext
;
19 import javax
.naming
.ldap
.LdapContext
;
20 import javax
.naming
.ldap
.LdapName
;
21 import javax
.transaction
.xa
.XAException
;
22 import javax
.transaction
.xa
.Xid
;
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 import org
.osgi
.framework
.InvalidSyntaxException
;
29 import org
.osgi
.service
.useradmin
.Authorization
;
30 import org
.osgi
.service
.useradmin
.Group
;
31 import org
.osgi
.service
.useradmin
.Role
;
32 import org
.osgi
.service
.useradmin
.User
;
34 public class LdapUserAdmin
extends AbstractUserDirectory
{
35 private final static Log log
= LogFactory
.getLog(LdapUserAdmin
.class);
37 private String baseDn
= "dc=example,dc=com";
38 private InitialLdapContext initialLdapContext
= null;
40 public LdapUserAdmin(String uri
) {
43 Hashtable
<String
, Object
> connEnv
= new Hashtable
<String
, Object
>();
44 connEnv
.put(Context
.INITIAL_CONTEXT_FACTORY
,
45 "com.sun.jndi.ldap.LdapCtxFactory");
46 connEnv
.put(Context
.PROVIDER_URL
, getUri().toString());
47 connEnv
.put("java.naming.ldap.attributes.binary", "userPassword");
48 // connEnv.put(Context.SECURITY_AUTHENTICATION, "simple");
49 // connEnv.put(Context.SECURITY_PRINCIPAL, "uid=admin,ou=system");
50 // connEnv.put(Context.SECURITY_CREDENTIALS, "secret");
52 initialLdapContext
= new InitialLdapContext(connEnv
, null);
53 // StartTlsResponse tls = (StartTlsResponse) ctx
54 // .extendedOperation(new StartTlsRequest());
56 initialLdapContext
.addToEnvironment(
57 Context
.SECURITY_AUTHENTICATION
, "simple");
58 initialLdapContext
.addToEnvironment(Context
.SECURITY_PRINCIPAL
,
59 "uid=admin,ou=system");
60 initialLdapContext
.addToEnvironment(Context
.SECURITY_CREDENTIALS
,
62 LdapContext ldapContext
= (LdapContext
) initialLdapContext
63 .lookup("uid=root,ou=users,dc=example,dc=com");
64 log
.debug(initialLdapContext
.getAttributes(
65 "uid=root,ou=users,dc=example,dc=com").get("cn"));
66 } catch (Exception e
) {
67 throw new UserDirectoryException("Cannot connect to LDAP", e
);
71 public void destroy() {
74 initialLdapContext
.close();
75 } catch (NamingException e
) {
76 log
.error("Cannot destroy LDAP user admin", e
);
81 protected Boolean
daoHasRole(LdapName dn
) {
82 return daoGetRole(dn
) != null;
86 protected DirectoryUser
daoGetRole(LdapName name
) {
88 Attributes attrs
= initialLdapContext
.getAttributes(name
);
89 if (attrs
.size() == 0)
92 if (attrs
.get("objectClass").contains("groupOfNames"))
93 res
= new LdifGroup(this, name
, attrs
);
94 else if (attrs
.get("objectClass").contains("inetOrgPerson"))
95 res
= new LdifUser(this, name
, attrs
);
97 throw new UserDirectoryException("Unsupported LDAP type for "
100 } catch (NamingException e
) {
101 throw new UserDirectoryException("Cannot get role for " + name
, e
);
106 protected List
<DirectoryUser
> doGetRoles(Filter f
) {
107 // TODO Auto-generated method stub
109 String searchFilter
= f
!= null ? f
.toString()
110 : "(|(objectClass=inetOrgPerson)(objectClass=groupOfNames))";
111 SearchControls searchControls
= new SearchControls();
112 searchControls
.setSearchScope(SearchControls
.SUBTREE_SCOPE
);
114 String searchBase
= baseDn
;
115 NamingEnumeration
<SearchResult
> results
= initialLdapContext
116 .search(searchBase
, searchFilter
, searchControls
);
118 ArrayList
<DirectoryUser
> res
= new ArrayList
<DirectoryUser
>();
119 while (results
.hasMoreElements()) {
120 SearchResult searchResult
= results
.next();
121 Attributes attrs
= searchResult
.getAttributes();
123 if (attrs
.get("objectClass").contains("groupOfNames"))
124 role
= new LdifGroup(this, toDn(searchBase
, searchResult
),
126 else if (attrs
.get("objectClass").contains("inetOrgPerson"))
127 role
= new LdifUser(this, toDn(searchBase
, searchResult
),
130 throw new UserDirectoryException(
131 "Unsupported LDAP type for "
132 + searchResult
.getName());
136 } catch (Exception e
) {
137 throw new UserDirectoryException(
138 "Cannot get roles for filter " + f
, e
);
143 protected void doGetUser(String key
, String value
,
144 List
<DirectoryUser
> collectedUsers
) {
146 String searchFilter
= "(&(objectClass=inetOrgPerson)(" + key
+ "="
149 SearchControls searchControls
= new SearchControls();
150 searchControls
.setSearchScope(SearchControls
.SUBTREE_SCOPE
);
152 String searchBase
= baseDn
;
153 NamingEnumeration
<SearchResult
> results
= initialLdapContext
154 .search(searchBase
, searchFilter
, searchControls
);
156 SearchResult searchResult
= null;
157 if (results
.hasMoreElements()) {
158 searchResult
= (SearchResult
) results
.nextElement();
159 if (results
.hasMoreElements())
162 if (searchResult
!= null)
163 collectedUsers
.add(new LdifUser(this, toDn(searchBase
,
164 searchResult
), searchResult
.getAttributes()));
165 } catch (Exception e
) {
166 throw new UserDirectoryException("Cannot get user with " + key
173 public User
getUser(String key
, String value
) {
175 List
<User
> users
= new ArrayList
<User
>();
176 for (String prop
: getIndexedUserProperties()) {
177 User user
= getUser(prop
, value
);
181 if (users
.size() == 1)
188 String searchFilter
= "(&(objectClass=inetOrgPerson)(" + key
+ "="
191 SearchControls searchControls
= new SearchControls();
192 searchControls
.setSearchScope(SearchControls
.SUBTREE_SCOPE
);
194 String searchBase
= baseDn
;
195 NamingEnumeration
<SearchResult
> results
= initialLdapContext
196 .search(searchBase
, searchFilter
, searchControls
);
198 SearchResult searchResult
= null;
199 if (results
.hasMoreElements()) {
200 searchResult
= (SearchResult
) results
.nextElement();
201 if (results
.hasMoreElements())
204 if (searchResult
== null)
206 return new LdifUser(this, toDn(searchBase
, searchResult
),
207 searchResult
.getAttributes());
208 } catch (Exception e
) {
209 throw new UserDirectoryException("Cannot get user with " + key
214 private LdapName
toDn(String baseDn
, Binding binding
)
215 throws InvalidNameException
{
216 return new LdapName(binding
.isRelative() ? binding
.getName() + ","
217 + baseDn
: binding
.getName());
220 // void populateDirectMemberOf(LdifUser user) {
223 // String searchFilter = "(&(objectClass=groupOfNames)(member="
224 // + user.getName() + "))";
226 // SearchControls searchControls = new SearchControls();
227 // searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
229 // String searchBase = "ou=node";
230 // NamingEnumeration<SearchResult> results = initialLdapContext
231 // .search(searchBase, searchFilter, searchControls);
234 // //user.directMemberOf.clear();
235 // while (results.hasMoreElements()) {
236 // SearchResult searchResult = (SearchResult) results
238 // LdifGroup group = new LdifGroup(toDn(searchBase, searchResult),
239 // searchResult.getAttributes());
240 // populateDirectMemberOf(group);
241 // //user.directMemberOf.add(group);
243 // } catch (Exception e) {
244 // throw new ArgeoException("Cannot populate direct members of "
250 protected List
<DirectoryGroup
> getDirectGroups(User user
) {
251 List
<DirectoryGroup
> directGroups
= new ArrayList
<DirectoryGroup
>();
253 String searchFilter
= "(&(objectClass=groupOfNames)(member="
254 + user
.getName() + "))";
256 SearchControls searchControls
= new SearchControls();
257 searchControls
.setSearchScope(SearchControls
.SUBTREE_SCOPE
);
259 String searchBase
= getGroupsSearchBase();
260 NamingEnumeration
<SearchResult
> results
= initialLdapContext
261 .search(searchBase
, searchFilter
, searchControls
);
263 while (results
.hasMoreElements()) {
264 SearchResult searchResult
= (SearchResult
) results
266 LdifGroup group
= new LdifGroup(this, toDn(searchBase
,
267 searchResult
), searchResult
.getAttributes());
268 directGroups
.add(group
);
271 } catch (Exception e
) {
272 throw new ArgeoException("Cannot populate direct members of "
277 protected String
getGroupsSearchBase() {
278 // TODO configure group search base
283 protected void prepare(WorkingCopy wc
) {
285 initialLdapContext
.reconnect(initialLdapContext
286 .getConnectControls());
288 for (LdapName dn
: wc
.getDeletedUsers().keySet()) {
289 if (!entryExists(dn
))
290 throw new UserDirectoryException("User to delete no found "
294 for (LdapName dn
: wc
.getNewUsers().keySet()) {
295 if (!entryExists(dn
))
296 throw new UserDirectoryException("User to create found "
300 for (LdapName dn
: wc
.getModifiedUsers().keySet()) {
301 if (!entryExists(dn
))
302 throw new UserDirectoryException("User to modify no found "
305 } catch (NamingException e
) {
306 throw new UserDirectoryException("Cannot prepare LDAP", e
);
310 private boolean entryExists(LdapName dn
) throws NamingException
{
311 return initialLdapContext
.getAttributes(dn
).size() != 0;
315 protected void commit(WorkingCopy wc
) {
318 for (LdapName dn
: wc
.getDeletedUsers().keySet()) {
319 initialLdapContext
.destroySubcontext(dn
);
322 for (LdapName dn
: wc
.getNewUsers().keySet()) {
323 DirectoryUser user
= wc
.getNewUsers().get(dn
);
324 initialLdapContext
.createSubcontext(dn
, user
.getAttributes());
327 for (LdapName dn
: wc
.getModifiedUsers().keySet()) {
328 Attributes modifiedAttrs
= wc
.getModifiedUsers().get(dn
);
329 initialLdapContext
.modifyAttributes(dn
,
330 DirContext
.REPLACE_ATTRIBUTE
, modifiedAttrs
);
332 } catch (NamingException e
) {
333 throw new UserDirectoryException("Cannot commit LDAP", e
);
338 protected void rollback(WorkingCopy wc
) {
339 // TODO Auto-generated method stub