[SYNCOPE-1806] Fixing all dynamic memberships refresh: Realms, Groups and Roles
diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractXMLContentExporter.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractXMLContentExporter.java
index cd8cf70..6984245 100644
--- a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractXMLContentExporter.java
+++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/content/AbstractXMLContentExporter.java
@@ -18,8 +18,6 @@
*/
package org.apache.syncope.core.persistence.common.content;
-import static org.apache.syncope.core.persistence.api.content.ContentDealer.ROOT_ELEMENT;
-
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import javax.xml.XMLConstants;
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DynRealmRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DynRealmRepoExtImpl.java
index 96b5289..0b3e29f 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DynRealmRepoExtImpl.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/DynRealmRepoExtImpl.java
@@ -158,10 +158,11 @@
boolean matches = anyMatchDAO.matches(
any, SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond()));
- Query find = entityManager.createNativeQuery(
- "SELECT dynRealm_id FROM " + DYNMEMB_TABLE + " WHERE any_id=?");
- find.setParameter(1, any.getKey());
- boolean existing = !find.getResultList().isEmpty();
+ Query query = entityManager.createNativeQuery(
+ "SELECT COUNT(dynRealm_id) FROM " + DYNMEMB_TABLE + " WHERE any_id=? AND dynRealm_id=?");
+ query.setParameter(1, any.getKey());
+ query.setParameter(2, dynRealm.getKey());
+ boolean existing = ((Number) query.getSingleResult()).longValue() > 0;
if (matches && !existing) {
Query insert = entityManager.
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java
index 271358b..1fccd2b 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/GroupRepoExtImpl.java
@@ -448,11 +448,11 @@
after.add(memb.getGroup().getKey());
}
- Query find = entityManager.createNativeQuery(
- "SELECT any_id FROM " + ADYNMEMB_TABLE + " WHERE group_id=? AND any_id=?");
- find.setParameter(1, memb.getGroup().getKey());
- find.setParameter(2, anyObject.getKey());
- boolean existing = !find.getResultList().isEmpty();
+ Query query = entityManager.createNativeQuery(
+ "SELECT COUNT(group_id) FROM " + ADYNMEMB_TABLE + " WHERE group_id=? AND any_id=?");
+ query.setParameter(1, memb.getGroup().getKey());
+ query.setParameter(2, anyObject.getKey());
+ boolean existing = ((Number) query.getSingleResult()).longValue() > 0;
if (existing) {
before.add(memb.getGroup().getKey());
}
@@ -518,11 +518,11 @@
after.add(memb.getGroup().getKey());
}
- Query find = entityManager.createNativeQuery(
- "SELECT any_id FROM " + UDYNMEMB_TABLE + " WHERE group_id=? AND any_id=?");
- find.setParameter(1, memb.getGroup().getKey());
- find.setParameter(2, user.getKey());
- boolean existing = !find.getResultList().isEmpty();
+ Query query = entityManager.createNativeQuery(
+ "SELECT COUNT(group_id) FROM " + UDYNMEMB_TABLE + " WHERE group_id=? AND any_id=?");
+ query.setParameter(1, memb.getGroup().getKey());
+ query.setParameter(2, user.getKey());
+ boolean existing = ((Number) query.getSingleResult()).longValue() > 0;
if (existing) {
before.add(memb.getGroup().getKey());
}
diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExtImpl.java
index c7362e7..a3e94a6 100644
--- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExtImpl.java
+++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/RoleRepoExtImpl.java
@@ -146,16 +146,16 @@
public void refreshDynMemberships(final User user) {
entityManager.createQuery(
"SELECT e FROM " + JPARole.class.getSimpleName() + " e "
- + "WHERE e.dynMembershipCond IS NOT NULL", Role.class).
- getResultStream().forEach(role -> {
+ + "WHERE e.dynMembershipCond IS NOT NULL", Role.class).getResultStream().forEach(role -> {
boolean matches = anyMatchDAO.matches(
user,
SearchCondConverter.convert(searchCondVisitor, role.getDynMembershipCond()));
- Query find = entityManager.createNativeQuery(
- "SELECT any_id FROM " + DYNMEMB_TABLE + " WHERE role_id=?");
- find.setParameter(1, role.getKey());
- boolean existing = !find.getResultList().isEmpty();
+ Query query = entityManager.createNativeQuery(
+ "SELECT COUNT(role_id) FROM " + DYNMEMB_TABLE + " WHERE any_id=? AND role_id=?");
+ query.setParameter(1, user.getKey());
+ query.setParameter(2, role.getKey());
+ boolean existing = ((Number) query.getSingleResult()).longValue() > 0;
if (matches && !existing) {
Query insert = entityManager.createNativeQuery(
diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java
index 30eb0a6..ace3537 100644
--- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java
+++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/DynRealmTest.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.persistence.jpa.outer;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -25,10 +26,12 @@
import java.util.List;
import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager;
import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO;
import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
@@ -58,6 +61,12 @@
@Autowired
private UserDAO userDAO;
+ @Autowired
+ private PlainSchemaDAO plainSchemaDAO;
+
+ @Autowired
+ private PlainAttrValidationManager plainAttrValidationManager;
+
@Test
public void misc() {
DynRealm dynRealm = entityFactory.newEntity(DynRealm.class);
@@ -95,4 +104,66 @@
assertTrue(userDAO.findDynRealms(user.getKey()).contains(actual.getKey()));
}
+
+ @Test
+ public void issueSYNCOPE1806() {
+ // 1. create two dyn realms with same condition
+ DynRealm realm1 = entityFactory.newEntity(DynRealm.class);
+ realm1.setKey("realm1");
+
+ DynRealmMembership memb1 = entityFactory.newEntity(DynRealmMembership.class);
+ memb1.setDynRealm(realm1);
+ memb1.setAnyType(anyTypeDAO.getUser());
+ memb1.setFIQLCond("cool==true");
+
+ realm1.add(memb1);
+ memb1.setDynRealm(realm1);
+
+ realm1 = dynRealmDAO.saveAndRefreshDynMemberships(realm1);
+
+ DynRealm realm2 = entityFactory.newEntity(DynRealm.class);
+ realm2.setKey("realm2");
+
+ DynRealmMembership memb2 = entityFactory.newEntity(DynRealmMembership.class);
+ memb2.setDynRealm(realm2);
+ memb2.setAnyType(anyTypeDAO.getUser());
+ memb2.setFIQLCond("cool==true");
+
+ realm2.add(memb2);
+ memb2.setDynRealm(realm2);
+
+ realm2 = dynRealmDAO.saveAndRefreshDynMemberships(realm2);
+
+ entityManager.flush();
+
+ // 2. verify that dynamic members are the same
+ DynRealmCond dynRealmCond1 = new DynRealmCond();
+ dynRealmCond1.setDynRealm(realm1.getKey());
+ List<User> matching1 = searchDAO.search(SearchCond.getLeaf(dynRealmCond1), AnyTypeKind.USER);
+
+ DynRealmCond dynRealmCond2 = new DynRealmCond();
+ dynRealmCond2.setDynRealm(realm2.getKey());
+ List<User> matching2 = searchDAO.search(SearchCond.getLeaf(dynRealmCond2), AnyTypeKind.USER);
+
+ assertEquals(matching1, matching2);
+ assertEquals(1, matching1.size());
+ assertTrue(matching1.stream().anyMatch(u -> "c9b2dec2-00a7-4855-97c0-d854842b4b24".equals(u.getKey())));
+
+ // 3. update an user to let them become part of both dyn realms
+ anyUtilsFactory.getInstance(AnyTypeKind.USER).addAttr(
+ plainAttrValidationManager,
+ "823074dc-d280-436d-a7dd-07399fae48ec",
+ plainSchemaDAO.findById("cool").orElseThrow(),
+ "true");
+
+ entityManager.flush();
+
+ // 4. verify that dynamic members are still the same
+ matching1 = searchDAO.search(SearchCond.getLeaf(dynRealmCond1), AnyTypeKind.USER);
+ matching2 = searchDAO.search(SearchCond.getLeaf(dynRealmCond2), AnyTypeKind.USER);
+ assertEquals(matching1, matching2);
+ assertEquals(2, matching1.size());
+ assertTrue(matching1.stream().anyMatch(u -> "c9b2dec2-00a7-4855-97c0-d854842b4b24".equals(u.getKey())));
+ assertTrue(matching1.stream().anyMatch(u -> "823074dc-d280-436d-a7dd-07399fae48ec".equals(u.getKey())));
+ }
}
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepoExtImpl.java
index ecfb245..e3f20ac 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepoExtImpl.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/DynRealmRepoExtImpl.java
@@ -179,9 +179,9 @@
any, SearchCondConverter.convert(searchCondVisitor, memb.getFIQLCond()));
boolean existing = neo4jTemplate.count(
- "MATCH (n {id: $id})-[:" + DYN_REALM_MEMBERSHIP_REL + "]-(p:" + Neo4jDynRealm.NODE + ") "
+ "MATCH (n {id: $aid})-[:" + DYN_REALM_MEMBERSHIP_REL + "]-(p:" + Neo4jDynRealm.NODE + "{id: $pid}) "
+ "RETURN COUNT(p)",
- Map.of("id", any.getKey())) > 0;
+ Map.of("aid", any.getKey(), "pid", dynRealm.getKey())) > 0;
if (matches && !existing) {
neo4jClient.query(
diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepoExtImpl.java
index 4a2c595..4bab38e 100644
--- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepoExtImpl.java
+++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/RoleRepoExtImpl.java
@@ -173,9 +173,10 @@
SearchCondConverter.convert(searchCondVisitor, role.getDynMembershipCond()));
boolean existing = neo4jTemplate.count(
- "MATCH (n)-[:" + DYN_ROLE_MEMBERSHIP_REL + "]-(p:" + Neo4jRole.NODE + "{id: $id}) "
+ "MATCH (n:" + Neo4jUser.NODE + " {id: $aid})-[:" + DYN_ROLE_MEMBERSHIP_REL + "]-"
+ + "(p:" + Neo4jRole.NODE + "{id: $pid}) "
+ "RETURN COUNT(n)",
- Map.of("id", role.getKey())) > 0;
+ Map.of("aid", user.getKey(), "pid", role.getKey())) > 0;
if (matches && !existing) {
neo4jClient.query(
diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DynRealmTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DynRealmTest.java
index 3f56088..272b2bd 100644
--- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DynRealmTest.java
+++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/DynRealmTest.java
@@ -18,6 +18,7 @@
*/
package org.apache.syncope.core.persistence.neo4j.outer;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -25,10 +26,12 @@
import java.util.List;
import org.apache.syncope.common.lib.types.AnyTypeKind;
+import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager;
import org.apache.syncope.core.persistence.api.dao.AnyMatchDAO;
import org.apache.syncope.core.persistence.api.dao.AnySearchDAO;
import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO;
import org.apache.syncope.core.persistence.api.dao.DynRealmDAO;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond;
import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
@@ -58,6 +61,12 @@
@Autowired
private UserDAO userDAO;
+ @Autowired
+ private PlainSchemaDAO plainSchemaDAO;
+
+ @Autowired
+ private PlainAttrValidationManager plainAttrValidationManager;
+
@Test
public void misc() {
DynRealm dynRealm = entityFactory.newEntity(DynRealm.class);
@@ -93,4 +102,62 @@
assertTrue(userDAO.findDynRealms(user.getKey()).contains(actual.getKey()));
}
+
+ @Test
+ public void issueSYNCOPE1806() {
+ // 1. create two dyn realms with same condition
+ DynRealm realm1 = entityFactory.newEntity(DynRealm.class);
+ realm1.setKey("realm1");
+
+ DynRealmMembership memb1 = entityFactory.newEntity(DynRealmMembership.class);
+ memb1.setDynRealm(realm1);
+ memb1.setAnyType(anyTypeDAO.getUser());
+ memb1.setFIQLCond("cool==true");
+
+ realm1.add(memb1);
+ memb1.setDynRealm(realm1);
+
+ realm1 = dynRealmDAO.saveAndRefreshDynMemberships(realm1);
+
+ DynRealm realm2 = entityFactory.newEntity(DynRealm.class);
+ realm2.setKey("realm2");
+
+ DynRealmMembership memb2 = entityFactory.newEntity(DynRealmMembership.class);
+ memb2.setDynRealm(realm2);
+ memb2.setAnyType(anyTypeDAO.getUser());
+ memb2.setFIQLCond("cool==true");
+
+ realm2.add(memb2);
+ memb2.setDynRealm(realm2);
+
+ realm2 = dynRealmDAO.saveAndRefreshDynMemberships(realm2);
+
+ // 2. verify that dynamic members are the same
+ DynRealmCond dynRealmCond1 = new DynRealmCond();
+ dynRealmCond1.setDynRealm(realm1.getKey());
+ List<User> matching1 = searchDAO.search(SearchCond.getLeaf(dynRealmCond1), AnyTypeKind.USER);
+
+ DynRealmCond dynRealmCond2 = new DynRealmCond();
+ dynRealmCond2.setDynRealm(realm2.getKey());
+ List<User> matching2 = searchDAO.search(SearchCond.getLeaf(dynRealmCond2), AnyTypeKind.USER);
+
+ assertEquals(matching1, matching2);
+ assertEquals(1, matching1.size());
+ assertTrue(matching1.stream().anyMatch(u -> "c9b2dec2-00a7-4855-97c0-d854842b4b24".equals(u.getKey())));
+
+ // 3. update an user to let them become part of both dyn realms
+ anyUtilsFactory.getInstance(AnyTypeKind.USER).addAttr(
+ plainAttrValidationManager,
+ "823074dc-d280-436d-a7dd-07399fae48ec",
+ plainSchemaDAO.findById("cool").orElseThrow(),
+ "true");
+
+ // 4. verify that dynamic members are still the same
+ matching1 = searchDAO.search(SearchCond.getLeaf(dynRealmCond1), AnyTypeKind.USER);
+ matching2 = searchDAO.search(SearchCond.getLeaf(dynRealmCond2), AnyTypeKind.USER);
+ assertEquals(matching1, matching2);
+ assertEquals(2, matching1.size());
+ assertTrue(matching1.stream().anyMatch(u -> "c9b2dec2-00a7-4855-97c0-d854842b4b24".equals(u.getKey())));
+ assertTrue(matching1.stream().anyMatch(u -> "823074dc-d280-436d-a7dd-07399fae48ec".equals(u.getKey())));
+ }
}
diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
index 42b9cd3..8dd7ab1 100644
--- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
+++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/DynRealmITCase.java
@@ -36,6 +36,7 @@
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import org.apache.syncope.client.lib.SyncopeClient;
+import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.SyncopeClientException;
import org.apache.syncope.common.lib.SyncopeConstants;
import org.apache.syncope.common.lib.request.AttrPatch;
@@ -252,7 +253,6 @@
dynRealm = new DynRealmTO();
dynRealm.setKey("name" + getUUIDString());
dynRealm.getDynMembershipConds().put(AnyTypeKind.USER.name(), "ctype==" + ctype);
- DYN_REALM_SERVICE.create(dynRealm);
Response response = DYN_REALM_SERVICE.create(dynRealm);
dynRealm = getObject(response.getLocation(), DynRealmService.class, DynRealmTO.class);
@@ -313,4 +313,60 @@
}
}
}
+
+ @Test
+ public void issueSYNCOPE1806() {
+ DynRealmTO realm1 = null;
+ DynRealmTO realm2 = null;
+ try {
+ // 1. create two dyn realms with same condition
+ realm1 = new DynRealmTO();
+ realm1.setKey("realm1");
+ realm1.getDynMembershipConds().put(AnyTypeKind.USER.name(), "cool==true");
+ realm1 = getObject(DYN_REALM_SERVICE.create(realm1).getLocation(), DynRealmService.class, DynRealmTO.class);
+ assertNotNull(realm1);
+
+ realm2 = new DynRealmTO();
+ realm2.setKey("realm2");
+ realm2.getDynMembershipConds().put(AnyTypeKind.USER.name(), "cool==true");
+ realm2 = getObject(DYN_REALM_SERVICE.create(realm2).getLocation(), DynRealmService.class, DynRealmTO.class);
+ assertNotNull(realm2);
+
+ // 2. verify that dynamic members are the same
+ PagedResult<UserTO> matching1 = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
+ SyncopeClient.getUserSearchConditionBuilder().inDynRealms(realm1.getKey()).query()).build());
+ PagedResult<UserTO> matching2 = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
+ SyncopeClient.getUserSearchConditionBuilder().inDynRealms(realm2.getKey()).query()).build());
+
+ assertEquals(matching1, matching2);
+ assertEquals(1, matching1.getResult().size());
+ assertTrue(matching1.getResult().stream().
+ anyMatch(u -> "c9b2dec2-00a7-4855-97c0-d854842b4b24".equals(u.getKey())));
+
+ // 3. update an user to let them become part of both dyn realms
+ UserUR userUR = new UserUR();
+ userUR.setKey("823074dc-d280-436d-a7dd-07399fae48ec");
+ userUR.getPlainAttrs().add(new AttrPatch.Builder(new Attr.Builder("cool").value("true").build()).build());
+ updateUser(userUR);
+
+ // 4. verify that dynamic members are still the same
+ matching1 = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
+ SyncopeClient.getUserSearchConditionBuilder().inDynRealms(realm1.getKey()).query()).build());
+ matching2 = USER_SERVICE.search(new AnyQuery.Builder().realm("/").fiql(
+ SyncopeClient.getUserSearchConditionBuilder().inDynRealms(realm2.getKey()).query()).build());
+ assertEquals(matching1, matching2);
+ assertEquals(2, matching1.getResult().size());
+ assertTrue(matching1.getResult().stream().
+ anyMatch(u -> "c9b2dec2-00a7-4855-97c0-d854842b4b24".equals(u.getKey())));
+ assertTrue(matching1.getResult().stream().
+ anyMatch(u -> "823074dc-d280-436d-a7dd-07399fae48ec".equals(u.getKey())));
+ } finally {
+ if (realm1 != null) {
+ DYN_REALM_SERVICE.delete(realm1.getKey());
+ }
+ if (realm2 != null) {
+ DYN_REALM_SERVICE.delete(realm2.getKey());
+ }
+ }
+ }
}