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
> rawRoles
= Arrays
.asList(rawAuthorization
.getRoles());
122 List
<String
> allRoles
= new ArrayList
<>(rawRoles
);
123 for (LdapName otherBaseDn
: businessRoles
.keySet()) {
124 if (otherBaseDn
.equals(userReferentialOfThisUser
.getBaseDn()))
126 DirectoryUserAdmin otherUserAdmin
= userAdminToUse(user
, businessRoles
.get(otherBaseDn
));
127 if (otherUserAdmin
== null)
129 for (String roleStr
: rawRoles
) {
130 User role
= (User
) findUserAdmin(roleStr
).getRole(roleStr
);
131 Authorization auth
= otherUserAdmin
.getAuthorization(role
);
132 allRoles
.addAll(Arrays
.asList(auth
.getRoles()));
137 // integrate system roles
138 final DirectoryUserAdmin userAdminToUse
= userAdminToUse(retrievedUser
, userReferentialOfThisUser
);
139 Objects
.requireNonNull(userAdminToUse
);
142 Set
<String
> sysRoles
= new HashSet
<String
>();
143 for (String role
: rawAuthorization
.getRoles()) {
144 User userOrGroup
= (User
) userAdminToUse
.getRole(role
);
145 Authorization auth
= systemRoles
.getAuthorization(userOrGroup
);
146 systemRoles
: for (String systemRole
: auth
.getRoles()) {
147 if (role
.equals(systemRole
))
148 continue systemRoles
;
149 sysRoles
.add(systemRole
);
151 // sysRoles.addAll(Arrays.asList(auth.getRoles()));
153 addAbstractSystemRoles(rawAuthorization
, sysRoles
);
154 Authorization authorization
= new AggregatingAuthorization(usernameToUse
, displayNameToUse
, sysRoles
,
155 allRoles
.toArray(new String
[allRoles
.size()]));
156 return authorization
;
158 if (userAdminToUse
!= null && userAdminToUse
.isScoped()) {
159 userAdminToUse
.destroy();
164 /** Decide whether to scope or not */
165 private DirectoryUserAdmin
userAdminToUse(User user
, DirectoryUserAdmin userAdmin
) {
166 if (userAdmin
.isAuthenticated())
168 if (user
instanceof DirectoryUser
) {
170 } else if (user
instanceof AuthenticatingUser
) {
171 return userAdmin
.scope(user
).orElse(null);
173 throw new IllegalArgumentException("Unsupported user type " + user
.getClass());
179 * Enrich with application-specific roles which are strictly programmatic, such
180 * as anonymous/user semantics.
182 protected void addAbstractSystemRoles(Authorization rawAuthorization
, Set
<String
> sysRoles
) {
187 // USER ADMIN AGGREGATOR
189 protected void addUserDirectory(UserDirectory ud
) {
190 if (!(ud
instanceof DirectoryUserAdmin
))
191 throw new IllegalArgumentException("Only " + DirectoryUserAdmin
.class.getName() + " is supported");
192 DirectoryUserAdmin userDirectory
= (DirectoryUserAdmin
) ud
;
193 String basePath
= userDirectory
.getBase();
194 if (isSystemRolesBaseDn(basePath
)) {
195 this.systemRoles
= userDirectory
;
196 systemRoles
.setExternalRoles(this);
197 } else if (isTokensBaseDn(basePath
)) {
198 this.tokens
= userDirectory
;
199 tokens
.setExternalRoles(this);
201 LdapName baseDn
= toLdapName(basePath
);
202 if (businessRoles
.containsKey(baseDn
))
203 throw new IllegalStateException("There is already a user admin for " + baseDn
);
204 businessRoles
.put(baseDn
, userDirectory
);
206 userDirectory
.init();
207 postAdd(userDirectory
);
210 /** Called after a new user directory has been added */
211 protected void postAdd(UserDirectory userDirectory
) {
214 private DirectoryUserAdmin
findUserAdmin(String name
) {
216 return findUserAdmin(new LdapName(name
));
217 } catch (InvalidNameException e
) {
218 throw new IllegalArgumentException("Badly formatted name " + name
, e
);
222 private DirectoryUserAdmin
findUserAdmin(LdapName name
) {
223 if (name
.startsWith(systemRolesBaseDn
))
225 if (tokensBaseDn
!= null && name
.startsWith(tokensBaseDn
))
227 List
<DirectoryUserAdmin
> res
= new ArrayList
<>(1);
228 userDirectories
: for (LdapName baseDn
: businessRoles
.keySet()) {
229 DirectoryUserAdmin userDirectory
= businessRoles
.get(baseDn
);
230 if (name
.startsWith(baseDn
)) {
231 if (userDirectory
.isDisabled())
232 continue userDirectories
;
233 // if (res.isEmpty()) {
234 res
.add(userDirectory
);
236 // for (AbstractUserDirectory ud : res) {
237 // LdapName bd = ud.getBaseDn();
238 // if (userDirectory.getBaseDn().startsWith(bd)) {
239 // // child user directory
246 throw new IllegalStateException("Cannot find user admin for " + name
);
248 throw new IllegalStateException("Multiple user admin found for " + name
);
252 protected boolean isSystemRolesBaseDn(String basePath
) {
253 return toLdapName(basePath
).equals(systemRolesBaseDn
);
256 protected boolean isTokensBaseDn(String basePath
) {
257 return tokensBaseDn
!= null && toLdapName(basePath
).equals(tokensBaseDn
);
260 // protected Dictionary<String, Object> currentState() {
261 // Dictionary<String, Object> res = new Hashtable<String, Object>();
262 // // res.put(NodeConstants.CN, NodeConstants.DEFAULT);
263 // for (LdapName name : businessRoles.keySet()) {
264 // AbstractUserDirectory userDirectory = businessRoles.get(name);
265 // String uri = UserAdminConf.propertiesAsUri(userDirectory.getProperties()).toString();
271 public void start() {
272 if (systemRoles
== null) {
273 // TODO do we really need separate system roles?
274 Hashtable
<String
, Object
> properties
= new Hashtable
<>();
275 properties
.put(DirectoryConf
.baseDn
.name(), "ou=roles,ou=system");
276 systemRoles
= new DirectoryUserAdmin(properties
);
281 for (LdapName name
: businessRoles
.keySet()) {
282 DirectoryUserAdmin userDirectory
= businessRoles
.get(name
);
283 destroy(userDirectory
);
285 businessRoles
.clear();
286 businessRoles
= null;
287 destroy(systemRoles
);
291 private void destroy(DirectoryUserAdmin userDirectory
) {
292 preDestroy(userDirectory
);
293 userDirectory
.destroy();
296 // protected void removeUserDirectory(UserDirectory userDirectory) {
297 // LdapName baseDn = toLdapName(userDirectory.getContext());
298 // businessRoles.remove(baseDn);
299 // if (userDirectory instanceof DirectoryUserAdmin)
300 // destroy((DirectoryUserAdmin) userDirectory);
304 protected void removeUserDirectory(String basePath
) {
305 if (isSystemRolesBaseDn(basePath
))
306 throw new IllegalArgumentException("System roles cannot be removed ");
307 LdapName baseDn
= toLdapName(basePath
);
308 if (!businessRoles
.containsKey(baseDn
))
309 throw new IllegalStateException("No user directory registered for " + baseDn
);
310 DirectoryUserAdmin userDirectory
= businessRoles
.remove(baseDn
);
311 destroy(userDirectory
);
315 * Called before each user directory is destroyed, so that additional actions
318 protected void preDestroy(UserDirectory userDirectory
) {
321 public Set
<UserDirectory
> getUserDirectories() {
322 TreeSet
<UserDirectory
> res
= new TreeSet
<>((o1
, o2
) -> o1
.getBase().compareTo(o2
.getBase()));
323 res
.addAll(businessRoles
.values());
324 res
.add(systemRoles
);