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
.osgi
.framework
.Filter
;
34 import org
.osgi
.framework
.FrameworkUtil
;
35 import org
.osgi
.framework
.InvalidSyntaxException
;
36 import org
.osgi
.service
.useradmin
.Authorization
;
37 import org
.osgi
.service
.useradmin
.Role
;
38 import org
.osgi
.service
.useradmin
.User
;
39 import org
.osgi
.service
.useradmin
.UserAdmin
;
41 /** Base class for a {@link UserDirectory}. */
42 public class DirectoryUserAdmin
extends AbstractLdapDirectory
implements UserAdmin
, UserDirectory
{
44 private UserAdmin externalRoles
;
45 // private List<String> indexedUserProperties = Arrays
46 // .asList(new String[] { LdapAttrs.uid.name(), LdapAttrs.mail.name(),
47 // LdapAttrs.cn.name() });
50 // private TransactionManager transactionManager;
51 public DirectoryUserAdmin(URI uriArg
, Dictionary
<String
, ?
> props
) {
52 this(uriArg
, props
, false);
55 public DirectoryUserAdmin(URI uriArg
, Dictionary
<String
, ?
> props
, boolean scoped
) {
56 super(uriArg
, props
, scoped
);
59 public DirectoryUserAdmin(Dictionary
<String
, ?
> props
) {
67 protected AbstractLdapDirectory
scope(User user
) {
68 throw new UnsupportedAddressTypeException();
71 protected DirectoryUserAdmin
scopeLdap(User user
) {
72 Dictionary
<String
, Object
> credentials
= user
.getCredentials();
73 String username
= (String
) credentials
.get(SHARED_STATE_USERNAME
);
75 username
= user
.getName();
76 Dictionary
<String
, Object
> properties
= cloneConfigProperties();
77 properties
.put(Context
.SECURITY_PRINCIPAL
, username
.toString());
78 Object pwdCred
= credentials
.get(SHARED_STATE_PASSWORD
);
79 byte[] pwd
= (byte[]) pwdCred
;
81 char[] password
= DirectoryDigestUtils
.bytesToChars(pwd
);
82 properties
.put(Context
.SECURITY_CREDENTIALS
, new String(password
));
84 properties
.put(Context
.SECURITY_AUTHENTICATION
, "GSSAPI");
86 return new DirectoryUserAdmin(null, properties
, true);
89 protected DirectoryUserAdmin
scopeLdif(User user
) {
90 Dictionary
<String
, Object
> credentials
= user
.getCredentials();
91 String username
= (String
) credentials
.get(SHARED_STATE_USERNAME
);
93 username
= user
.getName();
94 Object pwdCred
= credentials
.get(SHARED_STATE_PASSWORD
);
95 byte[] pwd
= (byte[]) pwdCred
;
97 char[] password
= DirectoryDigestUtils
.bytesToChars(pwd
);
98 User directoryUser
= (User
) getRole(username
);
99 if (!directoryUser
.hasCredential(null, password
))
100 throw new IllegalStateException("Invalid credentials");
102 throw new IllegalStateException("Password is required");
104 Dictionary
<String
, Object
> properties
= cloneConfigProperties();
105 properties
.put(DirectoryConf
.readOnly
.name(), "true");
106 DirectoryUserAdmin scopedUserAdmin
= new DirectoryUserAdmin(null, properties
, true);
107 // scopedUserAdmin.groups = Collections.unmodifiableNavigableMap(groups);
108 // scopedUserAdmin.users = Collections.unmodifiableNavigableMap(users);
109 // FIXME do it better
110 ((LdifDao
) getDirectoryDao()).scope((LdifDao
) scopedUserAdmin
.getDirectoryDao());
111 return scopedUserAdmin
;
115 public String
getRolePath(Role role
) {
116 return nameToRelativePath(LdapNameUtils
.toLdapName(role
.getName()));
120 public String
getRoleSimpleName(Role role
) {
121 LdapName dn
= LdapNameUtils
.toLdapName(role
.getName());
122 String name
= LdapNameUtils
.getLastRdnValue(dn
);
127 public Role
getRoleByPath(String path
) {
128 return (Role
) doGetRole(pathToName(path
));
131 protected List
<Role
> getAllRoles(DirectoryUser user
) {
132 List
<Role
> allRoles
= new ArrayList
<Role
>();
134 collectRoles(user
, allRoles
);
137 collectAnonymousRoles(allRoles
);
141 private void collectRoles(DirectoryUser user
, List
<Role
> allRoles
) {
142 List
<LdapEntry
> allEntries
= new ArrayList
<>();
143 LdapEntry entry
= (LdapEntry
) user
;
144 collectGroups(entry
, allEntries
);
145 for (LdapEntry e
: allEntries
) {
146 allRoles
.add((Role
) e
);
148 // Attributes attrs = user.getAttributes();
149 // // TODO centralize attribute name
150 // Attribute memberOf = attrs.get(LdapAttrs.memberOf.name());
151 // // if user belongs to this directory, we only check memberOf
152 // if (memberOf != null && user.getDn().startsWith(getBaseDn())) {
154 // NamingEnumeration<?> values = memberOf.getAll();
155 // while (values.hasMore()) {
156 // Object value = values.next();
157 // LdapName groupDn = new LdapName(value.toString());
158 // DirectoryUser group = doGetRole(groupDn);
159 // if (group != null)
160 // allRoles.add(group);
162 // } catch (NamingException e) {
163 // throw new IllegalStateException("Cannot get memberOf groups for " + user, e);
166 // for (LdapName groupDn : getDirectoryDao().getDirectGroups(user.getDn())) {
167 // // TODO check for loops
168 // DirectoryUser group = doGetRole(groupDn);
169 // if (group != null) {
170 // allRoles.add(group);
171 // collectRoles(group, allRoles);
177 private void collectAnonymousRoles(List
<Role
> allRoles
) {
178 // TODO gather anonymous roles
183 public Role
getRole(String name
) {
184 return (Role
) doGetRole(toLdapName(name
));
188 public Role
[] getRoles(String filter
) throws InvalidSyntaxException
{
189 List
<?
extends Role
> res
= getRoles(getBaseDn(), filter
, true);
190 return res
.toArray(new Role
[res
.size()]);
193 List
<DirectoryUser
> getRoles(LdapName searchBase
, String filter
, boolean deep
) throws InvalidSyntaxException
{
194 LdapEntryWorkingCopy wc
= getWorkingCopy();
195 // Filter f = filter != null ? FrameworkUtil.createFilter(filter) : null;
196 List
<LdapEntry
> searchRes
= getDirectoryDao().doGetEntries(searchBase
, filter
, deep
);
197 List
<DirectoryUser
> res
= new ArrayList
<>();
198 for (LdapEntry entry
: searchRes
)
199 res
.add((DirectoryUser
) entry
);
201 for (Iterator
<DirectoryUser
> it
= res
.iterator(); it
.hasNext();) {
202 DirectoryUser user
= (DirectoryUser
) it
.next();
203 LdapName dn
= LdapNameUtils
.toLdapName(user
.getName());
204 if (wc
.getDeletedData().containsKey(dn
))
207 Filter f
= filter
!= null ? FrameworkUtil
.createFilter(filter
) : null;
208 for (LdapEntry ldapEntry
: wc
.getNewData().values()) {
209 DirectoryUser user
= (DirectoryUser
) ldapEntry
;
210 if (f
== null || f
.match(user
.getProperties()))
213 // no need to check modified users,
214 // since doGetRoles was already based on the modified attributes
217 // if non deep we also search users and groups
220 // if (!(searchBase.endsWith(new LdapName(getUserBase()))
221 // || searchBase.endsWith(new LdapName(getGroupBase())))) {
222 // LdapName usersBase = (LdapName) ((LdapName) searchBase.clone()).add(getUserBase());
223 // res.addAll(getRoles(usersBase, filter, false));
224 // LdapName groupsBase = (LdapName) ((LdapName) searchBase.clone()).add(getGroupBase());
225 // res.addAll(getRoles(groupsBase, filter, false));
227 // } catch (InvalidNameException e) {
228 // throw new IllegalStateException("Cannot search users and groups", e);
235 public User
getUser(String key
, String value
) {
236 // TODO check value null or empty
237 List
<DirectoryUser
> collectedUsers
= new ArrayList
<DirectoryUser
>();
239 doGetUser(key
, value
, collectedUsers
);
241 throw new IllegalArgumentException("Key cannot be null");
244 if (collectedUsers
.size() == 1) {
245 return collectedUsers
.get(0);
246 } else if (collectedUsers
.size() > 1) {
247 // log.warn(collectedUsers.size() + " users for " + (key != null ? key + "=" :
253 protected void doGetUser(String key
, String value
, List
<DirectoryUser
> collectedUsers
) {
254 String f
= "(" + key
+ "=" + value
+ ")";
255 List
<LdapEntry
> users
= getDirectoryDao().doGetEntries(getBaseDn(), f
, true);
256 for (LdapEntry entry
: users
)
257 collectedUsers
.add((DirectoryUser
) entry
);
261 public Authorization
getAuthorization(User user
) {
262 if (user
== null || user
instanceof DirectoryUser
) {
263 return new LdifAuthorization(user
, getAllRoles((DirectoryUser
) user
));
266 DirectoryUserAdmin scopedUserAdmin
= (DirectoryUserAdmin
) scope(user
);
268 DirectoryUser directoryUser
= (DirectoryUser
) scopedUserAdmin
.getRole(user
.getName());
269 if (directoryUser
== null)
270 throw new IllegalStateException("No scoped user found for " + user
);
271 LdifAuthorization authorization
= new LdifAuthorization(directoryUser
,
272 scopedUserAdmin
.getAllRoles(directoryUser
));
273 return authorization
;
275 scopedUserAdmin
.destroy();
281 public Role
createRole(String name
, int type
) {
283 LdapEntryWorkingCopy wc
= getWorkingCopy();
284 LdapName dn
= toLdapName(name
);
285 if ((getDirectoryDao().entryExists(dn
) && !wc
.getDeletedData().containsKey(dn
))
286 || wc
.getNewData().containsKey(dn
))
287 throw new IllegalArgumentException("Already a role " + name
);
288 BasicAttributes attrs
= new BasicAttributes(true);
289 // attrs.put(LdifName.dn.name(), dn.toString());
290 Rdn nameRdn
= dn
.getRdn(dn
.size() - 1);
291 // TODO deal with multiple attr RDN
292 attrs
.put(nameRdn
.getType(), nameRdn
.getValue());
293 if (wc
.getDeletedData().containsKey(dn
)) {
294 wc
.getDeletedData().remove(dn
);
295 wc
.getModifiedData().put(dn
, attrs
);
296 return getRole(name
);
298 wc
.getModifiedData().put(dn
, attrs
);
299 LdapEntry newRole
= newRole(dn
, type
, attrs
);
300 wc
.getNewData().put(dn
, newRole
);
301 return (Role
) newRole
;
305 protected LdapEntry
newRole(LdapName dn
, int type
, Attributes attrs
) {
307 BasicAttribute objClass
= new BasicAttribute(objectClass
.name());
308 if (type
== Role
.USER
) {
309 String userObjClass
= getUserObjectClass();
310 objClass
.add(userObjClass
);
311 if (inetOrgPerson
.name().equals(userObjClass
)) {
312 objClass
.add(organizationalPerson
.name());
313 objClass
.add(person
.name());
314 } else if (organizationalPerson
.name().equals(userObjClass
)) {
315 objClass
.add(person
.name());
317 objClass
.add(top
.name());
318 objClass
.add(extensibleObject
.name());
320 newRole
= newUser(dn
, attrs
);
321 } else if (type
== Role
.GROUP
) {
322 String groupObjClass
= getGroupObjectClass();
323 objClass
.add(groupObjClass
);
324 // objClass.add(LdifName.extensibleObject.name());
325 objClass
.add(top
.name());
327 newRole
= newGroup(dn
, attrs
);
329 throw new IllegalArgumentException("Unsupported type " + type
);
334 public boolean removeRole(String name
) {
335 return removeEntry(LdapNameUtils
.toLdapName(name
));
337 // LdapEntryWorkingCopy wc = getWorkingCopy();
338 // LdapName dn = toLdapName(name);
339 // boolean actuallyDeleted;
340 // if (getDirectoryDao().daoHasEntry(dn) || wc.getNewData().containsKey(dn)) {
341 // DirectoryUser user = (DirectoryUser) getRole(name);
342 // wc.getDeletedData().put(dn, user);
343 // actuallyDeleted = true;
344 // } else {// just removing from groups (e.g. system roles)
345 // actuallyDeleted = false;
347 // for (LdapName groupDn : getDirectoryDao().getDirectGroups(dn)) {
348 // LdapEntry group = doGetRole(groupDn);
349 // group.getAttributes().get(getMemberAttributeId()).remove(dn.toString());
351 // return actuallyDeleted;
358 public HierarchyUnit
getHierarchyUnit(Role role
) {
359 LdapName dn
= LdapNameUtils
.toLdapName(role
.getName());
360 LdapName huDn
= LdapNameUtils
.getParent(dn
);
361 HierarchyUnit hierarchyUnit
= getDirectoryDao().doGetHierarchyUnit(huDn
);
362 if (hierarchyUnit
== null)
363 throw new IllegalStateException("No hierarchy unit found for " + role
);
364 return hierarchyUnit
;
368 public Iterable
<?
extends Role
> getHierarchyUnitRoles(HierarchyUnit hierarchyUnit
, String filter
, boolean deep
) {
369 LdapName dn
= LdapNameUtils
.toLdapName(hierarchyUnit
.getContext());
371 return getRoles(dn
, filter
, deep
);
372 } catch (InvalidSyntaxException e
) {
373 throw new IllegalArgumentException("Cannot filter " + filter
+ " " + dn
, e
);
380 protected LdapEntry
newUser(LdapName name
, Attributes attrs
) {
381 // TODO support devices, applications, etc.
382 return new LdifUser(this, name
, attrs
);
385 protected LdapEntry
newGroup(LdapName name
, Attributes attrs
) {
386 return new LdifGroup(this, name
, attrs
);
391 protected UserAdmin
getExternalRoles() {
392 return externalRoles
;
395 public void setExternalRoles(UserAdmin externalRoles
) {
396 this.externalRoles
= externalRoles
;
399 // public void setTransactionManager(TransactionManager transactionManager) {
400 // this.transactionManager = transactionManager;
406 static LdapName
toLdapName(String name
) {
408 return new LdapName(name
);
409 } catch (InvalidNameException e
) {
410 throw new IllegalArgumentException(name
+ " is not an LDAP name", e
);