1 package org
.argeo
.osgi
.useradmin
;
3 import static org
.argeo
.osgi
.useradmin
.LdifName
.objectClass
;
5 import java
.util
.ArrayList
;
6 import java
.util
.Dictionary
;
7 import java
.util
.Hashtable
;
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
;
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
;
29 * A user admin based on a LDAP server. Requires a {@link TransactionManager}
30 * and an open transaction for write access.
32 public class LdapUserAdmin
extends AbstractUserDirectory
{
33 private final static Log log
= LogFactory
.getLog(LdapUserAdmin
.class);
35 private InitialLdapContext initialLdapContext
= null;
37 public LdapUserAdmin(Dictionary
<String
, ?
> properties
) {
40 Hashtable
<String
, Object
> connEnv
= new Hashtable
<String
, Object
>();
41 connEnv
.put(Context
.INITIAL_CONTEXT_FACTORY
,
42 "com.sun.jndi.ldap.LdapCtxFactory");
43 connEnv
.put(Context
.PROVIDER_URL
, getUri().toString());
44 connEnv
.put("java.naming.ldap.attributes.binary",
45 LdifName
.userpassword
.name());
47 initialLdapContext
= new InitialLdapContext(connEnv
, null);
48 // StartTlsResponse tls = (StartTlsResponse) ctx
49 // .extendedOperation(new StartTlsRequest());
51 initialLdapContext
.addToEnvironment(
52 Context
.SECURITY_AUTHENTICATION
, "simple");
53 Object principal
= properties
.get(Context
.SECURITY_PRINCIPAL
);
54 if (principal
!= null) {
55 initialLdapContext
.addToEnvironment(Context
.SECURITY_PRINCIPAL
,
56 principal
.toString());
57 Object creds
= properties
.get(Context
.SECURITY_CREDENTIALS
);
59 initialLdapContext
.addToEnvironment(
60 Context
.SECURITY_CREDENTIALS
, creds
.toString());
64 // initialLdapContext.addToEnvironment(Context.SECURITY_PRINCIPAL,
65 // "uid=admin,ou=system");
66 // initialLdapContext.addToEnvironment(Context.SECURITY_CREDENTIALS,
68 } catch (Exception e
) {
69 throw new UserDirectoryException("Cannot connect to LDAP", e
);
73 public void destroy() {
76 initialLdapContext
.close();
77 } catch (NamingException e
) {
78 log
.error("Cannot destroy LDAP user admin", e
);
82 protected InitialLdapContext
getLdapContext() {
83 return initialLdapContext
;
87 protected Boolean
daoHasRole(LdapName dn
) {
88 return daoGetRole(dn
) != null;
92 protected DirectoryUser
daoGetRole(LdapName name
) {
94 Attributes attrs
= getLdapContext().getAttributes(name
);
95 if (attrs
.size() == 0)
98 if (attrs
.get(objectClass
.name()).contains(getGroupObjectClass()))
99 res
= new LdifGroup(this, name
, attrs
);
100 else if (attrs
.get(objectClass
.name()).contains(
101 getUserObjectClass()))
102 res
= new LdifUser(this, name
, attrs
);
104 throw new UserDirectoryException("Unsupported LDAP type for "
107 } catch (NamingException e
) {
108 throw new UserDirectoryException("Cannot get role for " + name
, e
);
113 protected List
<DirectoryUser
> doGetRoles(Filter f
) {
114 // TODO Auto-generated method stub
116 String searchFilter
= f
!= null ? f
.toString() : "(|("
117 + objectClass
+ "=" + getUserObjectClass() + ")("
118 + objectClass
+ "=" + getGroupObjectClass() + "))";
119 SearchControls searchControls
= new SearchControls();
120 searchControls
.setSearchScope(SearchControls
.SUBTREE_SCOPE
);
122 String searchBase
= getBaseDn();
123 NamingEnumeration
<SearchResult
> results
= getLdapContext().search(
124 searchBase
, searchFilter
, searchControls
);
126 ArrayList
<DirectoryUser
> res
= new ArrayList
<DirectoryUser
>();
127 while (results
.hasMoreElements()) {
128 SearchResult searchResult
= results
.next();
129 Attributes attrs
= searchResult
.getAttributes();
131 if (attrs
.get(objectClass
.name()).contains(
132 getGroupObjectClass()))
133 role
= new LdifGroup(this, toDn(searchBase
, searchResult
),
135 else if (attrs
.get(objectClass
.name()).contains(
136 getUserObjectClass()))
137 role
= new LdifUser(this, toDn(searchBase
, searchResult
),
140 throw new UserDirectoryException(
141 "Unsupported LDAP type for "
142 + searchResult
.getName());
146 } catch (Exception e
) {
147 throw new UserDirectoryException(
148 "Cannot get roles for filter " + f
, e
);
152 private LdapName
toDn(String baseDn
, Binding binding
)
153 throws InvalidNameException
{
154 return new LdapName(binding
.isRelative() ? binding
.getName() + ","
155 + baseDn
: binding
.getName());
159 protected List
<LdapName
> getDirectGroups(LdapName dn
) {
160 List
<LdapName
> directGroups
= new ArrayList
<LdapName
>();
162 String searchFilter
= "(&(" + objectClass
+ "="
163 + getGroupObjectClass() + ")(" + getMemberAttributeId()
166 SearchControls searchControls
= new SearchControls();
167 searchControls
.setSearchScope(SearchControls
.SUBTREE_SCOPE
);
169 String searchBase
= getBaseDn();
170 NamingEnumeration
<SearchResult
> results
= getLdapContext().search(
171 searchBase
, searchFilter
, searchControls
);
173 while (results
.hasMoreElements()) {
174 SearchResult searchResult
= (SearchResult
) results
176 directGroups
.add(toDn(searchBase
, searchResult
));
179 } catch (Exception e
) {
180 throw new ArgeoException("Cannot populate direct members of " + dn
,
186 protected void prepare(UserDirectoryWorkingCopy wc
) {
188 getLdapContext().reconnect(getLdapContext().getConnectControls());
190 for (LdapName dn
: wc
.getDeletedUsers().keySet()) {
191 if (!entryExists(dn
))
192 throw new UserDirectoryException("User to delete no found "
196 for (LdapName dn
: wc
.getNewUsers().keySet()) {
197 if (!entryExists(dn
))
198 throw new UserDirectoryException("User to create found "
202 for (LdapName dn
: wc
.getModifiedUsers().keySet()) {
203 if (!entryExists(dn
))
204 throw new UserDirectoryException("User to modify no found "
207 } catch (NamingException e
) {
208 throw new UserDirectoryException("Cannot prepare LDAP", e
);
212 private boolean entryExists(LdapName dn
) throws NamingException
{
213 return getLdapContext().getAttributes(dn
).size() != 0;
217 protected void commit(UserDirectoryWorkingCopy wc
) {
220 for (LdapName dn
: wc
.getDeletedUsers().keySet()) {
221 getLdapContext().destroySubcontext(dn
);
224 for (LdapName dn
: wc
.getNewUsers().keySet()) {
225 DirectoryUser user
= wc
.getNewUsers().get(dn
);
226 getLdapContext().createSubcontext(dn
, user
.getAttributes());
229 for (LdapName dn
: wc
.getModifiedUsers().keySet()) {
230 Attributes modifiedAttrs
= wc
.getModifiedUsers().get(dn
);
231 getLdapContext().modifyAttributes(dn
,
232 DirContext
.REPLACE_ATTRIBUTE
, modifiedAttrs
);
234 } catch (NamingException e
) {
235 throw new UserDirectoryException("Cannot commit LDAP", e
);
240 protected void rollback(UserDirectoryWorkingCopy wc
) {
241 // prepare not impacting