1 package org
.argeo
.osgi
.useradmin
;
3 import static org
.argeo
.util
.naming
.LdapAttrs
.objectClass
;
5 import java
.util
.ArrayList
;
6 import java
.util
.Dictionary
;
9 import javax
.naming
.AuthenticationNotSupportedException
;
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
.SearchControls
;
19 import javax
.naming
.directory
.SearchResult
;
20 import javax
.naming
.ldap
.LdapName
;
22 import org
.argeo
.util
.naming
.LdapObjs
;
23 import org
.osgi
.framework
.Filter
;
24 import org
.osgi
.service
.useradmin
.Role
;
25 import org
.osgi
.service
.useradmin
.User
;
27 /** A user admin based on a LDAP server. */
28 public class LdapUserAdmin
extends AbstractUserDirectory
{
29 private LdapConnection ldapConnection
;
31 public LdapUserAdmin(Dictionary
<String
, ?
> properties
) {
32 this(properties
, false);
35 public LdapUserAdmin(Dictionary
<String
, ?
> properties
, boolean scoped
) {
36 super(null, properties
, scoped
);
37 ldapConnection
= new LdapConnection(getUri().toString(), properties
);
40 public void destroy() {
41 ldapConnection
.destroy();
45 protected AbstractUserDirectory
scope(User user
) {
46 Dictionary
<String
, Object
> credentials
= user
.getCredentials();
47 String username
= (String
) credentials
.get(SHARED_STATE_USERNAME
);
49 username
= user
.getName();
50 Dictionary
<String
, Object
> properties
= cloneProperties();
51 properties
.put(Context
.SECURITY_PRINCIPAL
, username
.toString());
52 Object pwdCred
= credentials
.get(SHARED_STATE_PASSWORD
);
53 byte[] pwd
= (byte[]) pwdCred
;
55 char[] password
= DigestUtils
.bytesToChars(pwd
);
56 properties
.put(Context
.SECURITY_CREDENTIALS
, new String(password
));
58 properties
.put(Context
.SECURITY_AUTHENTICATION
, "GSSAPI");
60 return new LdapUserAdmin(properties
, true);
63 // protected InitialLdapContext getLdapContext() {
64 // return initialLdapContext;
68 protected Boolean
daoHasRole(LdapName dn
) {
70 return daoGetRole(dn
) != null;
71 } catch (NameNotFoundException e
) {
77 protected DirectoryUser
daoGetRole(LdapName name
) throws NameNotFoundException
{
79 Attributes attrs
= ldapConnection
.getAttributes(name
);
80 if (attrs
.size() == 0)
82 int roleType
= roleType(name
);
84 if (roleType
== Role
.GROUP
)
85 res
= newGroup(name
, attrs
);
86 else if (roleType
== Role
.USER
)
87 res
= newUser(name
, attrs
);
89 throw new IllegalArgumentException("Unsupported LDAP type for " + name
);
91 } catch (NameNotFoundException e
) {
93 } catch (NamingException e
) {
99 protected List
<DirectoryUser
> doGetRoles(LdapName searchBase
, Filter f
, boolean deep
) {
100 ArrayList
<DirectoryUser
> res
= new ArrayList
<DirectoryUser
>();
102 String searchFilter
= f
!= null ? f
.toString()
103 : "(|(" + objectClass
+ "=" + getUserObjectClass() + ")(" + objectClass
+ "="
104 + getGroupObjectClass() + "))";
105 SearchControls searchControls
= new SearchControls();
106 // FIXME make one level consistent with deep
107 searchControls
.setSearchScope(deep ? SearchControls
.SUBTREE_SCOPE
: SearchControls
.ONELEVEL_SCOPE
);
109 // LdapName searchBase = getBaseDn();
110 NamingEnumeration
<SearchResult
> results
= ldapConnection
.search(searchBase
, searchFilter
, searchControls
);
112 results
: while (results
.hasMoreElements()) {
113 SearchResult searchResult
= results
.next();
114 Attributes attrs
= searchResult
.getAttributes();
115 Attribute objectClassAttr
= attrs
.get(objectClass
.name());
116 LdapName dn
= toDn(searchBase
, searchResult
);
118 if (objectClassAttr
.contains(getGroupObjectClass())
119 || objectClassAttr
.contains(getGroupObjectClass().toLowerCase()))
120 role
= newGroup(dn
, attrs
);
121 else if (objectClassAttr
.contains(getUserObjectClass())
122 || objectClassAttr
.contains(getUserObjectClass().toLowerCase()))
123 role
= newUser(dn
, attrs
);
125 // log.warn("Unsupported LDAP type for " + searchResult.getName());
131 } catch (AuthenticationNotSupportedException e
) {
132 // ignore (typically an unsupported anonymous bind)
133 // TODO better logging
135 } catch (NamingException e
) {
136 throw new IllegalStateException("Cannot get roles for filter " + f
, e
);
140 private LdapName
toDn(LdapName baseDn
, Binding binding
) throws InvalidNameException
{
141 return new LdapName(binding
.isRelative() ? binding
.getName() + "," + baseDn
: binding
.getName());
145 protected List
<LdapName
> getDirectGroups(LdapName dn
) {
146 List
<LdapName
> directGroups
= new ArrayList
<LdapName
>();
148 String searchFilter
= "(&(" + objectClass
+ "=" + getGroupObjectClass() + ")(" + getMemberAttributeId()
151 SearchControls searchControls
= new SearchControls();
152 searchControls
.setSearchScope(SearchControls
.SUBTREE_SCOPE
);
154 LdapName searchBase
= getBaseDn();
155 NamingEnumeration
<SearchResult
> results
= ldapConnection
.search(searchBase
, searchFilter
, searchControls
);
157 while (results
.hasMoreElements()) {
158 SearchResult searchResult
= (SearchResult
) results
.nextElement();
159 directGroups
.add(toDn(searchBase
, searchResult
));
162 } catch (NamingException e
) {
163 throw new IllegalStateException("Cannot populate direct members of " + dn
, e
);
168 protected void prepare(UserDirectoryWorkingCopy wc
) {
170 ldapConnection
.prepareChanges(wc
);
171 } catch (NamingException e
) {
172 throw new IllegalStateException("Cannot prepare LDAP", e
);
177 protected void commit(UserDirectoryWorkingCopy wc
) {
179 ldapConnection
.commitChanges(wc
);
180 } catch (NamingException e
) {
181 throw new IllegalStateException("Cannot commit LDAP", e
);
186 protected void rollback(UserDirectoryWorkingCopy wc
) {
187 // prepare not impacting
195 protected Iterable
<HierarchyUnit
> doGetDirectHierarchyUnits(LdapName searchBase
, boolean functionalOnly
) {
196 List
<HierarchyUnit
> res
= new ArrayList
<>();
198 String searchFilter
= "(|(" + objectClass
+ "=" + LdapObjs
.organizationalUnit
.name() + ")(" + objectClass
199 + "=" + LdapObjs
.organization
.name() + "))";
201 SearchControls searchControls
= new SearchControls();
202 searchControls
.setSearchScope(SearchControls
.ONELEVEL_SCOPE
);
204 NamingEnumeration
<SearchResult
> results
= ldapConnection
.search(searchBase
, searchFilter
, searchControls
);
206 while (results
.hasMoreElements()) {
207 SearchResult searchResult
= (SearchResult
) results
.nextElement();
208 LdapName dn
= toDn(searchBase
, searchResult
);
209 Attributes attrs
= searchResult
.getAttributes();
210 LdifHierarchyUnit hierarchyUnit
= new LdifHierarchyUnit(this, dn
, attrs
);
211 if (hierarchyUnit
.isFunctional())
212 res
.add(hierarchyUnit
);
215 } catch (NamingException e
) {
216 throw new IllegalStateException("Cannot get direct hierarchy units ", e
);
221 protected HierarchyUnit
doGetHierarchyUnit(LdapName dn
) {
223 Attributes attrs
= ldapConnection
.getAttributes(dn
);
224 return new LdifHierarchyUnit(this, dn
, attrs
);
225 } catch (NamingException e
) {
226 throw new IllegalStateException("Cannot get hierarchy unit " + dn
, e
);