1 package org
.argeo
.osgi
.useradmin
;
3 import static org
.argeo
.util
.naming
.LdapAttrs
.objectClass
;
4 import static org
.argeo
.util
.naming
.LdapObjs
.extensibleObject
;
5 import static org
.argeo
.util
.naming
.LdapObjs
.inetOrgPerson
;
6 import static org
.argeo
.util
.naming
.LdapObjs
.organizationalPerson
;
7 import static org
.argeo
.util
.naming
.LdapObjs
.person
;
8 import static org
.argeo
.util
.naming
.LdapObjs
.top
;
11 import java
.nio
.channels
.UnsupportedAddressTypeException
;
12 import java
.util
.ArrayList
;
13 import java
.util
.Dictionary
;
14 import java
.util
.Iterator
;
15 import java
.util
.List
;
17 import javax
.naming
.Context
;
18 import javax
.naming
.InvalidNameException
;
19 import javax
.naming
.directory
.Attributes
;
20 import javax
.naming
.directory
.BasicAttribute
;
21 import javax
.naming
.directory
.BasicAttributes
;
22 import javax
.naming
.ldap
.LdapName
;
23 import javax
.naming
.ldap
.Rdn
;
25 import org
.argeo
.util
.directory
.DirectoryConf
;
26 import org
.argeo
.util
.directory
.DirectoryDigestUtils
;
27 import org
.argeo
.util
.directory
.HierarchyUnit
;
28 import org
.argeo
.util
.directory
.ldap
.AbstractLdapDirectory
;
29 import org
.argeo
.util
.directory
.ldap
.LdapDao
;
30 import org
.argeo
.util
.directory
.ldap
.LdapEntry
;
31 import org
.argeo
.util
.directory
.ldap
.LdapEntryWorkingCopy
;
32 import org
.argeo
.util
.directory
.ldap
.LdapNameUtils
;
33 import org
.argeo
.util
.directory
.ldap
.LdifDao
;
34 import org
.osgi
.framework
.Filter
;
35 import org
.osgi
.framework
.FrameworkUtil
;
36 import org
.osgi
.framework
.InvalidSyntaxException
;
37 import org
.osgi
.service
.useradmin
.Authorization
;
38 import org
.osgi
.service
.useradmin
.Role
;
39 import org
.osgi
.service
.useradmin
.User
;
40 import org
.osgi
.service
.useradmin
.UserAdmin
;
42 /** Base class for a {@link UserDirectory}. */
43 public class DirectoryUserAdmin
extends AbstractLdapDirectory
implements UserAdmin
, UserDirectory
{
45 private UserAdmin externalRoles
;
46 // private List<String> indexedUserProperties = Arrays
47 // .asList(new String[] { LdapAttrs.uid.name(), LdapAttrs.mail.name(),
48 // LdapAttrs.cn.name() });
51 // private TransactionManager transactionManager;
52 public DirectoryUserAdmin(URI uriArg
, Dictionary
<String
, ?
> props
) {
53 this(uriArg
, props
, false);
56 public DirectoryUserAdmin(URI uriArg
, Dictionary
<String
, ?
> props
, boolean scoped
) {
57 super(uriArg
, props
, scoped
);
60 public DirectoryUserAdmin(Dictionary
<String
, ?
> props
) {
68 protected AbstractLdapDirectory
scope(User user
) {
69 if (getDirectoryDao() instanceof LdapDao
) {
70 return scopeLdap(user
);
71 } else if (getDirectoryDao() instanceof LdifDao
) {
72 return scopeLdif(user
);
74 throw new IllegalStateException("Unsupported DAO " + getDirectoryDao().getClass());
78 protected DirectoryUserAdmin
scopeLdap(User user
) {
79 Dictionary
<String
, Object
> credentials
= user
.getCredentials();
80 String username
= (String
) credentials
.get(SHARED_STATE_USERNAME
);
82 username
= user
.getName();
83 Dictionary
<String
, Object
> properties
= cloneConfigProperties();
84 properties
.put(Context
.SECURITY_PRINCIPAL
, username
.toString());
85 Object pwdCred
= credentials
.get(SHARED_STATE_PASSWORD
);
86 byte[] pwd
= (byte[]) pwdCred
;
88 char[] password
= DirectoryDigestUtils
.bytesToChars(pwd
);
89 properties
.put(Context
.SECURITY_CREDENTIALS
, new String(password
));
91 properties
.put(Context
.SECURITY_AUTHENTICATION
, "GSSAPI");
93 DirectoryUserAdmin scopedDirectory
= new DirectoryUserAdmin(null, properties
, true);
94 scopedDirectory
.init();
95 return scopedDirectory
;
98 protected DirectoryUserAdmin
scopeLdif(User user
) {
99 Dictionary
<String
, Object
> credentials
= user
.getCredentials();
100 String username
= (String
) credentials
.get(SHARED_STATE_USERNAME
);
101 if (username
== null)
102 username
= user
.getName();
103 Object pwdCred
= credentials
.get(SHARED_STATE_PASSWORD
);
104 byte[] pwd
= (byte[]) pwdCred
;
106 char[] password
= DirectoryDigestUtils
.bytesToChars(pwd
);
107 User directoryUser
= (User
) getRole(username
);
108 if (!directoryUser
.hasCredential(null, password
))
109 throw new IllegalStateException("Invalid credentials");
111 throw new IllegalStateException("Password is required");
113 Dictionary
<String
, Object
> properties
= cloneConfigProperties();
114 properties
.put(DirectoryConf
.readOnly
.name(), "true");
115 DirectoryUserAdmin scopedUserAdmin
= new DirectoryUserAdmin(null, properties
, true);
116 // scopedUserAdmin.groups = Collections.unmodifiableNavigableMap(groups);
117 // scopedUserAdmin.users = Collections.unmodifiableNavigableMap(users);
118 // FIXME do it better
119 ((LdifDao
) getDirectoryDao()).scope((LdifDao
) scopedUserAdmin
.getDirectoryDao());
120 scopedUserAdmin
.init();
121 return scopedUserAdmin
;
125 public String
getRolePath(Role role
) {
126 return nameToRelativePath(LdapNameUtils
.toLdapName(role
.getName()));
130 public String
getRoleSimpleName(Role role
) {
131 LdapName dn
= LdapNameUtils
.toLdapName(role
.getName());
132 String name
= LdapNameUtils
.getLastRdnValue(dn
);
137 public Role
getRoleByPath(String path
) {
138 LdapEntry entry
= doGetRole(pathToName(path
));
139 if (!(entry
instanceof Role
)) {
140 throw new IllegalStateException("Path must be a UserAdmin Role.");
146 protected List
<Role
> getAllRoles(DirectoryUser user
) {
147 List
<Role
> allRoles
= new ArrayList
<Role
>();
149 collectRoles(user
, allRoles
);
152 collectAnonymousRoles(allRoles
);
156 private void collectRoles(DirectoryUser user
, List
<Role
> allRoles
) {
157 List
<LdapEntry
> allEntries
= new ArrayList
<>();
158 LdapEntry entry
= (LdapEntry
) user
;
159 collectGroups(entry
, allEntries
);
160 for (LdapEntry e
: allEntries
) {
161 if (e
instanceof Role
)
162 allRoles
.add((Role
) e
);
164 // Attributes attrs = user.getAttributes();
165 // // TODO centralize attribute name
166 // Attribute memberOf = attrs.get(LdapAttrs.memberOf.name());
167 // // if user belongs to this directory, we only check memberOf
168 // if (memberOf != null && user.getDn().startsWith(getBaseDn())) {
170 // NamingEnumeration<?> values = memberOf.getAll();
171 // while (values.hasMore()) {
172 // Object value = values.next();
173 // LdapName groupDn = new LdapName(value.toString());
174 // DirectoryUser group = doGetRole(groupDn);
175 // if (group != null)
176 // allRoles.add(group);
178 // } catch (NamingException e) {
179 // throw new IllegalStateException("Cannot get memberOf groups for " + user, e);
182 // for (LdapName groupDn : getDirectoryDao().getDirectGroups(user.getDn())) {
183 // // TODO check for loops
184 // DirectoryUser group = doGetRole(groupDn);
185 // if (group != null) {
186 // allRoles.add(group);
187 // collectRoles(group, allRoles);
193 private void collectAnonymousRoles(List
<Role
> allRoles
) {
194 // TODO gather anonymous roles
199 public Role
getRole(String name
) {
200 return (Role
) doGetRole(toLdapName(name
));
204 public Role
[] getRoles(String filter
) throws InvalidSyntaxException
{
205 List
<?
extends Role
> res
= getRoles(getBaseDn(), filter
, true);
206 return res
.toArray(new Role
[res
.size()]);
209 List
<DirectoryUser
> getRoles(LdapName searchBase
, String filter
, boolean deep
) throws InvalidSyntaxException
{
210 LdapEntryWorkingCopy wc
= getWorkingCopy();
211 // Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null;
212 List
<LdapEntry
> searchRes
= getDirectoryDao().doGetEntries(searchBase
, filter
, deep
);
213 List
<DirectoryUser
> res
= new ArrayList
<>();
214 for (LdapEntry entry
: searchRes
)
215 res
.add((DirectoryUser
) entry
);
217 for (Iterator
<DirectoryUser
> it
= res
.iterator(); it
.hasNext();) {
218 DirectoryUser user
= (DirectoryUser
) it
.next();
219 LdapName dn
= LdapNameUtils
.toLdapName(user
.getName());
220 if (wc
.getDeletedData().containsKey(dn
))
223 Filter f
= filter
!= null ? FrameworkUtil
.createFilter(filter
) : null;
224 for (LdapEntry ldapEntry
: wc
.getNewData().values()) {
225 DirectoryUser user
= (DirectoryUser
) ldapEntry
;
226 if (f
== null || f
.match(user
.getProperties()))
229 // no need to check modified users,
230 // since doGetRoles was already based on the modified attributes
233 // if non deep we also search users and groups
236 // if (!(searchBase.endsWith(new LdapName(getUserBase()))
237 // || searchBase.endsWith(new LdapName(getGroupBase())))) {
238 // LdapName usersBase = (LdapName) ((LdapName) searchBase.clone()).add(getUserBase());
239 // res.addAll(getRoles(usersBase, filter, false));
240 // LdapName groupsBase = (LdapName) ((LdapName) searchBase.clone()).add(getGroupBase());
241 // res.addAll(getRoles(groupsBase, filter, false));
243 // } catch (InvalidNameException e) {
244 // throw new IllegalStateException("Cannot search users and groups", e);
251 public User
getUser(String key
, String value
) {
252 // TODO check value null or empty
253 List
<DirectoryUser
> collectedUsers
= new ArrayList
<DirectoryUser
>();
255 doGetUser(key
, value
, collectedUsers
);
257 throw new IllegalArgumentException("Key cannot be null");
260 if (collectedUsers
.size() == 1) {
261 return collectedUsers
.get(0);
262 } else if (collectedUsers
.size() > 1) {
263 // log.warn(collectedUsers.size() + " users for " + (key != null ? key + "=" :
269 protected void doGetUser(String key
, String value
, List
<DirectoryUser
> collectedUsers
) {
270 String f
= "(" + key
+ "=" + value
+ ")";
271 List
<LdapEntry
> users
= getDirectoryDao().doGetEntries(getBaseDn(), f
, true);
272 for (LdapEntry entry
: users
)
273 collectedUsers
.add((DirectoryUser
) entry
);
277 public Authorization
getAuthorization(User user
) {
278 if (user
== null || user
instanceof DirectoryUser
) {
279 return new LdifAuthorization(user
, getAllRoles((DirectoryUser
) user
));
282 DirectoryUserAdmin scopedUserAdmin
= (DirectoryUserAdmin
) scope(user
);
284 DirectoryUser directoryUser
= (DirectoryUser
) scopedUserAdmin
.getRole(user
.getName());
285 if (directoryUser
== null)
286 throw new IllegalStateException("No scoped user found for " + user
);
287 LdifAuthorization authorization
= new LdifAuthorization(directoryUser
,
288 scopedUserAdmin
.getAllRoles(directoryUser
));
289 return authorization
;
291 scopedUserAdmin
.destroy();
297 public Role
createRole(String name
, int type
) {
299 LdapEntryWorkingCopy wc
= getWorkingCopy();
300 LdapName dn
= toLdapName(name
);
301 if ((getDirectoryDao().entryExists(dn
) && !wc
.getDeletedData().containsKey(dn
))
302 || wc
.getNewData().containsKey(dn
))
303 throw new IllegalArgumentException("Already a role " + name
);
304 BasicAttributes attrs
= new BasicAttributes(true);
305 // attrs.put(LdifName.dn.name(), dn.toString());
306 Rdn nameRdn
= dn
.getRdn(dn
.size() - 1);
307 // TODO deal with multiple attr RDN
308 attrs
.put(nameRdn
.getType(), nameRdn
.getValue());
309 if (wc
.getDeletedData().containsKey(dn
)) {
310 wc
.getDeletedData().remove(dn
);
311 wc
.getModifiedData().put(dn
, attrs
);
312 return getRole(name
);
314 wc
.getModifiedData().put(dn
, attrs
);
315 LdapEntry newRole
= newRole(dn
, type
, attrs
);
316 wc
.getNewData().put(dn
, newRole
);
317 return (Role
) newRole
;
321 protected LdapEntry
newRole(LdapName dn
, int type
, Attributes attrs
) {
323 BasicAttribute objClass
= new BasicAttribute(objectClass
.name());
324 if (type
== Role
.USER
) {
325 String userObjClass
= getUserObjectClass();
326 objClass
.add(userObjClass
);
327 if (inetOrgPerson
.name().equals(userObjClass
)) {
328 objClass
.add(organizationalPerson
.name());
329 objClass
.add(person
.name());
330 } else if (organizationalPerson
.name().equals(userObjClass
)) {
331 objClass
.add(person
.name());
333 objClass
.add(top
.name());
334 objClass
.add(extensibleObject
.name());
336 newRole
= newUser(dn
, attrs
);
337 } else if (type
== Role
.GROUP
) {
338 String groupObjClass
= getGroupObjectClass();
339 objClass
.add(groupObjClass
);
340 // objClass.add(LdifName.extensibleObject.name());
341 objClass
.add(top
.name());
343 newRole
= newGroup(dn
, attrs
);
345 throw new IllegalArgumentException("Unsupported type " + type
);
350 public boolean removeRole(String name
) {
351 return removeEntry(LdapNameUtils
.toLdapName(name
));
353 // LdapEntryWorkingCopy wc = getWorkingCopy();
354 // LdapName dn = toLdapName(name);
355 // boolean actuallyDeleted;
356 // if (getDirectoryDao().daoHasEntry(dn) || wc.getNewData().containsKey(dn)) {
357 // DirectoryUser user = (DirectoryUser) getRole(name);
358 // wc.getDeletedData().put(dn, user);
359 // actuallyDeleted = true;
360 // } else {// just removing from groups (e.g. system roles)
361 // actuallyDeleted = false;
363 // for (LdapName groupDn : getDirectoryDao().getDirectGroups(dn)) {
364 // LdapEntry group = doGetRole(groupDn);
365 // group.getAttributes().get(getMemberAttributeId()).remove(dn.toString());
367 // return actuallyDeleted;
374 public HierarchyUnit
getHierarchyUnit(Role role
) {
375 LdapName dn
= LdapNameUtils
.toLdapName(role
.getName());
376 LdapName huDn
= LdapNameUtils
.getParent(dn
);
377 HierarchyUnit hierarchyUnit
= getDirectoryDao().doGetHierarchyUnit(huDn
);
378 if (hierarchyUnit
== null)
379 throw new IllegalStateException("No hierarchy unit found for " + role
);
380 return hierarchyUnit
;
384 public Iterable
<?
extends Role
> getHierarchyUnitRoles(HierarchyUnit hierarchyUnit
, String filter
, boolean deep
) {
385 LdapName dn
= LdapNameUtils
.toLdapName(hierarchyUnit
.getContext());
387 return getRoles(dn
, filter
, deep
);
388 } catch (InvalidSyntaxException e
) {
389 throw new IllegalArgumentException("Cannot filter " + filter
+ " " + dn
, e
);
396 protected LdapEntry
newUser(LdapName name
, Attributes attrs
) {
397 // TODO support devices, applications, etc.
398 return new LdifUser(this, name
, attrs
);
401 protected LdapEntry
newGroup(LdapName name
, Attributes attrs
) {
402 return new LdifGroup(this, name
, attrs
);
407 protected UserAdmin
getExternalRoles() {
408 return externalRoles
;
411 public void setExternalRoles(UserAdmin externalRoles
) {
412 this.externalRoles
= externalRoles
;
415 // public void setTransactionManager(TransactionManager transactionManager) {
416 // this.transactionManager = transactionManager;
422 static LdapName
toLdapName(String name
) {
424 return new LdapName(name
);
425 } catch (InvalidNameException e
) {
426 throw new IllegalArgumentException(name
+ " is not an LDAP name", e
);