1 package org
.argeo
.cms
.osgi
.useradmin
;
3 import static org
.argeo
.cms
.osgi
.useradmin
.DirectoryUserAdmin
.toLdapName
;
5 import java
.util
.ArrayList
;
6 import java
.util
.Arrays
;
7 import java
.util
.HashMap
;
8 import java
.util
.HashSet
;
9 import java
.util
.Hashtable
;
10 import java
.util
.List
;
12 import java
.util
.Objects
;
14 import java
.util
.TreeSet
;
16 import javax
.naming
.InvalidNameException
;
17 import javax
.naming
.ldap
.LdapName
;
19 import org
.argeo
.api
.cms
.directory
.CmsUser
;
20 import org
.argeo
.api
.cms
.directory
.UserDirectory
;
21 import org
.argeo
.cms
.runtime
.DirectoryConf
;
22 import org
.osgi
.framework
.InvalidSyntaxException
;
23 import org
.osgi
.service
.useradmin
.Authorization
;
24 import org
.osgi
.service
.useradmin
.Group
;
25 import org
.osgi
.service
.useradmin
.Role
;
26 import org
.osgi
.service
.useradmin
.User
;
27 import org
.osgi
.service
.useradmin
.UserAdmin
;
30 * Aggregates multiple {@link UserDirectory} and integrates them with system
33 public class AggregatingUserAdmin
implements UserAdmin
{
34 private final LdapName systemRolesBaseDn
;
35 private final LdapName tokensBaseDn
;
38 private DirectoryUserAdmin systemRoles
= null;
39 private DirectoryUserAdmin tokens
= null;
40 private Map
<LdapName
, DirectoryUserAdmin
> businessRoles
= new HashMap
<LdapName
, DirectoryUserAdmin
>();
42 // TODO rather use an empty constructor and an init method
43 public AggregatingUserAdmin(String systemRolesBaseDn
, String tokensBaseDn
) {
45 this.systemRolesBaseDn
= new LdapName(systemRolesBaseDn
);
46 if (tokensBaseDn
!= null)
47 this.tokensBaseDn
= new LdapName(tokensBaseDn
);
49 this.tokensBaseDn
= null;
50 } catch (InvalidNameException e
) {
51 throw new IllegalStateException("Cannot initialize " + AggregatingUserAdmin
.class, e
);
56 public Role
createRole(String name
, int type
) {
57 return findUserAdmin(name
).createRole(name
, type
);
61 public boolean removeRole(String name
) {
62 boolean actuallyDeleted
= findUserAdmin(name
).removeRole(name
);
63 systemRoles
.removeRole(name
);
64 return actuallyDeleted
;
68 public Role
getRole(String name
) {
69 return findUserAdmin(name
).getRole(name
);
73 public Role
[] getRoles(String filter
) throws InvalidSyntaxException
{
74 List
<Role
> res
= new ArrayList
<Role
>();
75 for (UserAdmin userAdmin
: businessRoles
.values()) {
76 res
.addAll(Arrays
.asList(userAdmin
.getRoles(filter
)));
78 res
.addAll(Arrays
.asList(systemRoles
.getRoles(filter
)));
79 return res
.toArray(new Role
[res
.size()]);
83 public User
getUser(String key
, String value
) {
84 List
<User
> res
= new ArrayList
<User
>();
85 for (UserAdmin userAdmin
: businessRoles
.values()) {
86 User u
= userAdmin
.getUser(key
, value
);
90 // Note: node roles cannot contain users, so it is not searched
91 return res
.size() == 1 ? res
.get(0) : null;
94 /** Builds an authorisation by scanning all referentials. */
96 public Authorization
getAuthorization(User user
) {
97 if (user
== null) {// anonymous
98 return systemRoles
.getAuthorization(null);
100 DirectoryUserAdmin userReferentialOfThisUser
= findUserAdmin(user
.getName());
101 Authorization rawAuthorization
= userReferentialOfThisUser
.getAuthorization(user
);
102 User retrievedUser
= (User
) userReferentialOfThisUser
.getRole(user
.getName());
103 String usernameToUse
;
104 String displayNameToUse
;
105 if (user
instanceof Group
) {
106 // TODO check whether this is still working
107 String ownerDn
= TokenUtils
.userDn((Group
) user
);
108 if (ownerDn
!= null) {// tokens
109 UserAdmin ownerUserAdmin
= findUserAdmin(ownerDn
);
110 User ownerUser
= (User
) ownerUserAdmin
.getRole(ownerDn
);
111 usernameToUse
= ownerDn
;
112 displayNameToUse
= LdifAuthorization
.extractDisplayName(ownerUser
);
114 usernameToUse
= rawAuthorization
.getName();
115 displayNameToUse
= rawAuthorization
.toString();
117 } else {// regular users
118 usernameToUse
= rawAuthorization
.getName();
119 displayNameToUse
= rawAuthorization
.toString();
122 // gather roles from other referentials
123 List
<String
> rawRoles
= Arrays
.asList(rawAuthorization
.getRoles());
124 List
<String
> allRoles
= new ArrayList
<>(rawRoles
);
125 for (LdapName otherBaseDn
: businessRoles
.keySet()) {
126 if (otherBaseDn
.equals(userReferentialOfThisUser
.getBaseDn()))
128 DirectoryUserAdmin otherUserAdmin
= userAdminToUse(user
, businessRoles
.get(otherBaseDn
));
129 if (otherUserAdmin
== null)
131 for (String roleStr
: rawRoles
) {
132 User role
= (User
) findUserAdmin(roleStr
).getRole(roleStr
);
133 Authorization auth
= otherUserAdmin
.getAuthorization(role
);
134 allRoles
.addAll(Arrays
.asList(auth
.getRoles()));
139 // integrate system roles
140 final DirectoryUserAdmin userAdminToUse
= userAdminToUse(retrievedUser
, userReferentialOfThisUser
);
141 Objects
.requireNonNull(userAdminToUse
);
144 Set
<String
> sysRoles
= new HashSet
<String
>();
145 for (String role
: rawAuthorization
.getRoles()) {
146 User userOrGroup
= (User
) userAdminToUse
.getRole(role
);
147 Authorization auth
= systemRoles
.getAuthorization(userOrGroup
);
148 systemRoles
: for (String systemRole
: auth
.getRoles()) {
149 if (role
.equals(systemRole
))
150 continue systemRoles
;
151 sysRoles
.add(systemRole
);
153 // sysRoles.addAll(Arrays.asList(auth.getRoles()));
155 addAbstractSystemRoles(rawAuthorization
, sysRoles
);
156 Authorization authorization
= new AggregatingAuthorization(usernameToUse
, displayNameToUse
, sysRoles
,
157 allRoles
.toArray(new String
[allRoles
.size()]));
158 return authorization
;
160 if (userAdminToUse
!= null && userAdminToUse
.isScoped()) {
161 userAdminToUse
.destroy();
166 /** Decide whether to scope or not */
167 private DirectoryUserAdmin
userAdminToUse(User user
, DirectoryUserAdmin userAdmin
) {
168 if (userAdmin
.isAuthenticated())
170 if (user
instanceof CmsUser
) {
172 } else if (user
instanceof AuthenticatingUser
) {
173 return userAdmin
.scope(user
).orElse(null);
175 throw new IllegalArgumentException("Unsupported user type " + user
.getClass());
181 * Enrich with application-specific roles which are strictly programmatic, such
182 * as anonymous/user semantics.
184 protected void addAbstractSystemRoles(Authorization rawAuthorization
, Set
<String
> sysRoles
) {
189 // USER ADMIN AGGREGATOR
191 protected void addUserDirectory(UserDirectory ud
) {
192 if (!(ud
instanceof DirectoryUserAdmin
))
193 throw new IllegalArgumentException("Only " + DirectoryUserAdmin
.class.getName() + " is supported");
194 DirectoryUserAdmin userDirectory
= (DirectoryUserAdmin
) ud
;
195 String basePath
= userDirectory
.getBase();
196 if (isSystemRolesBaseDn(basePath
)) {
197 this.systemRoles
= userDirectory
;
198 systemRoles
.setExternalRoles(this);
199 } else if (isTokensBaseDn(basePath
)) {
200 this.tokens
= userDirectory
;
201 tokens
.setExternalRoles(this);
203 LdapName baseDn
= toLdapName(basePath
);
204 if (businessRoles
.containsKey(baseDn
))
205 throw new IllegalStateException("There is already a user admin for " + baseDn
);
206 businessRoles
.put(baseDn
, userDirectory
);
208 userDirectory
.init();
209 postAdd(userDirectory
);
212 /** Called after a new user directory has been added */
213 protected void postAdd(UserDirectory userDirectory
) {
216 private DirectoryUserAdmin
findUserAdmin(String name
) {
218 return findUserAdmin(new LdapName(name
));
219 } catch (InvalidNameException e
) {
220 throw new IllegalArgumentException("Badly formatted name " + name
, e
);
224 private DirectoryUserAdmin
findUserAdmin(LdapName name
) {
225 if (name
.startsWith(systemRolesBaseDn
))
227 if (tokensBaseDn
!= null && name
.startsWith(tokensBaseDn
))
229 List
<DirectoryUserAdmin
> res
= new ArrayList
<>(1);
230 userDirectories
: for (LdapName baseDn
: businessRoles
.keySet()) {
231 DirectoryUserAdmin userDirectory
= businessRoles
.get(baseDn
);
232 if (name
.startsWith(baseDn
)) {
233 if (userDirectory
.isDisabled())
234 continue userDirectories
;
235 // if (res.isEmpty()) {
236 res
.add(userDirectory
);
238 // for (AbstractUserDirectory ud : res) {
239 // LdapName bd = ud.getBaseDn();
240 // if (userDirectory.getBaseDn().startsWith(bd)) {
241 // // child user directory
248 throw new IllegalStateException("Cannot find user admin for " + name
);
250 throw new IllegalStateException("Multiple user admin found for " + name
);
254 protected boolean isSystemRolesBaseDn(String basePath
) {
255 return toLdapName(basePath
).equals(systemRolesBaseDn
);
258 protected boolean isTokensBaseDn(String basePath
) {
259 return tokensBaseDn
!= null && toLdapName(basePath
).equals(tokensBaseDn
);
262 // protected Dictionary<String, Object> currentState() {
263 // Dictionary<String, Object> res = new Hashtable<String, Object>();
264 // // res.put(NodeConstants.CN, NodeConstants.DEFAULT);
265 // for (LdapName name : businessRoles.keySet()) {
266 // AbstractUserDirectory userDirectory = businessRoles.get(name);
267 // String uri = UserAdminConf.propertiesAsUri(userDirectory.getProperties()).toString();
273 public void start() {
274 if (systemRoles
== null) {
275 // TODO do we really need separate system roles?
276 Hashtable
<String
, Object
> properties
= new Hashtable
<>();
277 properties
.put(DirectoryConf
.baseDn
.name(), "ou=roles,ou=system");
278 systemRoles
= new DirectoryUserAdmin(properties
);
283 for (LdapName name
: businessRoles
.keySet()) {
284 DirectoryUserAdmin userDirectory
= businessRoles
.get(name
);
285 destroy(userDirectory
);
287 businessRoles
.clear();
288 businessRoles
= null;
289 destroy(systemRoles
);
293 private void destroy(DirectoryUserAdmin userDirectory
) {
294 preDestroy(userDirectory
);
295 userDirectory
.destroy();
298 // protected void removeUserDirectory(UserDirectory userDirectory) {
299 // LdapName baseDn = toLdapName(userDirectory.getContext());
300 // businessRoles.remove(baseDn);
301 // if (userDirectory instanceof DirectoryUserAdmin)
302 // destroy((DirectoryUserAdmin) userDirectory);
306 protected void removeUserDirectory(String basePath
) {
307 if (isSystemRolesBaseDn(basePath
))
308 throw new IllegalArgumentException("System roles cannot be removed ");
309 LdapName baseDn
= toLdapName(basePath
);
310 if (!businessRoles
.containsKey(baseDn
))
311 throw new IllegalStateException("No user directory registered for " + baseDn
);
312 DirectoryUserAdmin userDirectory
= businessRoles
.remove(baseDn
);
313 destroy(userDirectory
);
317 * Called before each user directory is destroyed, so that additional actions
320 protected void preDestroy(UserDirectory userDirectory
) {
323 public Set
<UserDirectory
> getUserDirectories() {
324 TreeSet
<UserDirectory
> res
= new TreeSet
<>((o1
, o2
) -> o1
.getBase().compareTo(o2
.getBase()));
325 res
.addAll(businessRoles
.values());
326 res
.add(systemRoles
);