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
.LdapEntry
;
30 import org
.argeo
.util
.directory
.ldap
.LdapEntryWorkingCopy
;
31 import org
.argeo
.util
.directory
.ldap
.LdapNameUtils
;
32 import org
.argeo
.util
.directory
.ldap
.LdifDao
;
33 import org
.argeo
.util
.naming
.LdapObjs
;
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 throw new UnsupportedAddressTypeException();
72 protected DirectoryUserAdmin
scopeLdap(User user
) {
73 Dictionary
<String
, Object
> credentials
= user
.getCredentials();
74 String username
= (String
) credentials
.get(SHARED_STATE_USERNAME
);
76 username
= user
.getName();
77 Dictionary
<String
, Object
> properties
= cloneProperties();
78 properties
.put(Context
.SECURITY_PRINCIPAL
, username
.toString());
79 Object pwdCred
= credentials
.get(SHARED_STATE_PASSWORD
);
80 byte[] pwd
= (byte[]) pwdCred
;
82 char[] password
= DirectoryDigestUtils
.bytesToChars(pwd
);
83 properties
.put(Context
.SECURITY_CREDENTIALS
, new String(password
));
85 properties
.put(Context
.SECURITY_AUTHENTICATION
, "GSSAPI");
87 return new DirectoryUserAdmin(null, properties
, true);
90 protected DirectoryUserAdmin
scopeLdif(User user
) {
91 Dictionary
<String
, Object
> credentials
= user
.getCredentials();
92 String username
= (String
) credentials
.get(SHARED_STATE_USERNAME
);
94 username
= user
.getName();
95 Object pwdCred
= credentials
.get(SHARED_STATE_PASSWORD
);
96 byte[] pwd
= (byte[]) pwdCred
;
98 char[] password
= DirectoryDigestUtils
.bytesToChars(pwd
);
99 User directoryUser
= (User
) getRole(username
);
100 if (!directoryUser
.hasCredential(null, password
))
101 throw new IllegalStateException("Invalid credentials");
103 throw new IllegalStateException("Password is required");
105 Dictionary
<String
, Object
> properties
= cloneProperties();
106 properties
.put(DirectoryConf
.readOnly
.name(), "true");
107 DirectoryUserAdmin scopedUserAdmin
= new DirectoryUserAdmin(null, properties
, true);
108 // scopedUserAdmin.groups = Collections.unmodifiableNavigableMap(groups);
109 // scopedUserAdmin.users = Collections.unmodifiableNavigableMap(users);
110 // FIXME do it better
111 ((LdifDao
) getDirectoryDao()).scope((LdifDao
) scopedUserAdmin
.getDirectoryDao());
112 return scopedUserAdmin
;
116 public String
getRolePath(Role role
) {
117 return nameToRelativePath(LdapNameUtils
.toLdapName(role
.getName()));
121 public String
getRoleSimpleName(Role role
) {
122 LdapName dn
= LdapNameUtils
.toLdapName(role
.getName());
123 String name
= LdapNameUtils
.getLastRdnValue(dn
);
128 public Role
getRoleByPath(String path
) {
129 return (Role
) doGetRole(pathToName(path
));
132 protected List
<Role
> getAllRoles(DirectoryUser user
) {
133 List
<Role
> allRoles
= new ArrayList
<Role
>();
135 collectRoles(user
, allRoles
);
138 collectAnonymousRoles(allRoles
);
142 private void collectRoles(DirectoryUser user
, List
<Role
> allRoles
) {
143 List
<LdapEntry
> allEntries
= new ArrayList
<>();
144 LdapEntry entry
= (LdapEntry
) user
;
145 collectGroups(entry
, allEntries
);
146 for (LdapEntry e
: allEntries
) {
147 allRoles
.add((Role
) e
);
149 // Attributes attrs = user.getAttributes();
150 // // TODO centralize attribute name
151 // Attribute memberOf = attrs.get(LdapAttrs.memberOf.name());
152 // // if user belongs to this directory, we only check memberOf
153 // if (memberOf != null && user.getDn().startsWith(getBaseDn())) {
155 // NamingEnumeration<?> values = memberOf.getAll();
156 // while (values.hasMore()) {
157 // Object value = values.next();
158 // LdapName groupDn = new LdapName(value.toString());
159 // DirectoryUser group = doGetRole(groupDn);
160 // if (group != null)
161 // allRoles.add(group);
163 // } catch (NamingException e) {
164 // throw new IllegalStateException("Cannot get memberOf groups for " + user, e);
167 // for (LdapName groupDn : getDirectoryDao().getDirectGroups(user.getDn())) {
168 // // TODO check for loops
169 // DirectoryUser group = doGetRole(groupDn);
170 // if (group != null) {
171 // allRoles.add(group);
172 // collectRoles(group, allRoles);
178 private void collectAnonymousRoles(List
<Role
> allRoles
) {
179 // TODO gather anonymous roles
184 public Role
getRole(String name
) {
185 return (Role
) doGetRole(toLdapName(name
));
189 public Role
[] getRoles(String filter
) throws InvalidSyntaxException
{
190 List
<?
extends Role
> res
= getRoles(getBaseDn(), filter
, true);
191 return res
.toArray(new Role
[res
.size()]);
194 List
<DirectoryUser
> getRoles(LdapName searchBase
, String filter
, boolean deep
) throws InvalidSyntaxException
{
195 LdapEntryWorkingCopy wc
= getWorkingCopy();
196 // Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null;
197 List
<LdapEntry
> searchRes
= getDirectoryDao().doGetEntries(searchBase
, filter
, deep
);
198 List
<DirectoryUser
> res
= new ArrayList
<>();
199 for (LdapEntry entry
: searchRes
)
200 res
.add((DirectoryUser
) entry
);
202 for (Iterator
<DirectoryUser
> it
= res
.iterator(); it
.hasNext();) {
203 DirectoryUser user
= (DirectoryUser
) it
.next();
204 LdapName dn
= LdapNameUtils
.toLdapName(user
.getName());
205 if (wc
.getDeletedData().containsKey(dn
))
208 Filter f
= filter
!= null ? FrameworkUtil
.createFilter(filter
) : null;
209 for (LdapEntry ldapEntry
: wc
.getNewData().values()) {
210 DirectoryUser user
= (DirectoryUser
) ldapEntry
;
211 if (f
== null || f
.match(user
.getProperties()))
214 // no need to check modified users,
215 // since doGetRoles was already based on the modified attributes
218 // if non deep we also search users and groups
221 // if (!(searchBase.endsWith(new LdapName(getUserBase()))
222 // || searchBase.endsWith(new LdapName(getGroupBase())))) {
223 // LdapName usersBase = (LdapName) ((LdapName) searchBase.clone()).add(getUserBase());
224 // res.addAll(getRoles(usersBase, filter, false));
225 // LdapName groupsBase = (LdapName) ((LdapName) searchBase.clone()).add(getGroupBase());
226 // res.addAll(getRoles(groupsBase, filter, false));
228 // } catch (InvalidNameException e) {
229 // throw new IllegalStateException("Cannot search users and groups", e);
236 public User
getUser(String key
, String value
) {
237 // TODO check value null or empty
238 List
<DirectoryUser
> collectedUsers
= new ArrayList
<DirectoryUser
>();
240 doGetUser(key
, value
, collectedUsers
);
242 throw new IllegalArgumentException("Key cannot be null");
245 if (collectedUsers
.size() == 1) {
246 return collectedUsers
.get(0);
247 } else if (collectedUsers
.size() > 1) {
248 // log.warn(collectedUsers.size() + " users for " + (key != null ? key + "=" :
254 protected void doGetUser(String key
, String value
, List
<DirectoryUser
> collectedUsers
) {
255 String f
= "(" + key
+ "=" + value
+ ")";
256 List
<LdapEntry
> users
= getDirectoryDao().doGetEntries(getBaseDn(), f
, true);
257 for (LdapEntry entry
: users
)
258 collectedUsers
.add((DirectoryUser
) entry
);
262 public Authorization
getAuthorization(User user
) {
263 if (user
== null || user
instanceof DirectoryUser
) {
264 return new LdifAuthorization(user
, getAllRoles((DirectoryUser
) user
));
267 DirectoryUserAdmin scopedUserAdmin
= (DirectoryUserAdmin
) scope(user
);
269 DirectoryUser directoryUser
= (DirectoryUser
) scopedUserAdmin
.getRole(user
.getName());
270 if (directoryUser
== null)
271 throw new IllegalStateException("No scoped user found for " + user
);
272 LdifAuthorization authorization
= new LdifAuthorization(directoryUser
,
273 scopedUserAdmin
.getAllRoles(directoryUser
));
274 return authorization
;
276 scopedUserAdmin
.destroy();
282 public Role
createRole(String name
, int type
) {
284 LdapEntryWorkingCopy wc
= getWorkingCopy();
285 LdapName dn
= toLdapName(name
);
286 if ((getDirectoryDao().daoHasEntry(dn
) && !wc
.getDeletedData().containsKey(dn
))
287 || wc
.getNewData().containsKey(dn
))
288 throw new IllegalArgumentException("Already a role " + name
);
289 BasicAttributes attrs
= new BasicAttributes(true);
290 // attrs.put(LdifName.dn.name(), dn.toString());
291 Rdn nameRdn
= dn
.getRdn(dn
.size() - 1);
292 // TODO deal with multiple attr RDN
293 attrs
.put(nameRdn
.getType(), nameRdn
.getValue());
294 if (wc
.getDeletedData().containsKey(dn
)) {
295 wc
.getDeletedData().remove(dn
);
296 wc
.getModifiedData().put(dn
, attrs
);
297 return getRole(name
);
299 wc
.getModifiedData().put(dn
, attrs
);
300 LdapEntry newRole
= newRole(dn
, type
, attrs
);
301 wc
.getNewData().put(dn
, newRole
);
302 return (Role
) newRole
;
306 protected LdapEntry
newRole(LdapName dn
, int type
, Attributes attrs
) {
308 BasicAttribute objClass
= new BasicAttribute(objectClass
.name());
309 if (type
== Role
.USER
) {
310 String userObjClass
= getUserObjectClass();
311 objClass
.add(userObjClass
);
312 if (inetOrgPerson
.name().equals(userObjClass
)) {
313 objClass
.add(organizationalPerson
.name());
314 objClass
.add(person
.name());
315 } else if (organizationalPerson
.name().equals(userObjClass
)) {
316 objClass
.add(person
.name());
318 objClass
.add(top
.name());
319 objClass
.add(extensibleObject
.name());
321 newRole
= newUser(dn
, attrs
);
322 } else if (type
== Role
.GROUP
) {
323 String groupObjClass
= getGroupObjectClass();
324 objClass
.add(groupObjClass
);
325 // objClass.add(LdifName.extensibleObject.name());
326 objClass
.add(top
.name());
328 newRole
= newGroup(dn
, attrs
);
330 throw new IllegalArgumentException("Unsupported type " + type
);
335 public boolean removeRole(String name
) {
336 return removeEntry(LdapNameUtils
.toLdapName(name
));
338 // LdapEntryWorkingCopy wc = getWorkingCopy();
339 // LdapName dn = toLdapName(name);
340 // boolean actuallyDeleted;
341 // if (getDirectoryDao().daoHasEntry(dn) || wc.getNewData().containsKey(dn)) {
342 // DirectoryUser user = (DirectoryUser) getRole(name);
343 // wc.getDeletedData().put(dn, user);
344 // actuallyDeleted = true;
345 // } else {// just removing from groups (e.g. system roles)
346 // actuallyDeleted = false;
348 // for (LdapName groupDn : getDirectoryDao().getDirectGroups(dn)) {
349 // LdapEntry group = doGetRole(groupDn);
350 // group.getAttributes().get(getMemberAttributeId()).remove(dn.toString());
352 // return actuallyDeleted;
359 public HierarchyUnit
getHierarchyUnit(Role role
) {
360 LdapName dn
= LdapNameUtils
.toLdapName(role
.getName());
361 LdapName huDn
= LdapNameUtils
.getParent(dn
);
362 HierarchyUnit hierarchyUnit
= getDirectoryDao().doGetHierarchyUnit(huDn
);
363 if (hierarchyUnit
== null)
364 throw new IllegalStateException("No hierarchy unit found for " + role
);
365 return hierarchyUnit
;
369 public Iterable
<?
extends Role
> getHierarchyUnitRoles(HierarchyUnit hierarchyUnit
, String filter
, boolean deep
) {
370 LdapName dn
= LdapNameUtils
.toLdapName(hierarchyUnit
.getContext());
372 return getRoles(dn
, filter
, deep
);
373 } catch (InvalidSyntaxException e
) {
374 throw new IllegalArgumentException("Cannot filter " + filter
+ " " + dn
, e
);
381 protected LdapEntry
newUser(LdapName name
, Attributes attrs
) {
382 // TODO support devices, applications, etc.
383 return new LdifUser
.LdifPerson(this, name
, attrs
);
386 protected LdapEntry
newGroup(LdapName name
, Attributes attrs
) {
387 if (LdapNameUtils
.getParentRdn(name
).equals(getSystemRoleBaseRdn()))
388 return new LdifGroup
.LdifSystemPermissions(this, name
, attrs
);
390 if (hasObjectClass(attrs
, LdapObjs
.organization
))
391 return new LdifGroup
.LdifOrganization(this, name
, attrs
);
393 return new LdifGroup
.LdifFunctionalGroup(this, name
, attrs
);
398 protected UserAdmin
getExternalRoles() {
399 return externalRoles
;
402 public void setExternalRoles(UserAdmin externalRoles
) {
403 this.externalRoles
= externalRoles
;
406 // public void setTransactionManager(TransactionManager transactionManager) {
407 // this.transactionManager = transactionManager;
413 static LdapName
toLdapName(String name
) {
415 return new LdapName(name
);
416 } catch (InvalidNameException e
) {
417 throw new IllegalArgumentException(name
+ " is not an LDAP name", e
);