1 package org
.argeo
.osgi
.useradmin
;
3 import static org
.argeo
.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
.util
.directory
.DirectoryConf
;
20 import org
.osgi
.framework
.InvalidSyntaxException
;
21 import org
.osgi
.service
.useradmin
.Authorization
;
22 import org
.osgi
.service
.useradmin
.Group
;
23 import org
.osgi
.service
.useradmin
.Role
;
24 import org
.osgi
.service
.useradmin
.User
;
25 import org
.osgi
.service
.useradmin
.UserAdmin
;
28 * Aggregates multiple {@link UserDirectory} and integrates them with system
31 public class AggregatingUserAdmin
implements UserAdmin
{
32 private final LdapName systemRolesBaseDn
;
33 private final LdapName tokensBaseDn
;
36 private DirectoryUserAdmin systemRoles
= null;
37 private DirectoryUserAdmin tokens
= null;
38 private Map
<LdapName
, DirectoryUserAdmin
> businessRoles
= new HashMap
<LdapName
, DirectoryUserAdmin
>();
40 // TODO rather use an empty constructor and an init method
41 public AggregatingUserAdmin(String systemRolesBaseDn
, String tokensBaseDn
) {
43 this.systemRolesBaseDn
= new LdapName(systemRolesBaseDn
);
44 if (tokensBaseDn
!= null)
45 this.tokensBaseDn
= new LdapName(tokensBaseDn
);
47 this.tokensBaseDn
= null;
48 } catch (InvalidNameException e
) {
49 throw new IllegalStateException("Cannot initialize " + AggregatingUserAdmin
.class, e
);
54 public Role
createRole(String name
, int type
) {
55 return findUserAdmin(name
).createRole(name
, type
);
59 public boolean removeRole(String name
) {
60 boolean actuallyDeleted
= findUserAdmin(name
).removeRole(name
);
61 systemRoles
.removeRole(name
);
62 return actuallyDeleted
;
66 public Role
getRole(String name
) {
67 return findUserAdmin(name
).getRole(name
);
71 public Role
[] getRoles(String filter
) throws InvalidSyntaxException
{
72 List
<Role
> res
= new ArrayList
<Role
>();
73 for (UserAdmin userAdmin
: businessRoles
.values()) {
74 res
.addAll(Arrays
.asList(userAdmin
.getRoles(filter
)));
76 res
.addAll(Arrays
.asList(systemRoles
.getRoles(filter
)));
77 return res
.toArray(new Role
[res
.size()]);
81 public User
getUser(String key
, String value
) {
82 List
<User
> res
= new ArrayList
<User
>();
83 for (UserAdmin userAdmin
: businessRoles
.values()) {
84 User u
= userAdmin
.getUser(key
, value
);
88 // Note: node roles cannot contain users, so it is not searched
89 return res
.size() == 1 ? res
.get(0) : null;
92 /** Builds an authorisation by scanning all referentials. */
94 public Authorization
getAuthorization(User user
) {
95 if (user
== null) {// anonymous
96 return systemRoles
.getAuthorization(null);
98 DirectoryUserAdmin userReferentialOfThisUser
= findUserAdmin(user
.getName());
99 Authorization rawAuthorization
= userReferentialOfThisUser
.getAuthorization(user
);
100 User retrievedUser
= (User
) userReferentialOfThisUser
.getRole(user
.getName());
101 String usernameToUse
;
102 String displayNameToUse
;
103 if (user
instanceof Group
) {
104 // TODO check whether this is still working
105 String ownerDn
= TokenUtils
.userDn((Group
) user
);
106 if (ownerDn
!= null) {// tokens
107 UserAdmin ownerUserAdmin
= findUserAdmin(ownerDn
);
108 User ownerUser
= (User
) ownerUserAdmin
.getRole(ownerDn
);
109 usernameToUse
= ownerDn
;
110 displayNameToUse
= LdifAuthorization
.extractDisplayName(ownerUser
);
112 usernameToUse
= rawAuthorization
.getName();
113 displayNameToUse
= rawAuthorization
.toString();
115 } else {// regular users
116 usernameToUse
= rawAuthorization
.getName();
117 displayNameToUse
= rawAuthorization
.toString();
120 // gather roles from other referentials
121 List
<String
> allRoles
= new ArrayList
<>(Arrays
.asList(rawAuthorization
.getRoles()));
122 for (LdapName otherBaseDn
: businessRoles
.keySet()) {
123 if (otherBaseDn
.equals(userReferentialOfThisUser
.getBaseDn()))
125 DirectoryUserAdmin otherUserAdmin
= userAdminToUse(user
, businessRoles
.get(otherBaseDn
));
126 if (otherUserAdmin
== null)
128 Authorization auth
= otherUserAdmin
.getAuthorization(retrievedUser
);
129 allRoles
.addAll(Arrays
.asList(auth
.getRoles()));
133 // integrate system roles
134 final DirectoryUserAdmin userAdminToUse
= userAdminToUse(retrievedUser
, userReferentialOfThisUser
);
135 Objects
.requireNonNull(userAdminToUse
);
138 Set
<String
> sysRoles
= new HashSet
<String
>();
139 for (String role
: rawAuthorization
.getRoles()) {
140 User userOrGroup
= (User
) userAdminToUse
.getRole(role
);
141 Authorization auth
= systemRoles
.getAuthorization(userOrGroup
);
142 systemRoles
: for (String systemRole
: auth
.getRoles()) {
143 if (role
.equals(systemRole
))
144 continue systemRoles
;
145 sysRoles
.add(systemRole
);
147 // sysRoles.addAll(Arrays.asList(auth.getRoles()));
149 addAbstractSystemRoles(rawAuthorization
, sysRoles
);
150 Authorization authorization
= new AggregatingAuthorization(usernameToUse
, displayNameToUse
, sysRoles
,
151 allRoles
.toArray(new String
[allRoles
.size()]));
152 return authorization
;
154 if (userAdminToUse
!= null && userAdminToUse
.isScoped()) {
155 userAdminToUse
.destroy();
160 /** Decide whether to scope or not */
161 private DirectoryUserAdmin
userAdminToUse(User user
, DirectoryUserAdmin userAdmin
) {
162 if (user
instanceof DirectoryUser
) {
164 } else if (user
instanceof AuthenticatingUser
) {
165 return userAdmin
.scope(user
).orElse(null);
167 throw new IllegalArgumentException("Unsupported user type " + user
.getClass());
173 * Enrich with application-specific roles which are strictly programmatic, such
174 * as anonymous/user semantics.
176 protected void addAbstractSystemRoles(Authorization rawAuthorization
, Set
<String
> sysRoles
) {
181 // USER ADMIN AGGREGATOR
183 protected void addUserDirectory(UserDirectory ud
) {
184 if (!(ud
instanceof DirectoryUserAdmin
))
185 throw new IllegalArgumentException("Only " + DirectoryUserAdmin
.class.getName() + " is supported");
186 DirectoryUserAdmin userDirectory
= (DirectoryUserAdmin
) ud
;
187 String basePath
= userDirectory
.getBase();
188 if (isSystemRolesBaseDn(basePath
)) {
189 this.systemRoles
= userDirectory
;
190 systemRoles
.setExternalRoles(this);
191 } else if (isTokensBaseDn(basePath
)) {
192 this.tokens
= userDirectory
;
193 tokens
.setExternalRoles(this);
195 LdapName baseDn
= toLdapName(basePath
);
196 if (businessRoles
.containsKey(baseDn
))
197 throw new IllegalStateException("There is already a user admin for " + baseDn
);
198 businessRoles
.put(baseDn
, userDirectory
);
200 userDirectory
.init();
201 postAdd(userDirectory
);
204 /** Called after a new user directory has been added */
205 protected void postAdd(UserDirectory userDirectory
) {
208 private DirectoryUserAdmin
findUserAdmin(String name
) {
210 return findUserAdmin(new LdapName(name
));
211 } catch (InvalidNameException e
) {
212 throw new IllegalArgumentException("Badly formatted name " + name
, e
);
216 private DirectoryUserAdmin
findUserAdmin(LdapName name
) {
217 if (name
.startsWith(systemRolesBaseDn
))
219 if (tokensBaseDn
!= null && name
.startsWith(tokensBaseDn
))
221 List
<DirectoryUserAdmin
> res
= new ArrayList
<>(1);
222 userDirectories
: for (LdapName baseDn
: businessRoles
.keySet()) {
223 DirectoryUserAdmin userDirectory
= businessRoles
.get(baseDn
);
224 if (name
.startsWith(baseDn
)) {
225 if (userDirectory
.isDisabled())
226 continue userDirectories
;
227 // if (res.isEmpty()) {
228 res
.add(userDirectory
);
230 // for (AbstractUserDirectory ud : res) {
231 // LdapName bd = ud.getBaseDn();
232 // if (userDirectory.getBaseDn().startsWith(bd)) {
233 // // child user directory
240 throw new IllegalStateException("Cannot find user admin for " + name
);
242 throw new IllegalStateException("Multiple user admin found for " + name
);
246 protected boolean isSystemRolesBaseDn(String basePath
) {
247 return toLdapName(basePath
).equals(systemRolesBaseDn
);
250 protected boolean isTokensBaseDn(String basePath
) {
251 return tokensBaseDn
!= null && toLdapName(basePath
).equals(tokensBaseDn
);
254 // protected Dictionary<String, Object> currentState() {
255 // Dictionary<String, Object> res = new Hashtable<String, Object>();
256 // // res.put(NodeConstants.CN, NodeConstants.DEFAULT);
257 // for (LdapName name : businessRoles.keySet()) {
258 // AbstractUserDirectory userDirectory = businessRoles.get(name);
259 // String uri = UserAdminConf.propertiesAsUri(userDirectory.getProperties()).toString();
265 public void start() {
266 if (systemRoles
== null) {
267 // TODO do we really need separate system roles?
268 Hashtable
<String
, Object
> properties
= new Hashtable
<>();
269 properties
.put(DirectoryConf
.baseDn
.name(), "ou=roles,ou=system");
270 systemRoles
= new DirectoryUserAdmin(properties
);
275 for (LdapName name
: businessRoles
.keySet()) {
276 DirectoryUserAdmin userDirectory
= businessRoles
.get(name
);
277 destroy(userDirectory
);
279 businessRoles
.clear();
280 businessRoles
= null;
281 destroy(systemRoles
);
285 private void destroy(DirectoryUserAdmin userDirectory
) {
286 preDestroy(userDirectory
);
287 userDirectory
.destroy();
290 // protected void removeUserDirectory(UserDirectory userDirectory) {
291 // LdapName baseDn = toLdapName(userDirectory.getContext());
292 // businessRoles.remove(baseDn);
293 // if (userDirectory instanceof DirectoryUserAdmin)
294 // destroy((DirectoryUserAdmin) userDirectory);
298 protected void removeUserDirectory(String basePath
) {
299 if (isSystemRolesBaseDn(basePath
))
300 throw new IllegalArgumentException("System roles cannot be removed ");
301 LdapName baseDn
= toLdapName(basePath
);
302 if (!businessRoles
.containsKey(baseDn
))
303 throw new IllegalStateException("No user directory registered for " + baseDn
);
304 DirectoryUserAdmin userDirectory
= businessRoles
.remove(baseDn
);
305 destroy(userDirectory
);
309 * Called before each user directory is destroyed, so that additional actions
312 protected void preDestroy(UserDirectory userDirectory
) {
315 public Set
<UserDirectory
> getUserDirectories() {
316 TreeSet
<UserDirectory
> res
= new TreeSet
<>((o1
, o2
) -> o1
.getBase().compareTo(o2
.getBase()));
317 res
.addAll(businessRoles
.values());
318 res
.add(systemRoles
);