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