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 org.opencastproject.db.DBSession;
25 import org.opencastproject.db.DBSessionFactory;
26 import org.opencastproject.security.api.Group;
27 import org.opencastproject.security.api.GroupProvider;
28 import org.opencastproject.security.api.JaxbGroupList;
29 import org.opencastproject.security.api.JaxbOrganization;
30 import org.opencastproject.security.api.JaxbRole;
31 import org.opencastproject.security.api.OrganizationDirectoryService;
32 import org.opencastproject.security.api.Role;
33 import org.opencastproject.security.api.RoleProvider;
34 import org.opencastproject.security.api.SecurityService;
35 import org.opencastproject.security.api.UnauthorizedException;
36 import org.opencastproject.security.api.UserDirectoryService;
37 import org.opencastproject.security.api.UserProvider;
38 import org.opencastproject.security.impl.jpa.JpaGroup;
39 import org.opencastproject.security.impl.jpa.JpaOrganization;
40 import org.opencastproject.security.impl.jpa.JpaRole;
41 import org.opencastproject.userdirectory.api.AAIRoleProvider;
42 import org.opencastproject.userdirectory.api.GroupRoleProvider;
43 import org.opencastproject.userdirectory.utils.UserDirectoryUtils;
44 import org.opencastproject.util.NotFoundException;
45 import org.opencastproject.util.requests.SortCriterion;
46
47 import org.apache.commons.lang3.StringUtils;
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.io.IOException;
56 import java.util.ArrayList;
57 import java.util.HashSet;
58 import java.util.Iterator;
59 import java.util.List;
60 import java.util.Optional;
61 import java.util.Set;
62 import java.util.regex.Pattern;
63
64 import javax.persistence.EntityManagerFactory;
65
66
67
68
69 @Component(
70 property = {
71 "service.description=Provides a group role directory"
72 },
73 immediate = true,
74 service = { RoleProvider.class, JpaGroupRoleProvider.class }
75 )
76 public class JpaGroupRoleProvider implements AAIRoleProvider, GroupProvider, GroupRoleProvider {
77
78
79 private static final Logger logger = LoggerFactory.getLogger(JpaGroupRoleProvider.class);
80
81
82 public static final String PERSISTENCE_UNIT = "org.opencastproject.common";
83
84
85 protected SecurityService securityService = null;
86
87
88 protected EntityManagerFactory emf = null;
89
90 protected DBSessionFactory dbSessionFactory;
91
92 protected DBSession db;
93
94
95 protected OrganizationDirectoryService organizationDirectoryService;
96
97
98 protected UserDirectoryService userDirectoryService = null;
99
100
101 private ComponentContext cc;
102
103
104 @Reference(target = "(osgi.unit.name=org.opencastproject.common)")
105 public void setEntityManagerFactory(EntityManagerFactory emf) {
106 this.emf = emf;
107 }
108
109 @Reference
110 public void setDBSessionFactory(DBSessionFactory dbSessionFactory) {
111 this.dbSessionFactory = dbSessionFactory;
112 }
113
114
115
116
117
118
119
120 @Reference
121 public void setUserDirectoryService(UserDirectoryService userDirectoryService) {
122 this.userDirectoryService = userDirectoryService;
123 }
124
125
126
127
128
129 @Reference
130 public void setSecurityService(SecurityService securityService) {
131 this.securityService = securityService;
132 }
133
134
135
136
137
138 @Reference
139 public void setOrganizationDirectoryService(OrganizationDirectoryService organizationDirectoryService) {
140 this.organizationDirectoryService = organizationDirectoryService;
141 }
142
143
144
145
146
147
148
149 @Activate
150 public void activate(ComponentContext cc) {
151 logger.debug("Activate group role provider");
152 this.cc = cc;
153 db = dbSessionFactory.createSession(emf);
154 }
155
156
157
158
159
160
161 @Override
162 public Iterator<Role> getRoles() {
163 String orgId = securityService.getOrganization().getId();
164 List<JpaGroup> roles = db.exec(UserDirectoryPersistenceUtil.findGroupsQuery(orgId, 0, 0));
165 return getGroupsRoles(roles).iterator();
166 }
167
168
169
170
171
172
173 @Override
174 public List<Role> getRolesForUser(String userName) {
175 String orgId = securityService.getOrganization().getId();
176 List<JpaGroup> roles = db.exec(UserDirectoryPersistenceUtil.findGroupsByUserQuery(userName, orgId));
177 return getGroupsRoles(roles);
178 }
179
180
181
182
183
184
185 @Override
186 public List<Role> getRolesForGroup(String groupName) {
187 List<Role> roles = new ArrayList<>();
188 String orgId = securityService.getOrganization().getId();
189 Optional<JpaGroup> group = db.exec(UserDirectoryPersistenceUtil.findGroupByRoleQuery(groupName, orgId));
190 if (group.isPresent()) {
191 for (Role role : group.get().getRoles()) {
192 roles.add(new JaxbRole(role.getName(), role.getOrganizationId(), role.getDescription(), Role.Type.DERIVED));
193 }
194 } else {
195 logger.warn("Group {} not found", groupName);
196 }
197 return roles;
198 }
199
200
201
202
203
204
205
206 @Override
207 public String getOrganization() {
208 return UserProvider.ALL_ORGANIZATIONS;
209 }
210
211
212
213
214
215
216 @Override
217 public Iterator<Role> findRoles(String query, Role.Target target, int offset, int limit) {
218 if (query == null) {
219 throw new IllegalArgumentException("Query must be set");
220 }
221 String orgId = securityService.getOrganization().getId();
222
223
224 List<JpaGroup> groups = db.exec(UserDirectoryPersistenceUtil.findGroupsQuery(orgId, 0, 0));
225
226 List<Role> roles = new ArrayList<>();
227 for (JpaGroup group : groups) {
228 if (like(group.getRole(), query)) {
229 roles.add(new JaxbRole(
230 group.getRole(),
231 JaxbOrganization.fromOrganization(group.getOrganization()),
232 "",
233 Role.Type.GROUP
234 ));
235 }
236 }
237
238 Set<Role> result = new HashSet<>();
239 int i = 0;
240 for (Role entry : roles) {
241 if (limit != 0 && result.size() >= limit) {
242 break;
243 }
244 if (i >= offset) {
245 result.add(entry);
246 }
247 i++;
248 }
249 return result.iterator();
250 }
251
252
253
254
255
256
257
258
259
260
261
262 public void updateGroupMembershipFromRoles(String userName, String orgId, List<String> roleList) {
263 updateGroupMembershipFromRoles(userName, orgId, roleList, "");
264 }
265
266
267
268
269
270
271
272
273
274
275
276
277
278 public void updateGroupMembershipFromRoles(String userName, String orgId, List<String> roleList, String prefix) {
279 logger.debug("updateGroupMembershipFromRoles({}, size={})", userName, roleList.size());
280
281
282
283
284 Set<String> membershipRoles = new HashSet<>();
285
286
287 List<JpaGroup> membership = db.exec(UserDirectoryPersistenceUtil.findGroupsByUserQuery(userName, orgId));
288 for (JpaGroup group : membership) {
289 if (StringUtils.isNotBlank(prefix) && !group.getRole().startsWith(prefix)) {
290
291 continue;
292 }
293 if (roleList.contains(group.getRole())) {
294
295 membershipRoles.add(group.getRole());
296 }
297 }
298
299
300 for (String rolename : roleList) {
301 if (!membershipRoles.contains(rolename)) {
302 Optional<JpaGroup> group = db.exec(UserDirectoryPersistenceUtil.findGroupByRoleQuery(rolename, orgId));
303 if (group.isPresent()) {
304 try {
305 logger.debug("Adding user {} to group {}", userName, rolename);
306 group.get().getMembers().add(userName);
307 addGroup(group.get());
308 } catch (UnauthorizedException e) {
309 logger.warn("Unauthorized to add user {} to group {}", userName, group.get().getRole(), e);
310 }
311 } else {
312 logger.warn("Cannot add user {} to group {} - no group found with that role", userName, rolename);
313 }
314 }
315 }
316 }
317
318
319
320
321
322
323
324
325
326
327 public void removeMemberFromAllGroups(String userName, String orgId) {
328
329 List<JpaGroup> membership = db.exec(UserDirectoryPersistenceUtil.findGroupsByUserQuery(userName, orgId));
330 for (JpaGroup group : membership) {
331 try {
332 logger.debug("Removing user {} from group {}", userName, group.getRole());
333 group.getMembers().remove(userName);
334 addGroup(group);
335 } catch (UnauthorizedException e) {
336 logger.warn("Unauthorized to add or remove user {} from group {}", userName, group.getRole(), e);
337 }
338 }
339 }
340
341
342
343
344
345
346
347
348
349
350 public JpaGroup loadGroup(String groupId, String orgId) {
351 return db.exec(UserDirectoryPersistenceUtil.findGroupQuery(groupId, orgId))
352 .orElse(null);
353 }
354
355
356
357
358
359
360
361
362 public JpaGroup getGroup(String groupId) {
363 String orgId = securityService.getOrganization().getId();
364 return loadGroup(groupId, orgId);
365 }
366
367
368
369
370
371
372
373 @Override
374 public void addGroup(final JpaGroup group) throws UnauthorizedException {
375 if (group != null && !UserDirectoryUtils.isCurrentUserAuthorizedHandleRoles(securityService, group.getRoles())) {
376 throw new UnauthorizedException("The user is not allowed to add or update a group with the admin role");
377 }
378
379 Group existingGroup = loadGroup(group.getGroupId(), group.getOrganization().getId());
380 if (existingGroup != null
381 && !UserDirectoryUtils.isCurrentUserAuthorizedHandleRoles(securityService, existingGroup.getRoles())) {
382 throw new UnauthorizedException("The user is not allowed to update a group with the admin role");
383 }
384
385 db.execTx(em -> {
386 Set<JpaRole> roles = UserDirectoryPersistenceUtil.saveRolesQuery(group.getRoles()).apply(em);
387 JpaOrganization organization = UserDirectoryPersistenceUtil.saveOrganizationQuery(group.getOrganization())
388 .apply(em);
389
390 JpaGroup jpaGroup = new JpaGroup(group.getGroupId(), organization, group.getName(), group.getDescription(), roles,
391 group.getMembers());
392
393
394 Optional<JpaGroup> foundGroup = UserDirectoryPersistenceUtil.findGroupQuery(jpaGroup.getGroupId(),
395 jpaGroup.getOrganization().getId()).apply(em);
396 if (foundGroup.isEmpty()) {
397 em.persist(jpaGroup);
398 } else {
399 foundGroup.get().setName(jpaGroup.getName());
400 foundGroup.get().setDescription(jpaGroup.getDescription());
401 foundGroup.get().setMembers(jpaGroup.getMembers());
402 foundGroup.get().setRoles(roles);
403 em.merge(foundGroup.get());
404 }
405 });
406 }
407
408 private void removeGroup(String groupId, String orgId) throws NotFoundException, UnauthorizedException {
409 Group group = loadGroup(groupId, orgId);
410 if (group != null && !UserDirectoryUtils.isCurrentUserAuthorizedHandleRoles(securityService, group.getRoles())) {
411 throw new UnauthorizedException("The user is not allowed to delete a group with the admin role");
412 }
413
414 db.execTxChecked(UserDirectoryPersistenceUtil.removeGroupQuery(groupId, orgId));
415 }
416
417
418
419
420
421
422
423
424 private List<Role> getGroupsRoles(List<JpaGroup> groups) {
425 List<Role> roles = new ArrayList<>();
426 for (Group group : groups) {
427 roles.add(new JaxbRole(
428 group.getRole(),
429 JaxbOrganization.fromOrganization(group.getOrganization()),
430 "",
431 Role.Type.GROUP
432 ));
433 for (Role role : group.getRoles()) {
434 roles.add(new JaxbRole(role.getName(), role.getOrganizationId(), role.getDescription(), Role.Type.DERIVED));
435 }
436 }
437 return roles;
438 }
439
440 public Iterator<Group> getGroups() {
441 String orgId = securityService.getOrganization().getId();
442 return new ArrayList<Group>(db.exec(UserDirectoryPersistenceUtil.findGroupsQuery(orgId, 0, 0)))
443 .iterator();
444 }
445
446 private boolean like(final String str, final String expr) {
447 if (str == null) {
448 return false;
449 }
450 String regex = expr.replace("_", ".").replace("%", ".*?");
451 Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
452 return p.matcher(str).matches();
453 }
454
455
456
457
458
459
460
461
462
463
464
465
466 public JaxbGroupList getGroups(int limit, int offset) throws IOException {
467 if (limit < 1) {
468 limit = 100;
469 }
470 String orgId = securityService.getOrganization().getId();
471 JaxbGroupList groupList = new JaxbGroupList();
472 List<JpaGroup> groups = db.exec(UserDirectoryPersistenceUtil.findGroupsQuery(orgId, limit, offset));
473 for (JpaGroup group : groups) {
474 groupList.add(group);
475 }
476 return groupList;
477 }
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495 public List<JpaGroup> getGroups(Optional<Integer> limit, Optional<Integer> offset, Optional<String> nameFilter,
496 Optional<String> textFilter, ArrayList<SortCriterion> sortCriteria) {
497 String orgId = securityService.getOrganization().getId();
498 return db.exec(UserDirectoryPersistenceUtil.findGroupsQuery(orgId, limit, offset, nameFilter, textFilter,
499 sortCriteria));
500 }
501
502
503
504
505
506
507
508
509
510
511
512 public long countTotalGroups(Optional<String> nameFilter, Optional<String> textFilter) {
513 String orgId = securityService.getOrganization().getId();
514 return db.exec(UserDirectoryPersistenceUtil.countTotalGroupsQuery(orgId, nameFilter, textFilter));
515 }
516
517
518
519
520
521
522
523
524
525
526
527
528
529 public void removeGroup(String groupId) throws NotFoundException, UnauthorizedException, Exception {
530 String orgId = securityService.getOrganization().getId();
531 removeGroup(groupId, orgId);
532 }
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552 public void createGroup(String name, String description, String roles, String users)
553 throws IllegalArgumentException, UnauthorizedException, ConflictException {
554 JpaOrganization organization = (JpaOrganization) securityService.getOrganization();
555
556 HashSet<JpaRole> roleSet = new HashSet<>();
557 if (roles != null) {
558 for (String role : StringUtils.split(roles, ",")) {
559 roleSet.add(new JpaRole(StringUtils.trim(role), organization));
560 }
561 }
562
563 HashSet<String> members = new HashSet<>();
564 if (users != null) {
565 for (String member : StringUtils.split(users, ",")) {
566 members.add(StringUtils.trim(member));
567 }
568 }
569
570 final String groupId = name.toLowerCase().replaceAll("\\W", "_");
571
572 Optional<JpaGroup> existingGroup = db.exec(UserDirectoryPersistenceUtil.findGroupQuery(groupId,
573 organization.getId()));
574 if (existingGroup.isPresent()) {
575 throw new ConflictException("group already exists");
576 }
577
578 addGroup(new JpaGroup(groupId, organization, name, description, roleSet, members));
579 }
580
581
582
583
584
585
586
587
588
589
590
591
592 public boolean removeMemberFromGroup(String groupId, String member) throws NotFoundException, UnauthorizedException {
593 JpaGroup group = getGroup(groupId);
594 if (group == null) {
595 throw new NotFoundException();
596 }
597 Set<String> members = group.getMembers();
598 if (!members.contains(member)) {
599 return false;
600 }
601 group.removeMember(member);
602 userDirectoryService.invalidate(member);
603
604 addGroup(group);
605 return true;
606 }
607
608
609
610
611
612
613
614
615
616
617
618
619 public boolean addMemberToGroup(String groupId, String member) throws NotFoundException, UnauthorizedException {
620 JpaGroup group = getGroup(groupId);
621 if (group == null) {
622 throw new NotFoundException();
623 }
624 Set<String> members = group.getMembers();
625 if (members.contains(member)) {
626 return false;
627 }
628 group.addMember(member);
629 userDirectoryService.invalidate(member);
630
631 addGroup(group);
632 return true;
633 }
634
635
636
637
638
639
640 @Override
641 public void updateGroup(String groupId, String name, String description, String roles, String users)
642 throws NotFoundException, UnauthorizedException {
643 JpaOrganization organization = (JpaOrganization) securityService.getOrganization();
644
645 Optional<JpaGroup> groupOpt = db.exec(UserDirectoryPersistenceUtil.findGroupQuery(groupId, organization.getId()));
646 if (groupOpt.isEmpty()) {
647 throw new NotFoundException();
648 }
649 JpaGroup group = groupOpt.get();
650
651 if (StringUtils.isNotBlank(name)) {
652 group.setName(StringUtils.trim(name));
653 }
654
655 if (StringUtils.isNotBlank(description)) {
656 group.setDescription(StringUtils.trim(description));
657 }
658
659 if (StringUtils.isNotBlank(roles)) {
660 HashSet<JpaRole> roleSet = new HashSet<>();
661 for (String role : StringUtils.split(roles, ",")) {
662 roleSet.add(new JpaRole(StringUtils.trim(role), organization));
663 }
664 group.setRoles(roleSet);
665 } else {
666 group.setRoles(new HashSet<>());
667 }
668
669 if (users != null) {
670 HashSet<String> members = new HashSet<>();
671 HashSet<String> invalidateUsers = new HashSet<>();
672
673 Set<String> groupMembers = group.getMembers();
674
675 for (String member : StringUtils.split(users, ",")) {
676 String newMember = StringUtils.trim(member);
677 members.add(newMember);
678 if (!groupMembers.contains(newMember)) {
679 invalidateUsers.add(newMember);
680 }
681 }
682
683 for (String member : groupMembers) {
684 if (!members.contains(member)) {
685 invalidateUsers.add(member);
686 }
687 }
688
689 group.setMembers(members);
690
691
692 for (String member : invalidateUsers) {
693 userDirectoryService.invalidate(member);
694 }
695 }
696 addGroup(group);
697 }
698 }