1 package org
.argeo
.util
.directory
.ldap
;
3 import static org
.argeo
.util
.naming
.LdapAttrs
.objectClass
;
5 import java
.util
.ArrayList
;
8 import javax
.naming
.AuthenticationNotSupportedException
;
9 import javax
.naming
.Binding
;
10 import javax
.naming
.InvalidNameException
;
11 import javax
.naming
.NameNotFoundException
;
12 import javax
.naming
.NamingEnumeration
;
13 import javax
.naming
.NamingException
;
14 import javax
.naming
.directory
.Attribute
;
15 import javax
.naming
.directory
.Attributes
;
16 import javax
.naming
.directory
.BasicAttributes
;
17 import javax
.naming
.directory
.SearchControls
;
18 import javax
.naming
.directory
.SearchResult
;
19 import javax
.naming
.ldap
.LdapName
;
20 import javax
.naming
.ldap
.Rdn
;
22 import org
.argeo
.util
.directory
.HierarchyUnit
;
23 import org
.argeo
.util
.naming
.LdapAttrs
;
24 import org
.argeo
.util
.naming
.LdapObjs
;
26 /** A user admin based on a LDAP server. */
27 public class LdapDao
extends AbstractLdapDirectoryDao
{
28 private LdapConnection ldapConnection
;
30 // public LdapUserAdmin(Dictionary<String, ?> properties) {
31 // this(properties, false);
34 public LdapDao(AbstractLdapDirectory directory
) {
40 ldapConnection
= new LdapConnection(getDirectory().getUri().toString(), getDirectory().cloneConfigProperties());
43 public void destroy() {
44 ldapConnection
.destroy();
48 // protected AbstractUserDirectory scope(User user) {
49 // Dictionary<String, Object> credentials = user.getCredentials();
50 // String username = (String) credentials.get(SHARED_STATE_USERNAME);
51 // if (username == null)
52 // username = user.getName();
53 // Dictionary<String, Object> properties = cloneProperties();
54 // properties.put(Context.SECURITY_PRINCIPAL, username.toString());
55 // Object pwdCred = credentials.get(SHARED_STATE_PASSWORD);
56 // byte[] pwd = (byte[]) pwdCred;
58 // char[] password = DirectoryDigestUtils.bytesToChars(pwd);
59 // properties.put(Context.SECURITY_CREDENTIALS, new String(password));
61 // properties.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
63 // return new LdapUserAdmin(properties, true);
66 // protected InitialLdapContext getLdapContext() {
67 // return initialLdapContext;
71 public Boolean
entryExists(LdapName dn
) {
73 return ldapConnection
.entryExists(dn
);
74 } catch (NameNotFoundException e
) {
76 } catch (NamingException e
) {
77 throw new IllegalStateException("Cannot check " + dn
, e
);
82 public LdapEntry
doGetEntry(LdapName name
) throws NameNotFoundException
{
83 // if (!entryExists(name))
84 // throw new NameNotFoundException(name + " was not found in " + getDirectory().getBaseDn());
86 Attributes attrs
= ldapConnection
.getAttributes(name
);
89 Rdn technicalRdn
= LdapNameUtils
.getParentRdn(name
);
90 if (getDirectory().getGroupBaseRdn().equals(technicalRdn
)) {
91 if (attrs
.size() == 0) {// exists but not accessible
92 attrs
= new BasicAttributes();
93 attrs
.put(LdapAttrs
.objectClass
.name(), LdapObjs
.top
.name());
94 attrs
.put(LdapAttrs
.objectClass
.name(), getDirectory().getGroupObjectClass());
96 res
= newGroup(name
, attrs
);
97 } else if (getDirectory().getSystemRoleBaseRdn().equals(technicalRdn
)) {
98 if (attrs
.size() == 0) {// exists but not accessible
99 attrs
= new BasicAttributes();
100 attrs
.put(LdapAttrs
.objectClass
.name(), LdapObjs
.top
.name());
101 attrs
.put(LdapAttrs
.objectClass
.name(), getDirectory().getGroupObjectClass());
103 res
= newGroup(name
, attrs
);
104 } else if (getDirectory().getUserBaseRdn().equals(technicalRdn
)) {
105 if (attrs
.size() == 0) {// exists but not accessible
106 attrs
= new BasicAttributes();
107 attrs
.put(LdapAttrs
.objectClass
.name(), LdapObjs
.top
.name());
108 attrs
.put(LdapAttrs
.objectClass
.name(), getDirectory().getUserObjectClass());
110 res
= newUser(name
, attrs
);
112 res
= new DefaultLdapEntry(getDirectory(), name
, attrs
);
115 } catch (NameNotFoundException e
) {
117 } catch (NamingException e
) {
118 throw new IllegalStateException("Cannot retrieve entry " + name
, e
);
122 // protected boolean isGroup(LdapName dn) {
123 // Rdn technicalRdn = LdapNameUtils.getParentRdn(dn);
124 // if (getDirectory().getGroupBaseRdn().equals(technicalRdn)
125 // || getDirectory().getSystemRoleBaseRdn().equals(technicalRdn))
127 // else if (getDirectory().getUserBaseRdn().equals(technicalRdn))
130 // throw new IllegalArgumentException(
131 // "Cannot find role type, " + technicalRdn + " is not a technical RDN for " + dn);
135 public Attributes
doGetAttributes(LdapName name
) {
137 Attributes attrs
= ldapConnection
.getAttributes(name
);
139 } catch (NamingException e
) {
140 throw new IllegalStateException("Cannot get attributes for " + name
);
145 public List
<LdapEntry
> doGetEntries(LdapName searchBase
, String f
, boolean deep
) {
146 ArrayList
<LdapEntry
> res
= new ArrayList
<>();
148 String searchFilter
= f
!= null ? f
.toString()
149 : "(|(" + objectClass
+ "=" + getDirectory().getUserObjectClass() + ")(" + objectClass
+ "="
150 + getDirectory().getGroupObjectClass() + "))";
151 SearchControls searchControls
= new SearchControls();
152 // FIXME make one level consistent with deep
153 searchControls
.setSearchScope(deep ? SearchControls
.SUBTREE_SCOPE
: SearchControls
.ONELEVEL_SCOPE
);
155 // LdapName searchBase = getBaseDn();
156 NamingEnumeration
<SearchResult
> results
= ldapConnection
.search(searchBase
, searchFilter
, searchControls
);
158 results
: while (results
.hasMoreElements()) {
159 SearchResult searchResult
= results
.next();
160 Attributes attrs
= searchResult
.getAttributes();
161 Attribute objectClassAttr
= attrs
.get(objectClass
.name());
162 LdapName dn
= toDn(searchBase
, searchResult
);
164 if (objectClassAttr
.contains(getDirectory().getGroupObjectClass())
165 || objectClassAttr
.contains(getDirectory().getGroupObjectClass().toLowerCase()))
166 role
= newGroup(dn
, attrs
);
167 else if (objectClassAttr
.contains(getDirectory().getUserObjectClass())
168 || objectClassAttr
.contains(getDirectory().getUserObjectClass().toLowerCase()))
169 role
= newUser(dn
, attrs
);
171 // log.warn("Unsupported LDAP type for " + searchResult.getName());
177 } catch (AuthenticationNotSupportedException e
) {
178 // ignore (typically an unsupported anonymous bind)
179 // TODO better logging
181 } catch (NamingException e
) {
182 throw new IllegalStateException("Cannot get roles for filter " + f
, e
);
186 private LdapName
toDn(LdapName baseDn
, Binding binding
) throws InvalidNameException
{
187 return new LdapName(binding
.isRelative() ? binding
.getName() + "," + baseDn
: binding
.getName());
191 public List
<LdapName
> getDirectGroups(LdapName dn
) {
192 List
<LdapName
> directGroups
= new ArrayList
<LdapName
>();
194 String searchFilter
= "(&(" + objectClass
+ "=" + getDirectory().getGroupObjectClass() + ")("
195 + getDirectory().getMemberAttributeId() + "=" + dn
+ "))";
197 SearchControls searchControls
= new SearchControls();
198 searchControls
.setSearchScope(SearchControls
.SUBTREE_SCOPE
);
200 LdapName searchBase
= getDirectory().getBaseDn();
201 NamingEnumeration
<SearchResult
> results
= ldapConnection
.search(searchBase
, searchFilter
, searchControls
);
203 while (results
.hasMoreElements()) {
204 SearchResult searchResult
= (SearchResult
) results
.nextElement();
205 directGroups
.add(toDn(searchBase
, searchResult
));
208 } catch (NamingException e
) {
209 throw new IllegalStateException("Cannot populate direct members of " + dn
, e
);
214 public void prepare(LdapEntryWorkingCopy wc
) {
216 ldapConnection
.prepareChanges(wc
);
217 } catch (NamingException e
) {
218 throw new IllegalStateException("Cannot prepare LDAP", e
);
223 public void commit(LdapEntryWorkingCopy wc
) {
225 ldapConnection
.commitChanges(wc
);
226 } catch (NamingException e
) {
227 throw new IllegalStateException("Cannot commit LDAP", e
);
232 public void rollback(LdapEntryWorkingCopy wc
) {
233 // prepare not impacting
241 public Iterable
<HierarchyUnit
> doGetDirectHierarchyUnits(LdapName searchBase
, boolean functionalOnly
) {
242 List
<HierarchyUnit
> res
= new ArrayList
<>();
244 String searchFilter
= "(|(" + objectClass
+ "=" + LdapObjs
.organizationalUnit
.name() + ")(" + objectClass
245 + "=" + LdapObjs
.organization
.name() + "))";
246 // String searchFilter = "(|(" + objectClass + "=" + LdapObjs.organizationalUnit.name() + ")(" + objectClass
247 // + "=" + LdapObjs.organization.name() + ")(cn=accounts)(cn=users)(cn=groups))";
249 SearchControls searchControls
= new SearchControls();
250 searchControls
.setSearchScope(SearchControls
.ONELEVEL_SCOPE
);
252 NamingEnumeration
<SearchResult
> results
= ldapConnection
.search(searchBase
, searchFilter
, searchControls
);
254 while (results
.hasMoreElements()) {
255 SearchResult searchResult
= (SearchResult
) results
.nextElement();
256 LdapName dn
= toDn(searchBase
, searchResult
);
257 Attributes attrs
= searchResult
.getAttributes();
258 LdapHierarchyUnit hierarchyUnit
= new LdapHierarchyUnit(getDirectory(), dn
, attrs
);
259 if (functionalOnly
) {
260 if (hierarchyUnit
.isFunctional())
261 res
.add(hierarchyUnit
);
263 res
.add(hierarchyUnit
);
267 } catch (NamingException e
) {
268 throw new IllegalStateException("Cannot get direct hierarchy units ", e
);
273 public HierarchyUnit
doGetHierarchyUnit(LdapName dn
) {
275 if (getDirectory().getBaseDn().equals(dn
))
276 return getDirectory();
277 if (!dn
.startsWith(getDirectory().getBaseDn()))
278 throw new IllegalArgumentException(dn
+ " does not start with base DN " + getDirectory().getBaseDn());
279 Attributes attrs
= ldapConnection
.getAttributes(dn
);
280 return new LdapHierarchyUnit(getDirectory(), dn
, attrs
);
281 } catch (NamingException e
) {
282 throw new IllegalStateException("Cannot get hierarchy unit " + dn
, e
);