]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.util/src/org/argeo/osgi/useradmin/AggregatingUserAdmin.java
Revert start level changes
[lgpl/argeo-commons.git] / org.argeo.util / src / org / argeo / osgi / 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> allRoles = new ArrayList<>(Arrays.asList(rawAuthorization.getRoles()));
122 for (LdapName otherBaseDn : businessRoles.keySet()) {
123 if (otherBaseDn.equals(userReferentialOfThisUser.getBaseDn()))
124 continue;
125 DirectoryUserAdmin otherUserAdmin = userAdminToUse(user, businessRoles.get(otherBaseDn));
126 if (otherUserAdmin == null)
127 continue;
128 Authorization auth = otherUserAdmin.getAuthorization(retrievedUser);
129 allRoles.addAll(Arrays.asList(auth.getRoles()));
130
131 }
132
133 // integrate system roles
134 final DirectoryUserAdmin userAdminToUse = userAdminToUse(retrievedUser, userReferentialOfThisUser);
135 Objects.requireNonNull(userAdminToUse);
136
137 try {
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);
146 }
147 // sysRoles.addAll(Arrays.asList(auth.getRoles()));
148 }
149 addAbstractSystemRoles(rawAuthorization, sysRoles);
150 Authorization authorization = new AggregatingAuthorization(usernameToUse, displayNameToUse, sysRoles,
151 allRoles.toArray(new String[allRoles.size()]));
152 return authorization;
153 } finally {
154 if (userAdminToUse != null && userAdminToUse.isScoped()) {
155 userAdminToUse.destroy();
156 }
157 }
158 }
159
160 /** Decide whether to scope or not */
161 private DirectoryUserAdmin userAdminToUse(User user, DirectoryUserAdmin userAdmin) {
162 if (user instanceof DirectoryUser) {
163 return userAdmin;
164 } else if (user instanceof AuthenticatingUser) {
165 return userAdmin.scope(user).orElse(null);
166 } else {
167 throw new IllegalArgumentException("Unsupported user type " + user.getClass());
168 }
169
170 }
171
172 /**
173 * Enrich with application-specific roles which are strictly programmatic, such
174 * as anonymous/user semantics.
175 */
176 protected void addAbstractSystemRoles(Authorization rawAuthorization, Set<String> sysRoles) {
177
178 }
179
180 //
181 // USER ADMIN AGGREGATOR
182 //
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);
194 } else {
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);
199 }
200 userDirectory.init();
201 postAdd(userDirectory);
202 }
203
204 /** Called after a new user directory has been added */
205 protected void postAdd(UserDirectory userDirectory) {
206 }
207
208 private DirectoryUserAdmin findUserAdmin(String name) {
209 try {
210 return findUserAdmin(new LdapName(name));
211 } catch (InvalidNameException e) {
212 throw new IllegalArgumentException("Badly formatted name " + name, e);
213 }
214 }
215
216 private DirectoryUserAdmin findUserAdmin(LdapName name) {
217 if (name.startsWith(systemRolesBaseDn))
218 return systemRoles;
219 if (tokensBaseDn != null && name.startsWith(tokensBaseDn))
220 return tokens;
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);
229 // } else {
230 // for (AbstractUserDirectory ud : res) {
231 // LdapName bd = ud.getBaseDn();
232 // if (userDirectory.getBaseDn().startsWith(bd)) {
233 // // child user directory
234 // }
235 // }
236 // }
237 }
238 }
239 if (res.size() == 0)
240 throw new IllegalStateException("Cannot find user admin for " + name);
241 if (res.size() > 1)
242 throw new IllegalStateException("Multiple user admin found for " + name);
243 return res.get(0);
244 }
245
246 protected boolean isSystemRolesBaseDn(String basePath) {
247 return toLdapName(basePath).equals(systemRolesBaseDn);
248 }
249
250 protected boolean isTokensBaseDn(String basePath) {
251 return tokensBaseDn != null && toLdapName(basePath).equals(tokensBaseDn);
252 }
253
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();
260 // res.put(uri, "");
261 // }
262 // return res;
263 // }
264
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);
271 }
272 }
273
274 public void stop() {
275 for (LdapName name : businessRoles.keySet()) {
276 DirectoryUserAdmin userDirectory = businessRoles.get(name);
277 destroy(userDirectory);
278 }
279 businessRoles.clear();
280 businessRoles = null;
281 destroy(systemRoles);
282 systemRoles = null;
283 }
284
285 private void destroy(DirectoryUserAdmin userDirectory) {
286 preDestroy(userDirectory);
287 userDirectory.destroy();
288 }
289
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);
295 // }
296
297 @Deprecated
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);
306 }
307
308 /**
309 * Called before each user directory is destroyed, so that additional actions
310 * can be performed.
311 */
312 protected void preDestroy(UserDirectory userDirectory) {
313 }
314
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);
319 return res;
320 }
321
322 }