1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.syncope.core.persistence.jpa.dao;
20
21 import java.time.OffsetDateTime;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
28 import java.util.stream.Collectors;
29 import javax.persistence.NoResultException;
30 import javax.persistence.Query;
31 import javax.persistence.TypedQuery;
32 import org.apache.commons.lang3.tuple.Pair;
33 import org.apache.syncope.common.lib.types.AnyTypeKind;
34 import org.apache.syncope.common.lib.types.IdRepoEntitlement;
35 import org.apache.syncope.core.persistence.api.dao.AnyDAO;
36 import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO;
37 import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
38 import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
39 import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO;
40 import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
41 import org.apache.syncope.core.persistence.api.dao.GroupDAO;
42 import org.apache.syncope.core.persistence.api.dao.PlainAttrDAO;
43 import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
44 import org.apache.syncope.core.persistence.api.dao.UserDAO;
45 import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
46 import org.apache.syncope.core.persistence.api.entity.AnyType;
47 import org.apache.syncope.core.persistence.api.entity.AnyTypeClass;
48 import org.apache.syncope.core.persistence.api.entity.AnyUtils;
49 import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
50 import org.apache.syncope.core.persistence.api.entity.ExternalResource;
51 import org.apache.syncope.core.persistence.api.entity.Realm;
52 import org.apache.syncope.core.persistence.api.entity.anyobject.ADynGroupMembership;
53 import org.apache.syncope.core.persistence.api.entity.anyobject.AMembership;
54 import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject;
55 import org.apache.syncope.core.persistence.api.entity.group.Group;
56 import org.apache.syncope.core.persistence.api.entity.group.TypeExtension;
57 import org.apache.syncope.core.persistence.api.entity.user.UDynGroupMembership;
58 import org.apache.syncope.core.persistence.api.entity.user.UMembership;
59 import org.apache.syncope.core.persistence.api.entity.user.User;
60 import org.apache.syncope.core.persistence.api.search.SearchCondConverter;
61 import org.apache.syncope.core.persistence.api.search.SearchCondVisitor;
62 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership;
63 import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership;
64 import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup;
65 import org.apache.syncope.core.persistence.jpa.entity.group.JPATypeExtension;
66 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUDynGroupMembership;
67 import org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership;
68 import org.apache.syncope.core.provisioning.api.event.EntityLifecycleEvent;
69 import org.apache.syncope.core.provisioning.api.utils.RealmUtils;
70 import org.apache.syncope.core.spring.security.AuthContextUtils;
71 import org.apache.syncope.core.spring.security.DelegatedAdministrationException;
72 import org.identityconnectors.framework.common.objects.SyncDeltaType;
73 import org.springframework.context.ApplicationEventPublisher;
74 import org.springframework.transaction.annotation.Transactional;
75
76 public class JPAGroupDAO extends AbstractAnyDAO<Group> implements GroupDAO {
77
78 public static final String UDYNMEMB_TABLE = "UDynGroupMembers";
79
80 public static final String ADYNMEMB_TABLE = "ADynGroupMembers";
81
82 protected final ApplicationEventPublisher publisher;
83
84 protected final AnyMatchDAO anyMatchDAO;
85
86 protected final PlainAttrDAO plainAttrDAO;
87
88 protected final UserDAO userDAO;
89
90 protected final AnyObjectDAO anyObjectDAO;
91
92 protected final AnySearchDAO anySearchDAO;
93
94 protected final SearchCondVisitor searchCondVisitor;
95
96 public JPAGroupDAO(
97 final AnyUtilsFactory anyUtilsFactory,
98 final ApplicationEventPublisher publisher,
99 final PlainSchemaDAO plainSchemaDAO,
100 final DerSchemaDAO derSchemaDAO,
101 final DynRealmDAO dynRealmDAO,
102 final AnyMatchDAO anyMatchDAO,
103 final PlainAttrDAO plainAttrDAO,
104 final UserDAO userDAO,
105 final AnyObjectDAO anyObjectDAO,
106 final AnySearchDAO searchDAO,
107 final SearchCondVisitor searchCondVisitor) {
108
109 super(anyUtilsFactory, plainSchemaDAO, derSchemaDAO, dynRealmDAO);
110 this.publisher = publisher;
111 this.anyMatchDAO = anyMatchDAO;
112 this.plainAttrDAO = plainAttrDAO;
113 this.userDAO = userDAO;
114 this.anyObjectDAO = anyObjectDAO;
115 this.anySearchDAO = searchDAO;
116 this.searchCondVisitor = searchCondVisitor;
117 }
118
119 @Override
120 protected AnyUtils init() {
121 return anyUtilsFactory.getInstance(AnyTypeKind.GROUP);
122 }
123
124 @Transactional(readOnly = true)
125 @Override
126 public String findKey(final String name) {
127 Query query = entityManager().createNativeQuery("SELECT id FROM " + JPAGroup.TABLE + " WHERE name=?");
128 query.setParameter(1, name);
129
130 String key = null;
131
132 for (Object resultKey : query.getResultList()) {
133 key = resultKey instanceof Object[]
134 ? (String) ((Object[]) resultKey)[0]
135 : ((String) resultKey);
136 }
137
138 return key;
139 }
140
141 @Transactional(readOnly = true)
142 @Override
143 public OffsetDateTime findLastChange(final String key) {
144 return findLastChange(key, JPAGroup.TABLE);
145 }
146
147 @Override
148 public int count() {
149 Query query = entityManager().createQuery(
150 "SELECT COUNT(e) FROM " + anyUtils().anyClass().getSimpleName() + " e");
151 return ((Number) query.getSingleResult()).intValue();
152 }
153
154 @Override
155 public Map<String, Integer> countByRealm() {
156 Query query = entityManager().createQuery(
157 "SELECT e.realm, COUNT(e) FROM " + anyUtils().anyClass().getSimpleName() + " e GROUP BY e.realm");
158
159 @SuppressWarnings("unchecked")
160 List<Object[]> results = query.getResultList();
161 return results.stream().collect(Collectors.toMap(
162 result -> ((Realm) result[0]).getFullPath(),
163 result -> ((Number) result[1]).intValue()));
164 }
165
166 @Transactional(readOnly = true)
167 @Override
168 public void securityChecks(
169 final Set<String> authRealms,
170 final String key,
171 final String realm) {
172
173
174
175 boolean authorized = authRealms.stream().anyMatch(authRealm -> realm.startsWith(authRealm)
176 || authRealm.equals(RealmUtils.getGroupOwnerRealm(realm, key)));
177
178
179 if (!authorized) {
180 authorized = findDynRealms(key).stream().anyMatch(authRealms::contains);
181 }
182
183 if (authRealms.isEmpty() || !authorized) {
184 throw new DelegatedAdministrationException(realm, AnyTypeKind.GROUP.name(), key);
185 }
186 }
187
188 @Override
189 protected void securityChecks(final Group group) {
190 Set<String> authRealms = AuthContextUtils.getAuthorizations().
191 getOrDefault(IdRepoEntitlement.GROUP_READ, Set.of());
192
193 securityChecks(authRealms, group.getKey(), group.getRealm().getFullPath());
194 }
195
196 @Override
197 public Group findByName(final String name) {
198 TypedQuery<Group> query = entityManager().createQuery(
199 "SELECT e FROM " + anyUtils().anyClass().getSimpleName() + " e WHERE e.name = :name", Group.class);
200 query.setParameter("name", name);
201
202 Group result = null;
203 try {
204 result = query.getSingleResult();
205 } catch (NoResultException e) {
206 LOG.debug("No group found with name {}", name, e);
207 }
208
209 return result;
210 }
211
212 @Override
213 public List<String> findKeysByNamePattern(final String pattern) {
214 Query query = entityManager().createNativeQuery(
215 "SELECT id FROM " + JPAGroup.TABLE + " WHERE LOWER(name) LIKE LOWER(?1)");
216 query.setParameter(1, pattern);
217
218 @SuppressWarnings("unchecked")
219 List<Object> raw = query.getResultList();
220 return raw.stream().map(Object::toString).collect(Collectors.toList());
221 }
222
223 @Transactional(readOnly = true)
224 @Override
225 public List<Group> findOwnedByUser(final String userKey) {
226 User owner = userDAO.find(userKey);
227 if (owner == null) {
228 return List.of();
229 }
230
231 StringBuilder queryString = new StringBuilder("SELECT e FROM ").append(anyUtils().anyClass().getSimpleName())
232 .append(" e WHERE e.userOwner=:owner ");
233 userDAO.findAllGroupKeys(owner).forEach(groupKey -> queryString
234 .append("OR e.groupOwner.id='").append(groupKey).append("' "));
235
236 TypedQuery<Group> query = entityManager().createQuery(queryString.toString(), Group.class);
237 query.setParameter("owner", owner);
238
239 return query.getResultList();
240 }
241
242 @Transactional(readOnly = true)
243 @Override
244 public List<Group> findOwnedByGroup(final String groupKey) {
245 Group owner = find(groupKey);
246 if (owner == null) {
247 return List.of();
248 }
249
250 TypedQuery<Group> query = entityManager().createQuery(
251 "SELECT e FROM " + anyUtils().anyClass().getSimpleName() + " e WHERE e.groupOwner=:owner", Group.class);
252 query.setParameter("owner", owner);
253
254 return query.getResultList();
255 }
256
257 @Override
258 public List<AMembership> findAMemberships(final Group group) {
259 TypedQuery<AMembership> query = entityManager().createQuery(
260 "SELECT e FROM " + JPAAMembership.class.getSimpleName() + " e WHERE e.rightEnd=:group",
261 AMembership.class);
262 query.setParameter("group", group);
263
264 return query.getResultList();
265 }
266
267 @Override
268 public List<UMembership> findUMemberships(final Group group) {
269 TypedQuery<UMembership> query = entityManager().createQuery(
270 "SELECT e FROM " + JPAUMembership.class.getSimpleName() + " e WHERE e.rightEnd=:group",
271 UMembership.class);
272 query.setParameter("group", group);
273
274 return query.getResultList();
275 }
276
277 @Override
278 public List<Group> findAll(final int page, final int itemsPerPage) {
279 TypedQuery<Group> query = entityManager().createQuery(
280 "SELECT e FROM " + anyUtils().anyClass().getSimpleName() + " e ORDER BY e.id", Group.class);
281 query.setFirstResult(itemsPerPage * (page <= 0 ? 0 : page - 1));
282 query.setMaxResults(itemsPerPage);
283
284 return query.getResultList();
285 }
286
287 @Override
288 public List<String> findAllKeys(final int page, final int itemsPerPage) {
289 return findAllKeys(JPAGroup.TABLE, page, itemsPerPage);
290 }
291
292 protected SearchCond buildDynMembershipCond(final String baseCondFIQL) {
293 return SearchCondConverter.convert(searchCondVisitor, baseCondFIQL);
294 }
295
296 @Override
297 public Group saveAndRefreshDynMemberships(final Group group) {
298 Group merged = save(group);
299
300
301 clearUDynMembers(merged);
302 if (merged.getUDynMembership() != null) {
303 SearchCond cond = buildDynMembershipCond(merged.getUDynMembership().getFIQLCond());
304 int count = anySearchDAO.count(
305 merged.getRealm(), true, Set.of(merged.getRealm().getFullPath()), cond, AnyTypeKind.USER);
306 for (int page = 1; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
307 List<User> matching = anySearchDAO.search(
308 merged.getRealm(),
309 true,
310 Set.of(merged.getRealm().getFullPath()),
311 cond,
312 page,
313 AnyDAO.DEFAULT_PAGE_SIZE,
314 List.of(),
315 AnyTypeKind.USER);
316
317 matching.forEach(user -> {
318 Query insert = entityManager().createNativeQuery(
319 "INSERT INTO " + UDYNMEMB_TABLE + " VALUES(?, ?)");
320 insert.setParameter(1, user.getKey());
321 insert.setParameter(2, merged.getKey());
322 insert.executeUpdate();
323
324 publisher.publishEvent(
325 new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, user, AuthContextUtils.getDomain()));
326 });
327 }
328 }
329 clearADynMembers(merged);
330 merged.getADynMemberships().forEach(memb -> {
331 SearchCond cond = buildDynMembershipCond(memb.getFIQLCond());
332 int count = anySearchDAO.count(
333 merged.getRealm(), true, Set.of(merged.getRealm().getFullPath()), cond, AnyTypeKind.ANY_OBJECT);
334 for (int page = 1; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE) + 1; page++) {
335 List<AnyObject> matching = anySearchDAO.search(
336 merged.getRealm(),
337 true,
338 Set.of(merged.getRealm().getFullPath()),
339 cond,
340 page,
341 AnyDAO.DEFAULT_PAGE_SIZE,
342 List.of(),
343 AnyTypeKind.ANY_OBJECT);
344
345 matching.forEach(any -> {
346 Query insert = entityManager().createNativeQuery(
347 "INSERT INTO " + ADYNMEMB_TABLE + " VALUES(?, ?, ?)");
348 insert.setParameter(1, any.getType().getKey());
349 insert.setParameter(2, any.getKey());
350 insert.setParameter(3, merged.getKey());
351 insert.executeUpdate();
352
353 publisher.publishEvent(
354 new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, any, AuthContextUtils.getDomain()));
355 });
356 }
357 });
358
359 dynRealmDAO.refreshDynMemberships(merged);
360
361 return merged;
362 }
363
364 @Override
365 public void delete(final Group group) {
366 dynRealmDAO.removeDynMemberships(group.getKey());
367
368 findAMemberships(group).forEach(membership -> {
369 AnyObject leftEnd = membership.getLeftEnd();
370 leftEnd.remove(membership);
371 membership.setRightEnd(null);
372 leftEnd.getPlainAttrs(membership).forEach(attr -> {
373 leftEnd.remove(attr);
374 attr.setOwner(null);
375 attr.setMembership(null);
376 plainAttrDAO.delete(attr);
377 });
378
379 anyObjectDAO.save(leftEnd);
380 publisher.publishEvent(
381 new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, leftEnd, AuthContextUtils.getDomain()));
382 });
383
384 findUMemberships(group).forEach(membership -> {
385 User leftEnd = membership.getLeftEnd();
386 leftEnd.remove(membership);
387 membership.setRightEnd(null);
388 leftEnd.getPlainAttrs(membership).forEach(attr -> {
389 leftEnd.remove(attr);
390 attr.setOwner(null);
391 attr.setMembership(null);
392
393 plainAttrDAO.delete(attr);
394 });
395
396 userDAO.save(leftEnd);
397 publisher.publishEvent(
398 new EntityLifecycleEvent<>(this, SyncDeltaType.UPDATE, leftEnd, AuthContextUtils.getDomain()));
399 });
400
401 clearUDynMembers(group);
402 clearADynMembers(group);
403
404 entityManager().remove(group);
405 }
406
407 @Override
408 public List<TypeExtension> findTypeExtensions(final AnyTypeClass anyTypeClass) {
409 TypedQuery<TypeExtension> query = entityManager().createQuery(
410 "SELECT e FROM " + JPATypeExtension.class.getSimpleName()
411 + " e WHERE :anyTypeClass MEMBER OF e.auxClasses", TypeExtension.class);
412 query.setParameter("anyTypeClass", anyTypeClass);
413
414 return query.getResultList();
415 }
416
417 @Transactional(readOnly = true)
418 @Override
419 public boolean existsAMembership(final String anyObjectKey, final String groupKey) {
420 Query query = entityManager().createNativeQuery(
421 "SELECT COUNT(*) FROM " + JPAAMembership.TABLE + " WHERE group_id=? AND anyobject_it=?");
422 query.setParameter(1, groupKey);
423 query.setParameter(2, anyObjectKey);
424
425 return ((Number) query.getSingleResult()).intValue() > 0;
426 }
427
428 @Transactional(readOnly = true)
429 @Override
430 public boolean existsUMembership(final String userKey, final String groupKey) {
431 Query query = entityManager().createNativeQuery(
432 "SELECT COUNT(*) FROM " + JPAUMembership.TABLE + " WHERE group_id=? AND user_id=?");
433 query.setParameter(1, groupKey);
434 query.setParameter(2, userKey);
435
436 return ((Number) query.getSingleResult()).intValue() > 0;
437 }
438
439 @Transactional(readOnly = true)
440 @Override
441 @SuppressWarnings("unchecked")
442 public List<String> findAMembers(final String groupKey) {
443 Query query = entityManager().createNativeQuery(
444 "SELECT DISTINCT anyObject_id FROM " + JPAAMembership.TABLE + " WHERE group_id=?");
445 query.setParameter(1, groupKey);
446
447 List<String> result = new ArrayList<>();
448 query.getResultList().stream().map(key -> key instanceof Object[]
449 ? (String) ((Object[]) key)[0]
450 : ((String) key)).
451 forEach(item -> result.add((String) item));
452 return result;
453 }
454
455 @Transactional(readOnly = true)
456 @Override
457 @SuppressWarnings("unchecked")
458 public List<String> findUMembers(final String groupKey) {
459 Query query = entityManager().createNativeQuery(
460 "SELECT DISTINCT user_id FROM " + JPAUMembership.TABLE + " WHERE group_id=?");
461 query.setParameter(1, groupKey);
462
463 List<String> result = new ArrayList<>();
464 query.getResultList().stream().map(key -> key instanceof Object[]
465 ? (String) ((Object[]) key)[0]
466 : ((String) key)).
467 forEach(item -> result.add((String) item));
468 return result;
469 }
470
471 @Override
472 @SuppressWarnings("unchecked")
473 public List<String> findADynMembers(final Group group) {
474 List<String> result = new ArrayList<>();
475
476 group.getADynMemberships().forEach(memb -> {
477 Query query = entityManager().createNativeQuery(
478 "SELECT DISTINCT any_id FROM " + ADYNMEMB_TABLE + " WHERE group_id=? AND anyType_id=?");
479 query.setParameter(1, group.getKey());
480 query.setParameter(2, memb.getAnyType().getKey());
481
482 query.getResultList().stream().map(key -> key instanceof Object[]
483 ? (String) ((Object[]) key)[0]
484 : ((String) key)).
485 filter(anyObject -> !result.contains((String) anyObject)).
486 forEach(anyObject -> result.add((String) anyObject));
487 });
488
489 return result;
490 }
491
492 @Override
493 public int countAMembers(final String groupKey) {
494 Query query = entityManager().createNativeQuery(
495 "SELECT COUNT(DISTINCT anyObject_id) FROM " + JPAAMembership.TABLE + " WHERE group_id=?");
496 query.setParameter(1, groupKey);
497
498 return ((Number) query.getSingleResult()).intValue();
499 }
500
501 @Override
502 public int countUMembers(final String groupKey) {
503 Query query = entityManager().createNativeQuery(
504 "SELECT COUNT(DISTINCT user_id) FROM " + JPAUMembership.TABLE + " WHERE group_id=?");
505 query.setParameter(1, groupKey);
506
507 return ((Number) query.getSingleResult()).intValue();
508 }
509
510 @Override
511 public int countADynMembers(final Group group) {
512 Query query = entityManager().createNativeQuery(
513 "SELECT COUNT(DISTINCT any_id) FROM " + ADYNMEMB_TABLE + " WHERE group_id=?");
514 query.setParameter(1, group.getKey());
515
516 return ((Number) query.getSingleResult()).intValue();
517 }
518
519 @Override
520 public int countUDynMembers(final Group group) {
521 if (group.getUDynMembership() == null) {
522 return 0;
523 }
524
525 Query query = entityManager().createNativeQuery(
526 "SELECT COUNT(DISTINCT any_id) FROM " + UDYNMEMB_TABLE + " WHERE group_id=?");
527 query.setParameter(1, group.getKey());
528
529 return ((Number) query.getSingleResult()).intValue();
530 }
531
532 @Override
533 public void clearADynMembers(final Group group) {
534 Query delete = entityManager().createNativeQuery("DELETE FROM " + ADYNMEMB_TABLE + " WHERE group_id=?");
535 delete.setParameter(1, group.getKey());
536 delete.executeUpdate();
537 }
538
539 protected List<ADynGroupMembership> findWithADynMemberships(final AnyType anyType) {
540 TypedQuery<ADynGroupMembership> query = entityManager().createQuery(
541 "SELECT e FROM " + JPAADynGroupMembership.class.getSimpleName() + " e WHERE e.anyType=:anyType",
542 ADynGroupMembership.class);
543 query.setParameter("anyType", anyType);
544 return query.getResultList();
545 }
546
547 @Transactional
548 @Override
549 public Pair<Set<String>, Set<String>> refreshDynMemberships(final AnyObject anyObject) {
550 Set<String> before = new HashSet<>();
551 Set<String> after = new HashSet<>();
552 findWithADynMemberships(anyObject.getType()).forEach(memb -> {
553 boolean matches = anyMatchDAO.matches(anyObject, buildDynMembershipCond(memb.getFIQLCond()));
554 if (matches) {
555 after.add(memb.getGroup().getKey());
556 }
557
558 Query query = entityManager().createNativeQuery(
559 "SELECT COUNT(group_id) FROM " + ADYNMEMB_TABLE + " WHERE group_id=? AND any_id=?");
560 query.setParameter(1, memb.getGroup().getKey());
561 query.setParameter(2, anyObject.getKey());
562 boolean existing = ((Number) query.getSingleResult()).longValue() > 0;
563 if (existing) {
564 before.add(memb.getGroup().getKey());
565 }
566
567 if (matches && !existing) {
568 Query insert = entityManager().createNativeQuery(
569 "INSERT INTO " + ADYNMEMB_TABLE + " VALUES(?, ?, ?)");
570 insert.setParameter(1, anyObject.getType().getKey());
571 insert.setParameter(2, anyObject.getKey());
572 insert.setParameter(3, memb.getGroup().getKey());
573 insert.executeUpdate();
574 } else if (!matches && existing) {
575 Query delete = entityManager().createNativeQuery(
576 "DELETE FROM " + ADYNMEMB_TABLE + " WHERE group_id=? AND any_id=?");
577 delete.setParameter(1, memb.getGroup().getKey());
578 delete.setParameter(2, anyObject.getKey());
579 delete.executeUpdate();
580 }
581
582 publisher.publishEvent(new EntityLifecycleEvent<>(
583 this, SyncDeltaType.UPDATE, memb.getGroup(), AuthContextUtils.getDomain()));
584 });
585
586 return Pair.of(before, after);
587 }
588
589 @Override
590 public Set<String> removeDynMemberships(final AnyObject anyObject) {
591 List<Group> dynGroups = anyObjectDAO.findDynGroups(anyObject.getKey());
592
593 Query delete = entityManager().createNativeQuery("DELETE FROM " + ADYNMEMB_TABLE + " WHERE any_id=?");
594 delete.setParameter(1, anyObject.getKey());
595 delete.executeUpdate();
596
597 Set<String> before = new HashSet<>();
598 dynGroups.forEach(group -> {
599 before.add(group.getKey());
600
601 publisher.publishEvent(new EntityLifecycleEvent<>(
602 this, SyncDeltaType.UPDATE, group, AuthContextUtils.getDomain()));
603 });
604
605 return before;
606 }
607
608 @Override
609 @SuppressWarnings("unchecked")
610 public List<String> findUDynMembers(final Group group) {
611 if (group.getUDynMembership() == null) {
612 return List.of();
613 }
614
615 Query query = entityManager().createNativeQuery(
616 "SELECT DISTINCT any_id FROM " + UDYNMEMB_TABLE + " WHERE group_id=?");
617 query.setParameter(1, group.getKey());
618
619 List<String> result = new ArrayList<>();
620 query.getResultList().stream().map(key -> key instanceof Object[]
621 ? (String) ((Object[]) key)[0]
622 : ((String) key)).
623 forEach(user -> result.add((String) user));
624 return result;
625 }
626
627 @Override
628 public void clearUDynMembers(final Group group) {
629 Query delete = entityManager().createNativeQuery("DELETE FROM " + UDYNMEMB_TABLE + " WHERE group_id=?");
630 delete.setParameter(1, group.getKey());
631 delete.executeUpdate();
632 }
633
634 protected List<UDynGroupMembership> findWithUDynMemberships() {
635 TypedQuery<UDynGroupMembership> query = entityManager().createQuery(
636 "SELECT e FROM " + JPAUDynGroupMembership.class.getSimpleName() + " e",
637 UDynGroupMembership.class);
638
639 return query.getResultList();
640 }
641
642 @Transactional
643 @Override
644 public Pair<Set<String>, Set<String>> refreshDynMemberships(final User user) {
645 Set<String> before = new HashSet<>();
646 Set<String> after = new HashSet<>();
647 findWithUDynMemberships().forEach(memb -> {
648 boolean matches = anyMatchDAO.matches(user, buildDynMembershipCond(memb.getFIQLCond()));
649 if (matches) {
650 after.add(memb.getGroup().getKey());
651 }
652
653 Query query = entityManager().createNativeQuery(
654 "SELECT COUNT(group_id) FROM " + UDYNMEMB_TABLE + " WHERE group_id=? AND any_id=?");
655 query.setParameter(1, memb.getGroup().getKey());
656 query.setParameter(2, user.getKey());
657 boolean existing = ((Number) query.getSingleResult()).longValue() > 0;
658 if (existing) {
659 before.add(memb.getGroup().getKey());
660 }
661
662 if (matches && !existing) {
663 Query insert = entityManager().createNativeQuery(
664 "INSERT INTO " + UDYNMEMB_TABLE + " VALUES(?, ?)");
665 insert.setParameter(1, user.getKey());
666 insert.setParameter(2, memb.getGroup().getKey());
667 insert.executeUpdate();
668 } else if (!matches && existing) {
669 Query delete = entityManager().createNativeQuery(
670 "DELETE FROM " + UDYNMEMB_TABLE + " WHERE group_id=? AND any_id=?");
671 delete.setParameter(1, memb.getGroup().getKey());
672 delete.setParameter(2, user.getKey());
673 delete.executeUpdate();
674 }
675
676 publisher.publishEvent(new EntityLifecycleEvent<>(
677 this, SyncDeltaType.UPDATE, memb.getGroup(), AuthContextUtils.getDomain()));
678 });
679
680 return Pair.of(before, after);
681 }
682
683 @Override
684 public Set<String> removeDynMemberships(final User user) {
685 List<Group> dynGroups = userDAO.findDynGroups(user.getKey());
686
687 Query delete = entityManager().createNativeQuery("DELETE FROM " + UDYNMEMB_TABLE + " WHERE any_id=?");
688 delete.setParameter(1, user.getKey());
689 delete.executeUpdate();
690
691 Set<String> before = new HashSet<>();
692 dynGroups.forEach(group -> {
693 before.add(group.getKey());
694
695 publisher.publishEvent(new EntityLifecycleEvent<>(
696 this, SyncDeltaType.UPDATE, group, AuthContextUtils.getDomain()));
697 });
698
699 return before;
700 }
701
702 @Transactional(readOnly = true)
703 @Override
704 public Collection<String> findAllResourceKeys(final String key) {
705 return find(key).getResources().stream().map(ExternalResource::getKey).collect(Collectors.toList());
706 }
707 }