1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.opencastproject.userdirectory;
23
24 import static org.opencastproject.db.Queries.namedQuery;
25
26 import org.opencastproject.db.DBSession;
27 import org.opencastproject.db.DBSessionFactory;
28 import org.opencastproject.kernel.security.CustomPasswordEncoder;
29 import org.opencastproject.security.api.Group;
30 import org.opencastproject.security.api.Role;
31 import org.opencastproject.security.api.RoleProvider;
32 import org.opencastproject.security.api.SecurityService;
33 import org.opencastproject.security.api.UnauthorizedException;
34 import org.opencastproject.security.api.User;
35 import org.opencastproject.security.api.UserProvider;
36 import org.opencastproject.security.impl.jpa.JpaOrganization;
37 import org.opencastproject.security.impl.jpa.JpaRole;
38 import org.opencastproject.security.impl.jpa.JpaUser;
39 import org.opencastproject.userdirectory.utils.UserDirectoryUtils;
40 import org.opencastproject.util.NotFoundException;
41
42 import com.google.common.cache.CacheBuilder;
43 import com.google.common.cache.CacheLoader;
44 import com.google.common.cache.LoadingCache;
45
46 import org.apache.commons.lang3.StringUtils;
47 import org.apache.commons.lang3.tuple.Pair;
48 import org.osgi.service.component.ComponentContext;
49 import org.osgi.service.component.annotations.Activate;
50 import org.osgi.service.component.annotations.Component;
51 import org.osgi.service.component.annotations.Reference;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54
55 import java.util.ArrayList;
56 import java.util.Collection;
57 import java.util.Collections;
58 import java.util.HashSet;
59 import java.util.Iterator;
60 import java.util.List;
61 import java.util.Optional;
62 import java.util.Set;
63 import java.util.concurrent.TimeUnit;
64 import java.util.stream.Collectors;
65
66 import javax.persistence.EntityManagerFactory;
67
68
69
70
71 @Component(
72 property = {
73 "service.description=Provides a user directory"
74 },
75 immediate = true,
76 service = { UserProvider.class, RoleProvider.class, JpaUserAndRoleProvider.class }
77 )
78 public class JpaUserAndRoleProvider implements UserProvider, RoleProvider {
79
80
81 private static final Logger logger = LoggerFactory.getLogger(JpaUserAndRoleProvider.class);
82
83 public static final String PERSISTENCE_UNIT = "org.opencastproject.common";
84
85
86 public static final String PROVIDER_NAME = "opencast";
87
88
89 public static final String USERNAME = "username";
90
91
92 public static final String ROLES = "roles";
93
94
95 public static final String ENCODING = "UTF-8";
96
97
98 private static final String DELIMITER = ";==;";
99
100
101 protected SecurityService securityService = null;
102
103
104 protected JpaGroupRoleProvider groupRoleProvider;
105
106
107 private LoadingCache<String, Object> cache = null;
108
109
110 protected Object nullToken = new Object();
111
112
113 private CustomPasswordEncoder passwordEncoder = new CustomPasswordEncoder();
114
115
116 protected EntityManagerFactory emf = null;
117
118 protected DBSessionFactory dbSessionFactory;
119
120 protected DBSession db;
121
122
123 @Reference(target = "(osgi.unit.name=org.opencastproject.common)")
124 void setEntityManagerFactory(EntityManagerFactory emf) {
125 this.emf = emf;
126 }
127
128 @Reference
129 public void setDBSessionFactory(DBSessionFactory dbSessionFactory) {
130 this.dbSessionFactory = dbSessionFactory;
131 }
132
133
134
135
136
137 @Reference
138 public void setSecurityService(SecurityService securityService) {
139 this.securityService = securityService;
140 }
141
142
143
144
145
146 @Reference
147 void setGroupRoleProvider(JpaGroupRoleProvider groupRoleProvider) {
148 this.groupRoleProvider = groupRoleProvider;
149 }
150
151
152
153
154
155
156
157 @Activate
158 public void activate(ComponentContext cc) {
159 logger.debug("activate");
160
161
162 cache = CacheBuilder.newBuilder().expireAfterWrite(1, TimeUnit.MINUTES).build(new CacheLoader<>() {
163 @Override
164 public Object load(String id) {
165 String[] key = id.split(DELIMITER);
166 logger.trace("Loading user '{}':'{}' from database", key[0], key[1]);
167 User user = loadUser(key[0], key[1]);
168 return user == null ? nullToken : user;
169 }
170 });
171
172 db = dbSessionFactory.createSession(emf);
173 }
174
175
176
177
178
179
180 @Override
181 public List<Role> getRolesForUser(String userName) {
182 ArrayList<Role> roles = new ArrayList<>();
183 User user = loadUser(userName);
184 if (user == null) {
185 return roles;
186 }
187 roles.addAll(user.getRoles());
188 return roles;
189 }
190
191
192
193
194
195
196 @Override
197 public Iterator<User> findUsers(String query, int offset, int limit) {
198 if (query == null) {
199 throw new IllegalArgumentException("Query must be set");
200 }
201 String orgId = securityService.getOrganization().getId();
202 return db.exec(UserDirectoryPersistenceUtil.findUsersByQuery(orgId, query, limit, offset)).stream()
203 .map(JpaUserAndRoleProvider::addProviderName)
204 .collect(Collectors.toList())
205 .iterator();
206 }
207
208 @Override
209 public Iterator<User> findUsers(Collection<String> userNames) {
210 String orgId = securityService.getOrganization().getId();
211 return db.exec(UserDirectoryPersistenceUtil.findUsersByUserNameQuery(userNames, orgId)).stream()
212 .map(JpaUserAndRoleProvider::addProviderName)
213 .collect(Collectors.toList())
214 .iterator();
215 }
216
217
218
219
220 public List<User> findInsecurePasswordHashes() {
221 final String orgId = securityService.getOrganization().getId();
222 return db.exec(namedQuery.findAll(
223 "User.findInsecureHash",
224 User.class,
225 Pair.of("org", orgId)
226 ));
227 }
228
229
230
231
232
233
234 @Override
235 public Iterator<Role> findRoles(String query, Role.Target target, int offset, int limit) {
236 if (query == null) {
237 throw new IllegalArgumentException("Query must be set");
238 }
239
240
241 return Collections.emptyIterator();
242 }
243
244
245
246
247
248
249 @Override
250 public User loadUser(String userName) {
251 String orgId = securityService.getOrganization().getId();
252 Object user = cache.getUnchecked(userName.concat(DELIMITER).concat(orgId));
253 if (user == nullToken) {
254 return null;
255 } else {
256 return (User) user;
257 }
258 }
259
260 @Override
261 public Iterator<User> getUsers() {
262 String orgId = securityService.getOrganization().getId();
263 return db.exec(UserDirectoryPersistenceUtil.findUsersQuery(orgId, 0, 0)).stream()
264 .map(JpaUserAndRoleProvider::addProviderName)
265 .collect(Collectors.toList())
266 .iterator();
267 }
268
269
270
271
272
273
274 @Override
275 public String getOrganization() {
276 return ALL_ORGANIZATIONS;
277 }
278
279
280
281
282
283
284 @Override
285 public String toString() {
286 return getClass().getName();
287 }
288
289
290
291
292
293
294
295
296
297
298 public User loadUser(String userName, String organization) {
299 return db.exec(UserDirectoryPersistenceUtil.findUserQuery(userName, organization))
300 .map(JpaUserAndRoleProvider::addProviderName)
301 .orElse(null);
302 }
303
304
305
306
307
308
309
310
311
312
313 public User loadUser(long userId, String organization) {
314 return db.exec(UserDirectoryPersistenceUtil.findUserQuery(userId, organization))
315 .map(JpaUserAndRoleProvider::addProviderName)
316 .orElse(null);
317 }
318
319
320
321
322
323
324
325
326
327
328 public void addUser(JpaUser user) throws UnauthorizedException {
329 addUser(user, false);
330 }
331
332
333
334
335
336
337
338
339
340
341
342
343 public void addUser(JpaUser user, final boolean passwordEncoded) throws UnauthorizedException {
344 if (!UserDirectoryUtils.isCurrentUserAuthorizedHandleRoles(securityService, user.getRoles())) {
345 throw new UnauthorizedException("The user is not allowed to set the admin role on other users");
346 }
347
348
349 String encodedPassword = passwordEncoded
350 ? user.getPassword()
351 : passwordEncoder.encodePassword(user.getPassword());
352
353 db.execTx(em -> {
354
355 Set<JpaRole> roles = UserDirectoryPersistenceUtil.saveRolesQuery(filterRoles(user.getRoles())).apply(em);
356 JpaOrganization organization = UserDirectoryPersistenceUtil.saveOrganizationQuery(
357 (JpaOrganization) user.getOrganization()).apply(em);
358
359 JpaUser newUser = new JpaUser(user.getUsername(), encodedPassword, organization, user.getName(), user.getEmail(),
360 user.getProvider(), user.isManageable(), roles);
361
362
363 em.persist(newUser);
364 cache.put(user.getUsername() + DELIMITER + user.getOrganization().getId(), newUser);
365 });
366 updateGroupMembership(user);
367 }
368
369
370
371
372
373
374
375
376
377
378 public User updateUser(JpaUser user) throws NotFoundException, UnauthorizedException {
379 return updateUser(user, false);
380 }
381
382
383
384
385
386
387
388
389
390
391
392
393 public User updateUser(JpaUser user, final boolean passwordEncoded) throws NotFoundException, UnauthorizedException {
394 if (!UserDirectoryUtils.isCurrentUserAuthorizedHandleRoles(securityService, user.getRoles())) {
395 throw new UnauthorizedException("The user is not allowed to set the admin role on other users");
396 }
397
398 try {
399 return db.execTxChecked(em -> {
400 Optional<JpaUser> updateUser = UserDirectoryPersistenceUtil.findUserQuery(user.getUsername(),
401 user.getOrganization().getId()).apply(em);
402 if (updateUser.isEmpty()) {
403 throw new NotFoundException("User " + user.getUsername() + " not found.");
404 }
405
406 logger.debug("updateUser({})", user.getUsername());
407
408 if (!UserDirectoryUtils.isCurrentUserAuthorizedHandleRoles(securityService, updateUser.get().getRoles())) {
409 throw new UnauthorizedException("The user is not allowed to update an admin user");
410 }
411
412 String encodedPassword;
413
414 if (StringUtils.isEmpty(user.getPassword())) {
415 encodedPassword = updateUser.get().getPassword();
416 } else {
417
418 if (passwordEncoded) {
419 encodedPassword = user.getPassword();
420 } else {
421 encodedPassword = passwordEncoder.encodePassword(user.getPassword());
422 }
423 }
424
425
426 Set<JpaRole> roles = UserDirectoryPersistenceUtil.saveRolesQuery(filterRoles(user.getRoles())).apply(em);
427 JpaOrganization organization = UserDirectoryPersistenceUtil.saveOrganizationQuery(
428 (JpaOrganization) user.getOrganization()).apply(em);
429
430 JpaUser updatedUser = UserDirectoryPersistenceUtil.saveUserQuery(
431 new JpaUser(user.getUsername(), encodedPassword, organization, user.getName(), user.getEmail(), user
432 .getProvider(), true, roles)).apply(em);
433 cache.put(user.getUsername() + DELIMITER + organization.getId(), updatedUser);
434
435 updateGroupMembership(user);
436
437 return updatedUser;
438 });
439 } catch (NotFoundException | UnauthorizedException | RuntimeException e) {
440 throw e;
441 } catch (Exception e) {
442 throw new IllegalStateException(e);
443 }
444 }
445
446
447
448
449
450
451
452 private Set<JpaRole> filterRoles(Set<Role> userRoles) {
453 Set<JpaRole> roles = new HashSet<>();
454 for (Role role : userRoles) {
455 if (Role.Type.INTERNAL.equals(role.getType()) && !role.getName().startsWith(Group.ROLE_PREFIX)) {
456 JpaRole jpaRole = (JpaRole) role;
457 roles.add(jpaRole);
458 }
459 }
460 return roles;
461 }
462
463
464
465
466
467
468
469 private void updateGroupMembership(JpaUser user) {
470 logger.debug("updateGroupMembership({}, roles={})", user.getUsername(), user.getRoles().size());
471 List<String> internalGroupRoles = new ArrayList<>();
472
473 for (Role role : user.getRoles()) {
474 if (Role.Type.GROUP.equals(role.getType())
475 || (Role.Type.INTERNAL.equals(role.getType()) && role.getName().startsWith(Group.ROLE_PREFIX))) {
476 internalGroupRoles.add(role.getName());
477 }
478 }
479
480 groupRoleProvider.updateGroupMembershipFromRoles(
481 user.getUsername(),
482 user.getOrganization().getId(),
483 internalGroupRoles
484 );
485 }
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500 public void deleteUser(String username, String orgId) throws NotFoundException, UnauthorizedException, Exception {
501 User user = loadUser(username, orgId);
502 if (user != null && !UserDirectoryUtils.isCurrentUserAuthorizedHandleRoles(securityService, user.getRoles())) {
503 throw new UnauthorizedException("The user is not allowed to delete an admin user");
504 }
505
506
507 groupRoleProvider.removeMemberFromAllGroups(username, orgId);
508
509
510 db.execTxChecked(UserDirectoryPersistenceUtil.deleteUserQuery(username, orgId));
511
512 cache.invalidate(username + DELIMITER + orgId);
513 }
514
515
516
517
518
519
520
521 public void addRole(JpaRole jpaRole) {
522 HashSet<JpaRole> roles = new HashSet<>();
523 roles.add(jpaRole);
524 db.execTx(UserDirectoryPersistenceUtil.saveRolesQuery(roles));
525 }
526
527 @Override
528 public String getName() {
529 return PROVIDER_NAME;
530 }
531
532 private static User addProviderName(JpaUser u) {
533 u.setProvider(PROVIDER_NAME);
534 return u;
535 }
536
537 @Override
538 public long countUsers() {
539 String orgId = securityService.getOrganization().getId();
540 return db.exec(UserDirectoryPersistenceUtil.countUsersQuery(orgId));
541 }
542
543
544
545
546
547
548 public long countAllUsers() {
549 return db.exec(UserDirectoryPersistenceUtil.countUsersQuery());
550 }
551
552 @Override
553 public void invalidate(String userName) {
554 String orgId = securityService.getOrganization().getId();
555 cache.invalidate(userName + DELIMITER + orgId);
556 }
557 }