]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/osgi/useradmin/AggregatingUserAdmin.java
Improve ACR attribute typing.
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / osgi / useradmin / AggregatingUserAdmin.java
1 package org.argeo.cms.osgi.useradmin;
2
3 import static org.argeo.cms.osgi.useradmin.DirectoryUserAdmin.toLdapName;
4
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;
11 import java.util.Map;
12 import java.util.Objects;
13 import java.util.Set;
14 import java.util.TreeSet;
15
16 import javax.naming.InvalidNameException;
17 import javax.naming.ldap.LdapName;
18
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;
28
29 /**
30 * Aggregates multiple {@link UserDirectory} and integrates them with system
31 * roles.
32 */
33 public class AggregatingUserAdmin implements UserAdmin {
34 private final LdapName systemRolesBaseDn;
35 private final LdapName tokensBaseDn;
36
37 // DAOs
38 private DirectoryUserAdmin systemRoles = null;
39 private DirectoryUserAdmin tokens = null;
40 private Map<LdapName, DirectoryUserAdmin> businessRoles = new HashMap<LdapName, DirectoryUserAdmin>();
41
42 // TODO rather use an empty constructor and an init method
43 public AggregatingUserAdmin(String systemRolesBaseDn, String tokensBaseDn) {
44 try {
45 this.systemRolesBaseDn = new LdapName(systemRolesBaseDn);
46 if (tokensBaseDn != null)
47 this.tokensBaseDn = new LdapName(tokensBaseDn);
48 else
49 this.tokensBaseDn = null;
50 } catch (InvalidNameException e) {
51 throw new IllegalStateException("Cannot initialize " + AggregatingUserAdmin.class, e);
52 }
53 }
54
55 @Override
56 public Role createRole(String name, int type) {
57 return findUserAdmin(name).createRole(name, type);
58 }
59
60 @Override
61 public boolean removeRole(String name) {
62 boolean actuallyDeleted = findUserAdmin(name).removeRole(name);
63 systemRoles.removeRole(name);
64 return actuallyDeleted;
65 }
66
67 @Override
68 public Role getRole(String name) {
69 return findUserAdmin(name).getRole(name);
70 }
71
72 @Override
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)));
77 }
78 res.addAll(Arrays.asList(systemRoles.getRoles(filter)));
79 return res.toArray(new Role[res.size()]);
80 }
81
82 @Override
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);
87 if (u != null)
88 res.add(u);
89 }
90 // Note: node roles cannot contain users, so it is not searched
91 return res.size() == 1 ? res.get(0) : null;
92 }
93
94 /** Builds an authorisation by scanning all referentials. */
95 @Override
96 public Authorization getAuthorization(User user) {
97 if (user == null) {// anonymous
98 return systemRoles.getAuthorization(null);
99 }
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);
113 } else {
114 usernameToUse = rawAuthorization.getName();
115 displayNameToUse = rawAuthorization.toString();
116 }
117 } else {// regular users
118 usernameToUse = rawAuthorization.getName();
119 displayNameToUse = rawAuthorization.toString();
120 }
121
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()))
127 continue;
128 DirectoryUserAdmin otherUserAdmin = userAdminToUse(user, businessRoles.get(otherBaseDn));
129 if (otherUserAdmin == null)
130 continue;
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()));
135 }
136
137 }
138
139 // integrate system roles
140 final DirectoryUserAdmin userAdminToUse = userAdminToUse(retrievedUser, userReferentialOfThisUser);
141 Objects.requireNonNull(userAdminToUse);
142
143 try {
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);
152 }
153 // sysRoles.addAll(Arrays.asList(auth.getRoles()));
154 }
155 addAbstractSystemRoles(rawAuthorization, sysRoles);
156 Authorization authorization = new AggregatingAuthorization(usernameToUse, displayNameToUse, sysRoles,
157 allRoles.toArray(new String[allRoles.size()]));
158 return authorization;
159 } finally {
160 if (userAdminToUse != null && userAdminToUse.isScoped()) {
161 userAdminToUse.destroy();
162 }
163 }
164 }
165
166 /** Decide whether to scope or not */
167 private DirectoryUserAdmin userAdminToUse(User user, DirectoryUserAdmin userAdmin) {
168 if (userAdmin.isAuthenticated())
169 return userAdmin;
170 if (user instanceof CmsUser) {
171 return userAdmin;
172 } else if (user instanceof AuthenticatingUser) {
173 return userAdmin.scope(user).orElse(null);
174 } else {
175 throw new IllegalArgumentException("Unsupported user type " + user.getClass());
176 }
177
178 }
179
180 /**
181 * Enrich with application-specific roles which are strictly programmatic, such
182 * as anonymous/user semantics.
183 */
184 protected void addAbstractSystemRoles(Authorization rawAuthorization, Set<String> sysRoles) {
185
186 }
187
188 //
189 // USER ADMIN AGGREGATOR
190 //
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);
202 } else {
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);
207 }
208 userDirectory.init();
209 postAdd(userDirectory);
210 }
211
212 /** Called after a new user directory has been added */
213 protected void postAdd(UserDirectory userDirectory) {
214 }
215
216 private DirectoryUserAdmin findUserAdmin(String name) {
217 try {
218 return findUserAdmin(new LdapName(name));
219 } catch (InvalidNameException e) {
220 throw new IllegalArgumentException("Badly formatted name " + name, e);
221 }
222 }
223
224 private DirectoryUserAdmin findUserAdmin(LdapName name) {
225 if (name.startsWith(systemRolesBaseDn))
226 return systemRoles;
227 if (tokensBaseDn != null && name.startsWith(tokensBaseDn))
228 return tokens;
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);
237 // } else {
238 // for (AbstractUserDirectory ud : res) {
239 // LdapName bd = ud.getBaseDn();
240 // if (userDirectory.getBaseDn().startsWith(bd)) {
241 // // child user directory
242 // }
243 // }
244 // }
245 }
246 }
247 if (res.size() == 0)
248 throw new IllegalStateException("Cannot find user admin for " + name);
249 if (res.size() > 1)
250 throw new IllegalStateException("Multiple user admin found for " + name);
251 return res.get(0);
252 }
253
254 protected boolean isSystemRolesBaseDn(String basePath) {
255 return toLdapName(basePath).equals(systemRolesBaseDn);
256 }
257
258 protected boolean isTokensBaseDn(String basePath) {
259 return tokensBaseDn != null && toLdapName(basePath).equals(tokensBaseDn);
260 }
261
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();
268 // res.put(uri, "");
269 // }
270 // return res;
271 // }
272
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);
279 }
280 }
281
282 public void stop() {
283 for (LdapName name : businessRoles.keySet()) {
284 DirectoryUserAdmin userDirectory = businessRoles.get(name);
285 destroy(userDirectory);
286 }
287 businessRoles.clear();
288 businessRoles = null;
289 destroy(systemRoles);
290 systemRoles = null;
291 }
292
293 private void destroy(DirectoryUserAdmin userDirectory) {
294 preDestroy(userDirectory);
295 userDirectory.destroy();
296 }
297
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);
303 // }
304
305 @Deprecated
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);
314 }
315
316 /**
317 * Called before each user directory is destroyed, so that additional actions
318 * can be performed.
319 */
320 protected void preDestroy(UserDirectory userDirectory) {
321 }
322
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);
327 return res;
328 }
329
330 }