diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java index 4c20d651c9..f075239bcd 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/RealmLogic.java @@ -46,8 +46,8 @@ import org.apache.syncope.core.persistence.api.dao.search.AttrCond; import org.apache.syncope.core.persistence.api.dao.search.SearchCond; import org.apache.syncope.core.persistence.api.entity.Realm; -import org.apache.syncope.core.persistence.api.search.SyncopePage; import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.api.search.SyncopePage; import org.apache.syncope.core.provisioning.api.PropagationByResource; import org.apache.syncope.core.provisioning.api.data.RealmDataBinder; import org.apache.syncope.core.provisioning.api.propagation.PropagationManager; @@ -219,7 +219,7 @@ public ProvisioningResult delete(final String fullPath) { Set adminRealms = Set.of(realm.getFullPath()); AnyCond keyCond = new AnyCond(AttrCond.Type.ISNOTNULL); keyCond.setSchema("key"); - SearchCond allMatchingCond = SearchCond.getLeaf(keyCond); + SearchCond allMatchingCond = SearchCond.of(keyCond); long users = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.USER); long groups = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.GROUP); long anyObjects = searchDAO.count(realm, true, adminRealms, allMatchingCond, AnyTypeKind.ANY_OBJECT); diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java index 566c0d04ac..645ed3a794 100644 --- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java +++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/SyncopeLogic.java @@ -114,7 +114,7 @@ public Page searchAssignableGroups( termCond = new AnyCond(AttrCond.Type.ISNOTNULL); termCond.setSchema("key"); } - SearchCond searchCond = SearchCond.getLeaf(termCond); + SearchCond searchCond = SearchCond.of(termCond); long count = anySearchDAO.count(base, true, SyncopeConstants.FULL_ADMIN_REALMS, searchCond, AnyTypeKind.GROUP); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java index 61d8c62664..cb46e5d156 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/AnyDAO.java @@ -28,9 +28,6 @@ import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.DerSchema; import org.apache.syncope.core.persistence.api.entity.ExternalResource; -import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; -import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.Schema; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -45,11 +42,6 @@ public interface AnyDAO> extends DAO { A authFind(String key); - List findByPlainAttrValue(PlainSchema schema, PlainAttrValue attrValue, boolean ignoreCaseMatch); - - Optional findByPlainAttrUniqueValue( - PlainSchema schema, PlainAttrUniqueValue attrUniqueValue, boolean ignoreCaseMatch); - /** * Find any objects by derived attribute value. This method could fail if one or more string literals contained * into the derived attribute value provided derive from identifier (schema key) replacement. When you are going to @@ -73,7 +65,7 @@ Optional findByPlainAttrUniqueValue( default SearchCond getAllMatchingCond() { AnyCond idCond = new AnyCond(AttrCond.Type.ISNOTNULL); idCond.setSchema("id"); - return SearchCond.getLeaf(idCond); + return SearchCond.of(idCond); } AllowedSchemas findAllowedSchemas(A any, Class reference); diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java index 8e3ce96334..c0467c13b4 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/dao/search/SearchCond.java @@ -18,6 +18,7 @@ */ package org.apache.syncope.core.persistence.api.dao.search; +import java.util.Arrays; import java.util.List; import java.util.Optional; import org.apache.commons.lang3.builder.EqualsBuilder; @@ -45,10 +46,10 @@ public enum Type { private SearchCond right; - public static SearchCond getLeaf(final AbstractSearchCond leaf) { + public static SearchCond of(final AbstractSearchCond leaf) { SearchCond cond; - if (leaf instanceof SearchCond) { - cond = (SearchCond) leaf; + if (leaf instanceof SearchCond searchCond) { + cond = searchCond; } else { cond = new SearchCond(); cond.leaf = leaf; @@ -59,15 +60,15 @@ public static SearchCond getLeaf(final AbstractSearchCond leaf) { return cond; } - public static SearchCond getNotLeaf(final AbstractSearchCond leaf) { - SearchCond cond = getLeaf(leaf); + public static SearchCond negate(final AbstractSearchCond leaf) { + SearchCond cond = of(leaf); cond.type = Type.NOT_LEAF; return cond; } - public static SearchCond getAnd(final SearchCond left, final SearchCond right) { + private static SearchCond and(final SearchCond left, final SearchCond right) { SearchCond cond = new SearchCond(); cond.type = Type.AND; @@ -77,17 +78,21 @@ public static SearchCond getAnd(final SearchCond left, final SearchCond right) { return cond; } - public static SearchCond getAnd(final List conditions) { + public static SearchCond and(final List conditions) { if (conditions.size() == 1) { return conditions.get(0); } else if (conditions.size() > 2) { - return getAnd(conditions.get(0), getAnd(conditions.subList(1, conditions.size()))); + return and(conditions.get(0), and(conditions.subList(1, conditions.size()))); } else { - return getAnd(conditions.get(0), conditions.get(1)); + return and(conditions.get(0), conditions.get(1)); } } - public static SearchCond getOr(final SearchCond left, final SearchCond right) { + public static SearchCond and(final SearchCond... conditions) { + return and(Arrays.asList(conditions)); + } + + private static SearchCond or(final SearchCond left, final SearchCond right) { SearchCond cond = new SearchCond(); cond.type = Type.OR; @@ -97,16 +102,20 @@ public static SearchCond getOr(final SearchCond left, final SearchCond right) { return cond; } - public static SearchCond getOr(final List conditions) { + public static SearchCond or(final List conditions) { if (conditions.size() == 1) { return conditions.get(0); } else if (conditions.size() > 2) { - return getOr(conditions.get(0), getOr(conditions.subList(1, conditions.size()))); + return or(conditions.get(0), or(conditions.subList(1, conditions.size()))); } else { - return getOr(conditions.get(0), conditions.get(1)); + return or(conditions.get(0), conditions.get(1)); } } + public static SearchCond or(final SearchCond... conditions) { + return or(Arrays.asList(conditions)); + } + public Optional getAnyTypeCond() { return Optional.ofNullable(leaf instanceof AnyTypeCond ? (AnyTypeCond) leaf : null); } @@ -126,8 +135,8 @@ public String hasAnyTypeCond() { switch (type) { case LEAF: case NOT_LEAF: - if (leaf instanceof AnyTypeCond) { - anyTypeName = ((AnyTypeCond) leaf).getAnyTypeKey(); + if (leaf instanceof AnyTypeCond anyTypeCond) { + anyTypeName = anyTypeCond.getAnyTypeKey(); } break; @@ -148,7 +157,7 @@ public String hasAnyTypeCond() { } @SuppressWarnings("unchecked") - public Optional getLeaf(final Class clazz) { + public Optional asLeaf(final Class clazz) { return Optional.ofNullable((T) (clazz.isInstance(leaf) ? leaf : null)); } diff --git a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java index e6bf206f65..3df4e60e37 100644 --- a/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java +++ b/core/persistence-api/src/main/java/org/apache/syncope/core/persistence/api/search/SearchCondVisitor.java @@ -134,67 +134,67 @@ protected SearchCond visitPrimitive(final SearchCondition sc) { : AttrCond.Type.LIKE); } - leaf = SearchCond.getLeaf(attrCond); + leaf = SearchCond.of(attrCond); } else { switch (specialAttrName.get()) { case TYPE: AnyTypeCond typeCond = new AnyTypeCond(); typeCond.setAnyTypeKey(value); - leaf = SearchCond.getLeaf(typeCond); + leaf = SearchCond.of(typeCond); break; case AUX_CLASSES: AuxClassCond auxClassCond = new AuxClassCond(); auxClassCond.setAuxClass(value); - leaf = SearchCond.getLeaf(auxClassCond); + leaf = SearchCond.of(auxClassCond); break; case RESOURCES: ResourceCond resourceCond = new ResourceCond(); resourceCond.setResource(value); - leaf = SearchCond.getLeaf(resourceCond); + leaf = SearchCond.of(resourceCond); break; case GROUPS: MembershipCond groupCond = new MembershipCond(); groupCond.setGroup(value); - leaf = SearchCond.getLeaf(groupCond); + leaf = SearchCond.of(groupCond); break; case RELATIONSHIPS: RelationshipCond relationshipCond = new RelationshipCond(); relationshipCond.setAnyObject(value); - leaf = SearchCond.getLeaf(relationshipCond); + leaf = SearchCond.of(relationshipCond); break; case RELATIONSHIP_TYPES: RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond(); relationshipTypeCond.setRelationshipTypeKey(value); - leaf = SearchCond.getLeaf(relationshipTypeCond); + leaf = SearchCond.of(relationshipTypeCond); break; case ROLES: RoleCond roleCond = new RoleCond(); roleCond.setRole(value); - leaf = SearchCond.getLeaf(roleCond); + leaf = SearchCond.of(roleCond); break; case PRIVILEGES: PrivilegeCond privilegeCond = new PrivilegeCond(); privilegeCond.setPrivilege(value); - leaf = SearchCond.getLeaf(privilegeCond); + leaf = SearchCond.of(privilegeCond); break; case DYNREALMS: DynRealmCond dynRealmCond = new DynRealmCond(); dynRealmCond.setDynRealm(value); - leaf = SearchCond.getLeaf(dynRealmCond); + leaf = SearchCond.of(dynRealmCond); break; case MEMBER: MemberCond memberCond = new MemberCond(); memberCond.setMember(value); - leaf = SearchCond.getLeaf(memberCond); + leaf = SearchCond.of(memberCond); break; default: @@ -203,33 +203,33 @@ protected SearchCond visitPrimitive(final SearchCondition sc) { } } if (ct == ConditionType.NOT_EQUALS) { - Optional notEquals = leaf.getLeaf(AttrCond.class); + Optional notEquals = leaf.asLeaf(AttrCond.class); if (notEquals.isPresent() && notEquals.get().getType() == AttrCond.Type.ISNULL) { notEquals.get().setType(AttrCond.Type.ISNOTNULL); } else { - leaf = SearchCond.getNotLeaf(leaf); + leaf = SearchCond.negate(leaf); } } break; case GREATER_OR_EQUALS: attrCond.setType(AttrCond.Type.GE); - leaf = SearchCond.getLeaf(attrCond); + leaf = SearchCond.of(attrCond); break; case GREATER_THAN: attrCond.setType(AttrCond.Type.GT); - leaf = SearchCond.getLeaf(attrCond); + leaf = SearchCond.of(attrCond); break; case LESS_OR_EQUALS: attrCond.setType(AttrCond.Type.LE); - leaf = SearchCond.getLeaf(attrCond); + leaf = SearchCond.of(attrCond); break; case LESS_THAN: attrCond.setType(AttrCond.Type.LT); - leaf = SearchCond.getLeaf(attrCond); + leaf = SearchCond.of(attrCond); break; default: @@ -237,7 +237,7 @@ protected SearchCond visitPrimitive(final SearchCondition sc) { } // SYNCOPE-1293: explicitly re-process to allow 'token==$null' or 'token!=$null' - Optional reprocess = leaf.getLeaf(AttrCond.class). + Optional reprocess = leaf.asLeaf(AttrCond.class). filter(cond -> "token".equals(cond.getSchema()) && (cond.getType() == AttrCond.Type.ISNULL || cond.getType() == AttrCond.Type.ISNOTNULL) && cond.getExpression() == null); @@ -246,7 +246,7 @@ protected SearchCond visitPrimitive(final SearchCondition sc) { tokenCond.setSchema(reprocess.get().getSchema()); tokenCond.setType(reprocess.get().getType()); tokenCond.setExpression(null); - leaf = SearchCond.getLeaf(tokenCond); + leaf = SearchCond.of(tokenCond); } return leaf; @@ -263,11 +263,11 @@ protected SearchCond visitCompound(final SearchCondition sc) { SearchCond compound; switch (sc.getConditionType()) { case AND: - compound = SearchCond.getAnd(searchConds); + compound = SearchCond.and(searchConds); break; case OR: - compound = SearchCond.getOr(searchConds); + compound = SearchCond.or(searchConds); break; default: diff --git a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java index b3815b798b..4e767d4620 100644 --- a/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java +++ b/core/persistence-api/src/test/java/org/apache/syncope/core/persistence/api/search/SearchCondConverterTest.java @@ -52,7 +52,7 @@ public void eq() { AnyCond attrCond = new AnyCond(AttrCond.Type.EQ); attrCond.setSchema("username"); attrCond.setExpression("rossini"); - SearchCond leaf = SearchCond.getLeaf(attrCond); + SearchCond leaf = SearchCond.of(attrCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -65,7 +65,7 @@ public void ieq() { AnyCond attrCond = new AnyCond(AttrCond.Type.IEQ); attrCond.setSchema("username"); attrCond.setExpression("rossini"); - SearchCond leaf = SearchCond.getLeaf(attrCond); + SearchCond leaf = SearchCond.of(attrCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -78,7 +78,7 @@ public void nieq() { AnyCond anyCond = new AnyCond(AttrCond.Type.IEQ); anyCond.setSchema("username"); anyCond.setExpression("rossini"); - SearchCond leaf = SearchCond.getNotLeaf(anyCond); + SearchCond leaf = SearchCond.negate(anyCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -91,7 +91,7 @@ public void like() { AttrCond attrCond = new AnyCond(AttrCond.Type.LIKE); attrCond.setSchema("username"); attrCond.setExpression("ros%"); - SearchCond leaf = SearchCond.getLeaf(attrCond); + SearchCond leaf = SearchCond.of(attrCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -104,7 +104,7 @@ public void ilike() { AttrCond attrCond = new AnyCond(AttrCond.Type.ILIKE); attrCond.setSchema("username"); attrCond.setExpression("ros%"); - SearchCond leaf = SearchCond.getLeaf(attrCond); + SearchCond leaf = SearchCond.of(attrCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -117,7 +117,7 @@ public void nilike() { AttrCond attrCond = new AnyCond(AttrCond.Type.ILIKE); attrCond.setSchema("username"); attrCond.setExpression("ros%"); - SearchCond leaf = SearchCond.getNotLeaf(attrCond); + SearchCond leaf = SearchCond.negate(attrCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -129,7 +129,7 @@ public void isNull() { AttrCond attrCond = new AttrCond(AttrCond.Type.ISNULL); attrCond.setSchema("loginDate"); - SearchCond leaf = SearchCond.getLeaf(attrCond); + SearchCond leaf = SearchCond.of(attrCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -141,7 +141,7 @@ public void isNotNull() { AttrCond attrCond = new AttrCond(AttrCond.Type.ISNOTNULL); attrCond.setSchema("loginDate"); - SearchCond leaf = SearchCond.getLeaf(attrCond); + SearchCond leaf = SearchCond.of(attrCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -154,7 +154,7 @@ public void relationships() { RelationshipCond relationshipCond = new RelationshipCond(); relationshipCond.setAnyObject("ca20ffca-1305-442f-be9a-3723a0cd88ca"); - SearchCond leaf = SearchCond.getLeaf(relationshipCond); + SearchCond leaf = SearchCond.of(relationshipCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -166,7 +166,7 @@ public void relationshipTypes() { RelationshipTypeCond relationshipCond = new RelationshipTypeCond(); relationshipCond.setRelationshipTypeKey("type1"); - SearchCond leaf = SearchCond.getLeaf(relationshipCond); + SearchCond leaf = SearchCond.of(relationshipCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); @@ -184,7 +184,7 @@ public void groups() { MembershipCond groupCond = new MembershipCond(); groupCond.setGroup("e7ff94e8-19c9-4f0a-b8b7-28327edbf6ed"); - SearchCond leaf = SearchCond.getLeaf(groupCond); + SearchCond leaf = SearchCond.of(groupCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -196,7 +196,7 @@ public void roles() { RoleCond roleCond = new RoleCond(); roleCond.setRole("User reviewer"); - SearchCond leaf = SearchCond.getLeaf(roleCond); + SearchCond leaf = SearchCond.of(roleCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -208,7 +208,7 @@ public void privileges() { PrivilegeCond privilegeCond = new PrivilegeCond(); privilegeCond.setPrivilege("postMighty"); - SearchCond leaf = SearchCond.getLeaf(privilegeCond); + SearchCond leaf = SearchCond.of(privilegeCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -221,7 +221,7 @@ public void dynRealms() { DynRealmCond dynRealmCond = new DynRealmCond(); dynRealmCond.setDynRealm(dynRealm); - SearchCond leaf = SearchCond.getLeaf(dynRealmCond); + SearchCond leaf = SearchCond.of(dynRealmCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -233,7 +233,7 @@ public void auxClasses() { AuxClassCond cond = new AuxClassCond(); cond.setAuxClass("clazz1"); - SearchCond leaf = SearchCond.getLeaf(cond); + SearchCond leaf = SearchCond.of(cond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -245,7 +245,7 @@ public void resources() { ResourceCond resCond = new ResourceCond(); resCond.setResource("resource-ldap"); - SearchCond leaf = SearchCond.getLeaf(resCond); + SearchCond leaf = SearchCond.of(resCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -257,7 +257,7 @@ public void type() { AnyTypeCond acond = new AnyTypeCond(); acond.setAnyTypeKey("PRINTER"); - SearchCond leaf = SearchCond.getLeaf(acond); + SearchCond leaf = SearchCond.of(acond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -269,7 +269,7 @@ public void member() { MemberCond mcond = new MemberCond(); mcond.setMember("rossini"); - SearchCond leaf = SearchCond.getLeaf(mcond); + SearchCond leaf = SearchCond.of(mcond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -286,9 +286,9 @@ public void and() { AttrCond fullnameLeafCond2 = new AttrCond(AttrCond.Type.LIKE); fullnameLeafCond2.setSchema("fullname"); fullnameLeafCond2.setExpression("%i%"); - SearchCond andCond = SearchCond.getAnd( - SearchCond.getLeaf(fullnameLeafCond1), - SearchCond.getLeaf(fullnameLeafCond2)); + SearchCond andCond = SearchCond.and( + SearchCond.of(fullnameLeafCond1), + SearchCond.of(fullnameLeafCond2)); assertEquals(andCond, SearchCondConverter.convert(VISITOR, fiql)); } @@ -311,10 +311,10 @@ public void or() { AttrCond fullnameLeafCond3 = new AttrCond(AttrCond.Type.LIKE); fullnameLeafCond3.setSchema("fullname"); fullnameLeafCond3.setExpression("%ini"); - SearchCond orCond = SearchCond.getOr(SearchCond.getLeaf(fullnameLeafCond1), - SearchCond.getOr( - SearchCond.getLeaf(fullnameLeafCond2), - SearchCond.getLeaf(fullnameLeafCond3))); + SearchCond orCond = SearchCond.or(SearchCond.of(fullnameLeafCond1), + SearchCond.or( + SearchCond.of(fullnameLeafCond2), + SearchCond.of(fullnameLeafCond3))); assertEquals(orCond, SearchCondConverter.convert(VISITOR, fiql)); } @@ -327,7 +327,7 @@ public void issueSYNCOPE1223() { cond.setSchema("ctype"); cond.setExpression("ou=sample,o=isp"); - assertEquals(SearchCond.getLeaf(cond), SearchCondConverter.convert(VISITOR, fiql)); + assertEquals(SearchCond.of(cond), SearchCondConverter.convert(VISITOR, fiql)); } @Test @@ -338,7 +338,7 @@ public void issueSYNCOPE1779() { AnyCond anyCond = new AnyCond(AttrCond.Type.ILIKE); anyCond.setSchema("username"); anyCond.setExpression("ros\\_%"); - SearchCond leaf = SearchCond.getLeaf(anyCond); + SearchCond leaf = SearchCond.of(anyCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); @@ -347,7 +347,7 @@ public void issueSYNCOPE1779() { anyCond = new AnyCond(AttrCond.Type.EQ); anyCond.setSchema("name"); anyCond.setExpression("_018c34a9-f86b-75cf-855b-a3915cc5ff44"); - leaf = SearchCond.getLeaf(anyCond); + leaf = SearchCond.of(anyCond); assertEquals(leaf, SearchCondConverter.convert(VISITOR, fiql)); } @@ -361,7 +361,7 @@ public void issueSYNCOPE1826() { anyCond.setSchema("username"); anyCond.setExpression("sh test app 0722"); - assertEquals(SearchCond.getLeaf(anyCond), SearchCondConverter.convert(VISITOR, fiql)); + assertEquals(SearchCond.of(anyCond), SearchCondConverter.convert(VISITOR, fiql)); fiql = "lastLoginDate==2016-03-02T15:21:22%2B0300"; @@ -369,6 +369,6 @@ public void issueSYNCOPE1826() { lastLoginDateCond.setSchema("lastLoginDate"); lastLoginDateCond.setExpression("2016-03-02T15:21:22+0300"); - assertEquals(SearchCond.getLeaf(lastLoginDateCond), SearchCondConverter.convert(VISITOR, fiql)); + assertEquals(SearchCond.of(lastLoginDateCond), SearchCondConverter.convert(VISITOR, fiql)); } } diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnyMatchDAO.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnyMatchDAO.java index 0e2085d9ab..52e982bb5e 100644 --- a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnyMatchDAO.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnyMatchDAO.java @@ -123,68 +123,68 @@ public > boolean matches(final T any, final SearchCond cond) { boolean not = cond.getType() == SearchCond.Type.NOT_LEAF; switch (cond.getType()) { case LEAF, NOT_LEAF -> { - Boolean match = cond.getLeaf(AnyTypeCond.class). + Boolean match = cond.asLeaf(AnyTypeCond.class). filter(leaf -> AnyTypeKind.ANY_OBJECT == any.getType().getKind()). map(leaf -> matches(any, leaf, not)). orElse(null); if (match == null) { - match = cond.getLeaf(RelationshipTypeCond.class). + match = cond.asLeaf(RelationshipTypeCond.class). filter(leaf -> any instanceof GroupableRelatable). map(leaf -> matches((GroupableRelatable) any, leaf, not)). orElse(null); } if (match == null) { - match = cond.getLeaf(RelationshipCond.class). + match = cond.asLeaf(RelationshipCond.class). filter(leaf -> any instanceof GroupableRelatable). map(leaf -> matches((GroupableRelatable) any, leaf, not)). orElse(null); } if (match == null) { - match = cond.getLeaf(MembershipCond.class). + match = cond.asLeaf(MembershipCond.class). filter(leaf -> any instanceof GroupableRelatable). map(leaf -> matches((GroupableRelatable) any, leaf, not)). orElse(null); } if (match == null) { - match = cond.getLeaf(RoleCond.class). + match = cond.asLeaf(RoleCond.class). filter(leaf -> any instanceof User). map(leaf -> matches((User) any, leaf, not)). orElse(null); } if (match == null) { - match = cond.getLeaf(DynRealmCond.class). + match = cond.asLeaf(DynRealmCond.class). map(leaf -> matches(any, leaf, not)). orElse(null); } if (match == null) { - match = cond.getLeaf(MemberCond.class). + match = cond.asLeaf(MemberCond.class). filter(leaf -> any instanceof Group). map(leaf -> matches((Group) any, leaf, not)). orElse(null); } if (match == null) { - match = cond.getLeaf(ResourceCond.class). + match = cond.asLeaf(ResourceCond.class). map(leaf -> matches(any, leaf, not)). orElse(null); } if (match == null) { - match = cond.getLeaf(AnyCond.class). + match = cond.asLeaf(AnyCond.class). map(value -> matches(any, value, not)). - orElseGet(() -> cond.getLeaf(AttrCond.class). + orElseGet(() -> cond.asLeaf(AttrCond.class). map(leaf -> matches(any, leaf, not)). orElse(null)); } if (match == null) { - match = cond.getLeaf(AttrCond.class). + match = cond.asLeaf(AttrCond.class). map(leaf -> matches(any, leaf, not)). orElse(null); } diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnySearchDAO.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnySearchDAO.java index 6ceda7585c..8b538d90c3 100644 --- a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnySearchDAO.java +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractAnySearchDAO.java @@ -69,6 +69,8 @@ public abstract class AbstractAnySearchDAO implements AnySearchDAO { protected static final Logger LOG = LoggerFactory.getLogger(AnySearchDAO.class); + protected static final String ALWAYS_FALSE_CLAUSE = "1=2"; + private static final String[] ORDER_BY_NOT_ALLOWED = { "serialVersionUID", "password", "securityQuestion", "securityAnswer", "token", "tokenExpireTime" }; @@ -87,10 +89,10 @@ protected static SearchCond buildEffectiveCond( List dynRealmConds = dynRealmKeys.stream().map(key -> { DynRealmCond dynRealmCond = new DynRealmCond(); dynRealmCond.setDynRealm(key); - return SearchCond.getLeaf(dynRealmCond); + return SearchCond.of(dynRealmCond); }).toList(); if (!dynRealmConds.isEmpty()) { - result.add(SearchCond.getOr(dynRealmConds)); + result.add(SearchCond.or(dynRealmConds)); } List groupOwnerConds = groupOwners.stream().map(key -> { @@ -105,13 +107,13 @@ protected static SearchCond buildEffectiveCond( membershipCond.setGroup(key); asc = membershipCond; } - return SearchCond.getLeaf(asc); + return SearchCond.of(asc); }).toList(); if (!groupOwnerConds.isEmpty()) { - result.add(SearchCond.getOr(groupOwnerConds)); + result.add(SearchCond.or(groupOwnerConds)); } - return SearchCond.getAnd(result); + return SearchCond.and(result); } protected static String key(final AttrSchemaType schemaType) { @@ -281,6 +283,9 @@ protected Triple check(final AnyCond cond, schema.setType(attrSchemaType); } } + if (schema.getType() == null || schema.getType() == AttrSchemaType.Dropdown) { + schema.setType(AttrSchemaType.String); + } // Deal with any Integer fields logically mapping to boolean values boolean foundBooleanMin = false; diff --git a/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AnyFinder.java b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AnyFinder.java new file mode 100644 index 0000000000..f9dbb602ef --- /dev/null +++ b/core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AnyFinder.java @@ -0,0 +1,184 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.common.dao; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.regex.Pattern; +import org.apache.commons.jexl3.parser.Parser; +import org.apache.commons.jexl3.parser.ParserConstants; +import org.apache.commons.jexl3.parser.Token; +import org.apache.commons.lang3.StringUtils; +import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; +import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; +import org.apache.syncope.core.persistence.api.dao.search.AttrCond; +import org.apache.syncope.core.persistence.api.dao.search.SearchCond; +import org.apache.syncope.core.persistence.api.entity.Any; +import org.apache.syncope.core.persistence.api.entity.DerSchema; +import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; +import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.transaction.annotation.Transactional; + +public class AnyFinder { + + protected static final Logger LOG = LoggerFactory.getLogger(AnyFinder.class); + + protected static final Comparator LITERAL_COMPARATOR = (l1, l2) -> { + if (l1 == null && l2 == null) { + return 0; + } else if (l1 != null && l2 == null) { + return -1; + } else if (l1 == null) { + return 1; + } else if (l1.length() == l2.length()) { + return 0; + } else if (l1.length() > l2.length()) { + return -1; + } else { + return 1; + } + }; + + /** + * Split an attribute value recurring on provided literals/tokens. + * + * @param attrValue value to be split + * @param literals literals/tokens + * @return split value + */ + protected static List split(final String attrValue, final List literals) { + final List attrValues = new ArrayList<>(); + + if (literals.isEmpty()) { + attrValues.add(attrValue); + } else { + for (String token : attrValue.split(Pattern.quote(literals.get(0)))) { + if (!token.isEmpty()) { + attrValues.addAll(split(token, literals.subList(1, literals.size()))); + } + } + } + + return attrValues; + } + + protected final PlainSchemaDAO plainSchemaDAO; + + protected final AnySearchDAO anySearchDAO; + + public AnyFinder(final PlainSchemaDAO plainSchemaDAO, final AnySearchDAO anySearchDAO) { + this.plainSchemaDAO = plainSchemaDAO; + this.anySearchDAO = anySearchDAO; + } + + @Transactional(readOnly = true) + public > Optional findByPlainAttrUniqueValue( + final AnyTypeKind anyTypeKind, + final PlainSchema schema, + final PlainAttrUniqueValue attrUniqueValue) { + + AttrCond cond = new AttrCond(AttrCond.Type.EQ); + cond.setSchema(schema.getKey()); + cond.setExpression(attrUniqueValue.getStringValue()); + + List result = anySearchDAO.search(SearchCond.of(cond), anyTypeKind); + if (result.isEmpty()) { + return Optional.empty(); + } + return Optional.of(result.get(0)); + } + + @Transactional(readOnly = true) + public > List findByDerAttrValue( + final AnyTypeKind anyTypeKind, + final DerSchema derSchema, + final String value, + final boolean ignoreCaseMatch) { + + if (derSchema == null) { + LOG.error("No DerSchema"); + return List.of(); + } + + Parser parser = new Parser(derSchema.getExpression()); + + // Schema keys + List identifiers = new ArrayList<>(); + + // Literals + List literals = new ArrayList<>(); + + // Get schema keys and literals + for (Token token = parser.getNextToken(); token != null && StringUtils.isNotBlank(token.toString()); + token = parser.getNextToken()) { + + if (token.kind == ParserConstants.STRING_LITERAL) { + literals.add(token.toString().substring(1, token.toString().length() - 1)); + } + + if (token.kind == ParserConstants.IDENTIFIER) { + identifiers.add(token.toString()); + } + } + + // Sort literals in order to process later literals included into others + literals.sort(LITERAL_COMPARATOR); + + // Split value on provided literals + List attrValues = split(value, literals); + + if (attrValues.size() != identifiers.size()) { + LOG.error("Ambiguous JEXL expression resolution: literals and values have different size"); + return List.of(); + } + + List andConditions = new ArrayList<>(); + + // Contains used identifiers in order to avoid replications + Set used = new HashSet<>(); + + // Create several clauses: one for eanch identifiers + for (int i = 0; i < identifiers.size() && !used.contains(identifiers.get(i)); i++) { + // verify schema existence and get schema type + PlainSchema schema = plainSchemaDAO.findById(identifiers.get(i)).orElse(null); + + if (schema == null) { + LOG.error("Invalid schema '{}', ignoring", identifiers.get(i)); + } else { + used.add(identifiers.get(i)); + + AttrCond cond = new AttrCond(ignoreCaseMatch ? AttrCond.Type.IEQ : AttrCond.Type.EQ); + cond.setSchema(schema.getKey()); + cond.setExpression(attrValues.get(i)); + andConditions.add(SearchCond.of(cond)); + } + } + + LOG.debug("Generated search {} conditions: {}", anyTypeKind, andConditions); + + return anySearchDAO.search(SearchCond.and(andConditions), anyTypeKind); + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MariaDBPersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MariaDBPersistenceContext.java index 337d5700fd..e8c8b39086 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MariaDBPersistenceContext.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MariaDBPersistenceContext.java @@ -31,8 +31,6 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; -import org.apache.syncope.core.persistence.jpa.dao.AnyFinder; -import org.apache.syncope.core.persistence.jpa.dao.MariaDBAnyFinder; import org.apache.syncope.core.persistence.jpa.dao.MariaDBJPAAnySearchDAO; import org.apache.syncope.core.persistence.jpa.dao.repo.MariaDBPlainSchemaRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExt; @@ -53,12 +51,6 @@ public EntityFactory entityFactory() { return new MariaDBEntityFactory(); } - @ConditionalOnMissingBean - @Bean - public AnyFinder anyFinder(final @Lazy PlainSchemaDAO plainSchemaDAO, final EntityManager entityManager) { - return new MariaDBAnyFinder(plainSchemaDAO, entityManager); - } - @ConditionalOnMissingBean @Bean public AnySearchDAO anySearchDAO( diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MySQLPersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MySQLPersistenceContext.java index 44b35dbcce..2bde40db09 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MySQLPersistenceContext.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/MySQLPersistenceContext.java @@ -31,8 +31,6 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; -import org.apache.syncope.core.persistence.jpa.dao.AnyFinder; -import org.apache.syncope.core.persistence.jpa.dao.MySQLAnyFinder; import org.apache.syncope.core.persistence.jpa.dao.MySQLJPAAnySearchDAO; import org.apache.syncope.core.persistence.jpa.dao.repo.MySQLPlainSchemaRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExt; @@ -53,12 +51,6 @@ public EntityFactory entityFactory() { return new MySQLEntityFactory(); } - @ConditionalOnMissingBean - @Bean - public AnyFinder anyFinder(final @Lazy PlainSchemaDAO plainSchemaDAO, final EntityManager entityManager) { - return new MySQLAnyFinder(plainSchemaDAO, entityManager); - } - @ConditionalOnMissingBean @Bean public AnySearchDAO anySearchDAO( diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/OraclePersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/OraclePersistenceContext.java index 2d4a7fa11d..ac25ca2f7d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/OraclePersistenceContext.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/OraclePersistenceContext.java @@ -31,8 +31,6 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; -import org.apache.syncope.core.persistence.jpa.dao.AnyFinder; -import org.apache.syncope.core.persistence.jpa.dao.OracleAnyFinder; import org.apache.syncope.core.persistence.jpa.dao.OracleJPAAnySearchDAO; import org.apache.syncope.core.persistence.jpa.dao.repo.OraclePlainSchemaRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExt; @@ -53,12 +51,6 @@ public EntityFactory entityFactory() { return new OracleEntityFactory(); } - @ConditionalOnMissingBean - @Bean - public AnyFinder anyFinder(final @Lazy PlainSchemaDAO plainSchemaDAO, final EntityManager entityManager) { - return new OracleAnyFinder(plainSchemaDAO, entityManager); - } - @ConditionalOnMissingBean @Bean public AnySearchDAO anySearchDAO( diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PGPersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PGPersistenceContext.java index 8a4f2dfe23..00af19795c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PGPersistenceContext.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PGPersistenceContext.java @@ -31,8 +31,6 @@ import org.apache.syncope.core.persistence.api.dao.UserDAO; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; -import org.apache.syncope.core.persistence.jpa.dao.AnyFinder; -import org.apache.syncope.core.persistence.jpa.dao.PGAnyFinder; import org.apache.syncope.core.persistence.jpa.dao.PGJPAAnySearchDAO; import org.apache.syncope.core.persistence.jpa.dao.repo.PGPlainSchemaRepoExtImpl; import org.apache.syncope.core.persistence.jpa.dao.repo.PlainSchemaRepoExt; @@ -53,12 +51,6 @@ public EntityFactory entityFactory() { return new PGEntityFactory(); } - @ConditionalOnMissingBean - @Bean - public AnyFinder anyFinder(final @Lazy PlainSchemaDAO plainSchemaDAO, final EntityManager entityManager) { - return new PGAnyFinder(plainSchemaDAO, entityManager); - } - @ConditionalOnMissingBean @Bean public AnySearchDAO anySearchDAO( diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java index 82abb71e9f..cfa3ce359e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/PersistenceContext.java @@ -88,9 +88,9 @@ import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; import org.apache.syncope.core.persistence.common.CommonPersistenceContext; import org.apache.syncope.core.persistence.common.RuntimeDomainLoader; +import org.apache.syncope.core.persistence.common.dao.AnyFinder; import org.apache.syncope.core.persistence.jpa.content.XMLContentExporter; import org.apache.syncope.core.persistence.jpa.content.XMLContentLoader; -import org.apache.syncope.core.persistence.jpa.dao.AnyFinder; import org.apache.syncope.core.persistence.jpa.dao.JPAAnyMatchDAO; import org.apache.syncope.core.persistence.jpa.dao.JPAAuditEventDAO; import org.apache.syncope.core.persistence.jpa.dao.JPABatchDAO; @@ -371,6 +371,12 @@ protected Class getRepositoryBaseClass(final RepositoryMetadata metadata) { }; } + @ConditionalOnMissingBean + @Bean + public AnyFinder anyFinder(final @Lazy PlainSchemaDAO plainSchemaDAO, final @Lazy AnySearchDAO anySearchDAO) { + return new AnyFinder(plainSchemaDAO, anySearchDAO); + } + @ConditionalOnMissingBean @Bean public AccessTokenDAO accessTokenDAO(final JpaRepositoryFactory jpaRepositoryFactory) { diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAAnySearchDAO.java index b8c8a05839..752881249c 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPAAnySearchDAO.java @@ -22,13 +22,15 @@ import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.Query; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; import java.util.stream.Collectors; -import java.util.stream.Stream; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; @@ -39,6 +41,7 @@ import org.apache.syncope.common.lib.SyncopeClientException; import org.apache.syncope.common.lib.SyncopeConstants; import org.apache.syncope.common.lib.types.AnyTypeKind; +import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.syncope.common.lib.types.ClientExceptionType; import org.apache.syncope.common.rest.api.service.JAXRSService; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; @@ -101,6 +104,14 @@ protected static void fillWithParameters(final Query query, final List p } } + protected static Supplier syncopeClientException(final String message) { + return () -> { + SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidSearchParameters); + sce.getElements().add(message); + return sce; + }; + } + protected final EntityManagerFactory entityManagerFactory; protected final EntityManager entityManager; @@ -143,975 +154,1023 @@ protected boolean isOracle() { }); } - protected String buildAdminRealmsFilter( - final Set realmKeys, - final SearchSupport svs, - final List parameters) { - - if (realmKeys.isEmpty()) { - return "u.any_id IS NOT NULL"; - } + protected SearchSupport.SearchView defaultSV(final SearchSupport svs) { + return svs.field(); + } - String realmKeysArg = realmKeys.stream(). - map(realmKey -> "?" + setParameter(parameters, realmKey)). - collect(Collectors.joining(",")); - return "u.any_id IN (SELECT any_id FROM " + svs.field().name() - + " WHERE realm_id IN (" + realmKeysArg + "))"; + protected String anyId(final SearchSupport svs) { + return defaultSV(svs).alias() + ".any_id"; } - protected Triple, Set> getAdminRealmsFilter( - final Realm base, - final boolean recursive, - final Set adminRealms, + protected Optional getQueryForCustomConds( + final SearchCond cond, + final List parameters, final SearchSupport svs, - final List parameters) { - - Set realmKeys = new HashSet<>(); - Set dynRealmKeys = new HashSet<>(); - Set groupOwners = new HashSet<>(); + final boolean not) { - if (recursive) { - adminRealms.forEach(realmPath -> RealmUtils.parseGroupOwnerRealm(realmPath).ifPresentOrElse( - goRealm -> groupOwners.add(goRealm.getRight()), - () -> { - if (realmPath.startsWith("/")) { - Realm realm = realmSearchDAO.findByFullPath(realmPath).orElseThrow(() -> { - SyncopeClientException noRealm = - SyncopeClientException.build(ClientExceptionType.InvalidRealm); - noRealm.getElements().add("Invalid realm specified: " + realmPath); - return noRealm; - }); + // do nothing by default, leave it open for subclasses + return Optional.empty(); + } - realmKeys.addAll(realmSearchDAO.findDescendants(realm.getFullPath(), base.getFullPath())); - } else { - dynRealmDAO.findById(realmPath).ifPresentOrElse( - dynRealm -> dynRealmKeys.add(dynRealm.getKey()), - () -> LOG.warn("Ignoring invalid dynamic realm {}", realmPath)); - } - })); - if (!dynRealmKeys.isEmpty()) { - realmKeys.clear(); - } - } else { - if (adminRealms.stream().anyMatch(r -> r.startsWith(base.getFullPath()))) { - realmKeys.add(base.getKey()); - } - } + protected Optional>> getQuery( + final SearchCond cond, final List parameters, final SearchSupport svs) { - return Triple.of(buildAdminRealmsFilter(realmKeys, svs, parameters), dynRealmKeys, groupOwners); - } + boolean not = cond.getType() == SearchCond.Type.NOT_LEAF; - SearchSupport buildSearchSupport(final AnyTypeKind kind) { - return new SearchViewSupport(kind); - } + Optional node = Optional.empty(); + Set plainSchemas = new HashSet<>(); - @Override - protected long doCount( - final Realm base, - final boolean recursive, - final Set adminRealms, - final SearchCond cond, - final AnyTypeKind kind) { + switch (cond.getType()) { + case LEAF: + case NOT_LEAF: + if (node.isEmpty()) { + node = cond.asLeaf(AnyTypeCond.class). + filter(leaf -> AnyTypeKind.ANY_OBJECT == svs.anyTypeKind). + map(leaf -> getQuery(leaf, not, parameters, svs)); + } - List parameters = new ArrayList<>(); + if (node.isEmpty()) { + node = cond.asLeaf(AuxClassCond.class). + map(leaf -> getQuery(leaf, not, parameters, svs)); + } - SearchSupport svs = buildSearchSupport(kind); + if (node.isEmpty()) { + node = cond.asLeaf(RelationshipTypeCond.class). + map(leaf -> getQuery(leaf, not, parameters, svs)); + } - Triple, Set> filter = - getAdminRealmsFilter(base, recursive, adminRealms, svs, parameters); + if (node.isEmpty()) { + node = cond.asLeaf(RelationshipCond.class). + map(leaf -> getQuery(leaf, not, parameters, svs)); + } - // 1. get the query string from the search condition - Pair> queryInfo = - getQuery(buildEffectiveCond(cond, filter.getMiddle(), filter.getRight(), kind), parameters, svs); + if (node.isEmpty()) { + node = cond.asLeaf(MembershipCond.class). + map(leaf -> getQuery(leaf, not, parameters, svs)); + } - StringBuilder queryString = queryInfo.getLeft(); + if (node.isEmpty()) { + node = cond.asLeaf(MemberCond.class). + map(leaf -> getQuery(leaf, not, parameters, svs)); + } - // 2. take realms into account - queryString.insert(0, "SELECT u.any_id FROM ("); - queryString.append(") u WHERE ").append(filter.getLeft()); + if (node.isEmpty()) { + node = cond.asLeaf(RoleCond.class). + filter(leaf -> AnyTypeKind.USER == svs.anyTypeKind). + map(leaf -> getQuery(leaf, not, parameters, svs)); + } - // 3. prepare the COUNT query - queryString.insert(0, "SELECT COUNT(any_id) FROM ("); - queryString.append(") count_any_id"); + if (node.isEmpty()) { + node = cond.asLeaf(PrivilegeCond.class). + filter(leaf -> AnyTypeKind.USER == svs.anyTypeKind). + map(leaf -> getQuery(leaf, not, parameters, svs)); + } - Query countQuery = entityManager.createNativeQuery(queryString.toString()); - fillWithParameters(countQuery, parameters); + if (node.isEmpty()) { + node = cond.asLeaf(DynRealmCond.class). + map(leaf -> getQuery(leaf, not, parameters, svs)); + } - return ((Number) countQuery.getSingleResult()).longValue(); - } + if (node.isEmpty()) { + node = cond.asLeaf(ResourceCond.class). + map(leaf -> getQuery(leaf, not, parameters, svs)); + } - @Override - @SuppressWarnings("unchecked") - protected > List doSearch( - final Realm base, - final boolean recursive, - final Set adminRealms, - final SearchCond cond, - final Pageable pageable, - final AnyTypeKind kind) { + if (node.isEmpty()) { + node = cond.asLeaf(AnyCond.class). + map(anyCond -> getQuery(anyCond, not, parameters, svs)). + or(() -> cond.asLeaf(AttrCond.class). + map(attrCond -> { + Pair checked = check(attrCond, svs.anyTypeKind); + Pair query = getQuery(attrCond, not, checked, parameters, svs); + if (query.getLeft()) { + plainSchemas.add(checked.getLeft().getKey()); + } + return query.getRight(); + })); + } - List parameters = new ArrayList<>(); - SearchSupport svs = buildSearchSupport(kind); - try { - Triple, Set> filter = - getAdminRealmsFilter(base, recursive, adminRealms, svs, parameters); + // allow for additional search conditions + if (node.isEmpty()) { + node = getQueryForCustomConds(cond, parameters, svs, not); + } + break; - // 1. get the query string from the search condition - Pair> queryInfo = - getQuery(buildEffectiveCond(cond, filter.getMiddle(), filter.getRight(), kind), parameters, svs); + case AND: + AnySearchNode andNode = new AnySearchNode(AnySearchNode.Type.AND); - StringBuilder queryString = queryInfo.getLeft(); + getQuery(cond.getLeft(), parameters, svs).ifPresent(left -> { + andNode.add(left.getLeft()); + plainSchemas.addAll(left.getRight()); + }); - LOG.debug("Query: {}, parameters: {}", queryString, parameters); + getQuery(cond.getRight(), parameters, svs).ifPresent(right -> { + andNode.add(right.getLeft()); + plainSchemas.addAll(right.getRight()); + }); - // 2. take into account realms and ordering - OrderBySupport obs = parseOrderBy(svs, pageable.getSort().get()); - if (queryString.charAt(0) == '(') { - queryString.insert(0, buildSelect(obs)); - } else { - queryString.insert(0, buildSelect(obs).append('(')); - queryString.append(')'); - } - queryString. - append(buildWhere(svs, obs)). - append(filter.getLeft()). - append(buildOrderBy(obs)); + if (!andNode.getChildren().isEmpty()) { + node = Optional.of(andNode); + } + break; - LOG.debug("Query with auth and order by statements: {}, parameters: {}", queryString, parameters); + case OR: + AnySearchNode orNode = new AnySearchNode(AnySearchNode.Type.OR); - // 3. prepare the search query - Query query = entityManager.createNativeQuery(queryString.toString()); + getQuery(cond.getLeft(), parameters, svs).ifPresent(left -> { + orNode.add(left.getLeft()); + plainSchemas.addAll(left.getRight()); + }); - if (pageable.isPaged()) { - query.setFirstResult(pageable.getPageSize() * pageable.getPageNumber()); - query.setMaxResults(pageable.getPageSize()); - } + getQuery(cond.getRight(), parameters, svs).ifPresent(right -> { + orNode.add(right.getLeft()); + plainSchemas.addAll(right.getRight()); + }); - // 4. populate the search query with parameter values - fillWithParameters(query, parameters); + if (!orNode.getChildren().isEmpty()) { + node = Optional.of(orNode); + } + break; - // 5. Prepare the result (avoiding duplicates) - return buildResult(query.getResultList(), kind); - } catch (SyncopeClientException e) { - throw e; - } catch (Exception e) { - LOG.error("While searching for {}", kind, e); + default: } - return List.of(); + return node.map(n -> Pair.of(n, plainSchemas)); } - protected StringBuilder buildSelect(final OrderBySupport obs) { - StringBuilder select = new StringBuilder("SELECT DISTINCT u.any_id"); + protected AnySearchNode getQuery( + final AnyTypeCond cond, + final boolean not, + final List parameters, + final SearchSupport svs) { - obs.items.forEach(item -> select.append(',').append(item.select)); - select.append(" FROM "); + StringBuilder clause = new StringBuilder("type_id"); + if (not) { + clause.append("<>"); + } else { + clause.append('='); + } + clause.append('?').append(setParameter(parameters, cond.getAnyTypeKey())); - return select; + return new AnySearchNode.Leaf(defaultSV(svs), clause.toString()); } - protected void processOBS( - final SearchSupport svs, - final OrderBySupport obs, - final StringBuilder where) { - - Set attrs = obs.items.stream(). - map(item -> item.orderBy.substring(0, item.orderBy.indexOf(' '))).collect(Collectors.toSet()); - - obs.views.forEach(searchView -> { - where.append(','); - - boolean searchViewAddedToWhere = false; - if (searchView.name().equals(svs.asSearchViewSupport().attr().name())) { - StringBuilder attrWhere = new StringBuilder(); - StringBuilder nullAttrWhere = new StringBuilder(); - - if (svs.nonMandatorySchemas || obs.nonMandatorySchemas) { - where.append(" (SELECT * FROM ").append(searchView.name()); - searchViewAddedToWhere = true; + protected AnySearchNode getQuery( + final AuxClassCond cond, + final boolean not, + final List parameters, + final SearchSupport svs) { - attrs.forEach(field -> { - if (attrWhere.length() == 0) { - attrWhere.append(" WHERE "); - } else { - attrWhere.append(" OR "); - } - attrWhere.append("schema_id='").append(field).append("'"); - - nullAttrWhere.append(" UNION SELECT any_id, "). - append("'"). - append(field). - append("' AS schema_id, "). - append("null AS booleanvalue, "). - append("null AS datevalue, "). - append("null AS doublevalue, "). - append("null AS longvalue, "). - append("null AS stringvalue FROM ").append(svs.field().name()). - append(" WHERE "). - append("any_id NOT IN ("). - append("SELECT any_id FROM "). - append(svs.asSearchViewSupport().attr().name()).append(' ').append(searchView.alias()). - append(" WHERE ").append("schema_id='").append(field).append("')"); - }); - where.append(attrWhere).append(nullAttrWhere).append(')'); - } - } - if (!searchViewAddedToWhere) { - where.append(searchView.name()); - } + StringBuilder clause = new StringBuilder(); + if (not) { + clause.append(anyId(svs)).append(" NOT IN ("); + } else { + clause.append(anyId(svs)).append(" IN ("); + } + clause.append("SELECT any_id FROM "). + append(svs.auxClass().name()). + append(" WHERE anyTypeClass_id=?"). + append(setParameter(parameters, cond.getAuxClass())). + append(')'); - where.append(' ').append(searchView.alias()); - }); + return new AnySearchNode.Leaf(defaultSV(svs), clause.toString()); } - protected StringBuilder buildWhere( - final SearchSupport svs, - final OrderBySupport obs) { - - StringBuilder where = new StringBuilder(" u"); - processOBS(svs, obs, where); - where.append(" WHERE "); - - obs.views.forEach(searchView -> where.append("u.any_id=").append(searchView.alias()).append(".any_id AND ")); + protected AnySearchNode getQuery( + final RelationshipTypeCond cond, + final boolean not, + final List parameters, + final SearchSupport svs) { - obs.items.stream(). - filter(item -> StringUtils.isNotBlank(item.where)). - forEach(item -> where.append(item.where).append(" AND ")); + StringBuilder clause = new StringBuilder(); + if (not) { + clause.append(anyId(svs)).append(" NOT IN ("); + } else { + clause.append(anyId(svs)).append(" IN ("); + } + clause.append("SELECT any_id ").append("FROM "). + append(svs.relationship().name()). + append(" WHERE type=?").append(setParameter(parameters, cond.getRelationshipTypeKey())). + append(" UNION SELECT right_any_id AS any_id FROM "). + append(svs.relationship().name()). + append(" WHERE type=?").append(setParameter(parameters, cond.getRelationshipTypeKey())). + append(')'); - return where; + return new AnySearchNode.Leaf(defaultSV(svs), clause.toString()); } - protected StringBuilder buildOrderBy(final OrderBySupport obs) { - StringBuilder orderBy = new StringBuilder(); + protected AnySearchNode getQuery( + final RelationshipCond cond, + final boolean not, + final List parameters, + final SearchSupport svs) { - if (!obs.items.isEmpty()) { - obs.items.forEach(item -> orderBy.append(item.orderBy).append(',')); + Set rightAnyObjects = check(cond); - orderBy.insert(0, " ORDER BY "); - orderBy.deleteCharAt(orderBy.length() - 1); + StringBuilder clause = new StringBuilder(); + if (not) { + clause.append(anyId(svs)).append(" NOT IN ("); + } else { + clause.append(anyId(svs)).append(" IN ("); } + clause.append("SELECT DISTINCT any_id FROM "). + append(svs.relationship().name()).append(" WHERE "). + append(rightAnyObjects.stream(). + map(key -> "right_any_id=?" + setParameter(parameters, key)). + collect(Collectors.joining(" OR "))). + append(')'); - return orderBy; + return new AnySearchNode.Leaf(defaultSV(svs), clause.toString()); } - protected void parseOrderByForPlainSchema( - final SearchSupport svs, - final OrderBySupport obs, - final OrderBySupport.Item item, - final Sort.Order clause, - final PlainSchema schema, - final String fieldName) { + protected AnySearchNode getQuery( + final MembershipCond cond, + final boolean not, + final List parameters, + final SearchSupport svs) { - // keep track of involvement of non-mandatory schemas in the order by clauses - obs.nonMandatorySchemas = !"true".equals(schema.getMandatoryCondition()); + List groupKeys = check(cond); - if (schema.isUniqueConstraint()) { - obs.views.add(svs.asSearchViewSupport().uniqueAttr()); + String subwhere = groupKeys.stream(). + map(key -> "group_id=?" + setParameter(parameters, key)). + collect(Collectors.joining(" OR ")); - item.select = new StringBuilder(). - append(svs.asSearchViewSupport().uniqueAttr().alias()).append('.'). - append(key(schema.getType())). - append(" AS ").append(fieldName).toString(); - item.where = new StringBuilder(). - append(svs.asSearchViewSupport().uniqueAttr().alias()). - append(".schema_id='").append(fieldName).append("'").toString(); - item.orderBy = fieldName + ' ' + clause.getDirection().name(); + StringBuilder clause = new StringBuilder("("); + + if (not) { + clause.append(anyId(svs)).append(" NOT IN ("); } else { - obs.views.add(svs.asSearchViewSupport().attr()); + clause.append(anyId(svs)).append(" IN ("); + } + clause.append("SELECT DISTINCT any_id FROM "). + append(svs.membership().name()).append(" WHERE "). + append(subwhere). + append(") "); - item.select = new StringBuilder(). - append(svs.asSearchViewSupport().attr().alias()).append('.').append(key(schema.getType())). - append(" AS ").append(fieldName).toString(); - item.where = new StringBuilder(). - append(svs.asSearchViewSupport().attr().alias()). - append(".schema_id='").append(fieldName).append("'").toString(); - item.orderBy = fieldName + ' ' + clause.getDirection().name(); + if (not) { + clause.append("AND ").append(anyId(svs)).append(" NOT IN ("); + } else { + clause.append("OR ").append(anyId(svs)).append(" IN ("); } - } - protected void parseOrderByForField( - final SearchSupport svs, - final OrderBySupport.Item item, - final String fieldName, - final Sort.Order clause) { + clause.append("SELECT DISTINCT any_id FROM "). + append(svs.dyngroupmembership().name()).append(" WHERE "). + append(subwhere). + append("))"); - item.select = svs.field().alias() + '.' + fieldName; - item.where = StringUtils.EMPTY; - item.orderBy = svs.field().alias() + '.' + fieldName + ' ' + clause.getDirection().name(); + return new AnySearchNode.Leaf(defaultSV(svs), clause.toString()); } - protected void parseOrderByForCustom( - final SearchSupport svs, - final Sort.Order clause, - final OrderBySupport.Item item, - final OrderBySupport obs) { + protected AnySearchNode getQuery( + final RoleCond cond, + final boolean not, + final List parameters, + final SearchSupport svs) { - // do nothing by default, meant for subclasses - } + StringBuilder clause = new StringBuilder("("); - protected OrderBySupport parseOrderBy( - final SearchSupport svs, - final Stream orderBy) { + if (not) { + clause.append(anyId(svs)).append(" NOT IN ("); + } else { + clause.append(anyId(svs)).append(" IN ("); + } - AnyUtils anyUtils = anyUtilsFactory.getInstance(svs.anyTypeKind); + clause.append("SELECT DISTINCT any_id FROM "). + append(svs.role().name()).append(" WHERE "). + append("role_id=?").append(setParameter(parameters, cond.getRole())). + append(") "); - OrderBySupport obs = new OrderBySupport(); + if (not) { + clause.append("AND ").append(anyId(svs)).append(" NOT IN ("); + } else { + clause.append("OR ").append(anyId(svs)).append(" IN ("); + } - Set orderByUniquePlainSchemas = new HashSet<>(); - Set orderByNonUniquePlainSchemas = new HashSet<>(); - orderBy.forEach(clause -> { - OrderBySupport.Item item = new OrderBySupport.Item(); + clause.append("SELECT DISTINCT any_id FROM "). + append(SearchSupport.dynrolemembership().name()).append(" WHERE "). + append("role_id=?").append(setParameter(parameters, cond.getRole())). + append("))"); - parseOrderByForCustom(svs, clause, item, obs); + return new AnySearchNode.Leaf(defaultSV(svs), clause.toString()); + } - if (item.isEmpty()) { - if (anyUtils.getField(clause.getProperty()).isPresent()) { - String fieldName = clause.getProperty(); + protected AnySearchNode getQuery( + final PrivilegeCond cond, + final boolean not, + final List parameters, + final SearchSupport svs) { - // Adjust field name to column name - if (ArrayUtils.contains(RELATIONSHIP_FIELDS, fieldName)) { - fieldName += "_id"; - } + StringBuilder clause = new StringBuilder("("); - obs.views.add(svs.field()); + if (not) { + clause.append(anyId(svs)).append(" NOT IN ("); + } else { + clause.append(anyId(svs)).append(" IN ("); + } - parseOrderByForField(svs, item, fieldName, clause); - } else { - plainSchemaDAO.findById(clause.getProperty()).ifPresent(schema -> { - if (schema.isUniqueConstraint()) { - orderByUniquePlainSchemas.add(schema.getKey()); - } else { - orderByNonUniquePlainSchemas.add(schema.getKey()); - } - if (orderByUniquePlainSchemas.size() > 1 || orderByNonUniquePlainSchemas.size() > 1) { - SyncopeClientException invalidSearch = - SyncopeClientException.build(ClientExceptionType.InvalidSearchParameters); - invalidSearch.getElements().add("Order by more than one attribute is not allowed; " - + "remove one from " + (orderByUniquePlainSchemas.size() > 1 - ? orderByUniquePlainSchemas : orderByNonUniquePlainSchemas)); - throw invalidSearch; - } - parseOrderByForPlainSchema(svs, obs, item, clause, schema, clause.getProperty()); - }); - } - } - - if (item.isEmpty()) { - LOG.warn("Cannot build any valid clause from {}", clause); - } else { - obs.items.add(item); - } - }); - - return obs; - } - - protected void getQueryForCustomConds( - final SearchCond cond, - final List parameters, - final SearchSupport svs, - final boolean not, - final StringBuilder query) { - - // do nothing by default, leave it open for subclasses - } - - protected void queryOp( - final StringBuilder query, - final String op, - final Pair> leftInfo, - final Pair> rightInfo) { - - String subQuery = leftInfo.getLeft().toString(); - // Add extra parentheses - subQuery = subQuery.replaceFirst("WHERE ", "WHERE ("); - query.append(subQuery). - append(' ').append(op).append(" any_id IN ( ").append(rightInfo.getLeft()).append("))"); - } - - protected Pair> getQuery( - final SearchCond cond, final List parameters, final SearchSupport svs) { - - boolean not = cond.getType() == SearchCond.Type.NOT_LEAF; - - StringBuilder query = new StringBuilder(); - Set involvedPlainAttrs = new HashSet<>(); - - switch (cond.getType()) { - case LEAF, NOT_LEAF -> { - cond.getLeaf(AnyTypeCond.class). - filter(leaf -> AnyTypeKind.ANY_OBJECT == svs.anyTypeKind). - ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs))); - - cond.getLeaf(AuxClassCond.class). - ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs))); - - cond.getLeaf(RelationshipTypeCond.class). - filter(leaf -> AnyTypeKind.GROUP != svs.anyTypeKind). - ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs))); - - cond.getLeaf(RelationshipCond.class). - filter(leaf -> AnyTypeKind.GROUP != svs.anyTypeKind). - ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs))); - - cond.getLeaf(MembershipCond.class). - filter(leaf -> AnyTypeKind.GROUP != svs.anyTypeKind). - ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs))); - - cond.getLeaf(MemberCond.class). - filter(leaf -> AnyTypeKind.GROUP == svs.anyTypeKind). - ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs))); - - cond.getLeaf(RoleCond.class). - filter(leaf -> AnyTypeKind.USER == svs.anyTypeKind). - ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs))); - - cond.getLeaf(PrivilegeCond.class). - filter(leaf -> AnyTypeKind.USER == svs.anyTypeKind). - ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs))); - - cond.getLeaf(DynRealmCond.class). - ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs))); - - cond.getLeaf(ResourceCond.class). - ifPresent(leaf -> query.append(getQuery(leaf, not, parameters, svs))); - - cond.getLeaf(AnyCond.class).ifPresentOrElse( - anyCond -> query.append(getQuery(anyCond, not, parameters, svs)), - () -> cond.getLeaf(AttrCond.class).ifPresent(leaf -> { - query.append(getQuery(leaf, not, parameters, svs)); - try { - involvedPlainAttrs.add(check(leaf, svs.anyTypeKind).getLeft().getKey()); - } catch (IllegalArgumentException e) { - // ignore - } - })); - - // allow for additional search conditions - getQueryForCustomConds(cond, parameters, svs, not, query); - } - - case AND -> { - Pair> leftAndInfo = getQuery(cond.getLeft(), parameters, svs); - involvedPlainAttrs.addAll(leftAndInfo.getRight()); - - Pair> rigthAndInfo = getQuery(cond.getRight(), parameters, svs); - involvedPlainAttrs.addAll(rigthAndInfo.getRight()); - - queryOp(query, "AND", leftAndInfo, rigthAndInfo); - } - - case OR -> { - Pair> leftOrInfo = getQuery(cond.getLeft(), parameters, svs); - involvedPlainAttrs.addAll(leftOrInfo.getRight()); - - Pair> rigthOrInfo = getQuery(cond.getRight(), parameters, svs); - involvedPlainAttrs.addAll(rigthOrInfo.getRight()); - - queryOp(query, "OR", leftOrInfo, rigthOrInfo); - } + clause.append("SELECT DISTINCT any_id FROM "). + append(svs.priv().name()).append(" WHERE "). + append("privilege_id=?").append(setParameter(parameters, cond.getPrivilege())). + append(") "); - default -> { - } + if (not) { + clause.append("AND ").append(anyId(svs)).append(" NOT IN ("); + } else { + clause.append("OR ").append(anyId(svs)).append(" IN ("); } - return Pair.of(query, involvedPlainAttrs); + clause.append("SELECT DISTINCT any_id FROM "). + append(svs.dynpriv().name()).append(" WHERE "). + append("privilege_id=?").append(setParameter(parameters, cond.getPrivilege())). + append("))"); + + return new AnySearchNode.Leaf(defaultSV(svs), clause.toString()); } - protected String getQuery( - final AnyTypeCond cond, + protected AnySearchNode getQuery( + final DynRealmCond cond, final boolean not, final List parameters, final SearchSupport svs) { - StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM "). - append(svs.field().name()).append(" WHERE type_id"); + StringBuilder clause = new StringBuilder("("); if (not) { - query.append("<>"); + clause.append(anyId(svs)).append(" NOT IN ("); } else { - query.append('='); + clause.append(anyId(svs)).append(" IN ("); } - query.append('?').append(setParameter(parameters, cond.getAnyTypeKey())); + clause.append("SELECT DISTINCT any_id FROM "). + append(SearchSupport.dynrealmmembership().name()).append(" WHERE "). + append("dynRealm_id=?").append(setParameter(parameters, cond.getDynRealm())). + append("))"); - return query.toString(); + return new AnySearchNode.Leaf(defaultSV(svs), clause.toString()); } - protected String getQuery( - final AuxClassCond cond, + protected AnySearchNode getQuery( + final ResourceCond cond, final boolean not, final List parameters, final SearchSupport svs) { - StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM "). - append(svs.field().name()).append(" WHERE "); + StringBuilder clause = new StringBuilder(); if (not) { - query.append("any_id NOT IN ("); + clause.append(anyId(svs)).append(" NOT IN ("); } else { - query.append("any_id IN ("); + clause.append(anyId(svs)).append(" IN ("); } - query.append("SELECT DISTINCT any_id FROM "). - append(svs.auxClass().name()). - append(" WHERE anyTypeClass_id=?"). - append(setParameter(parameters, cond.getAuxClass())). - append(')'); + clause.append("SELECT DISTINCT any_id FROM "). + append(svs.resource().name()). + append(" WHERE resource_id=?"). + append(setParameter(parameters, cond.getResource())); - return query.toString(); + if (svs.anyTypeKind == AnyTypeKind.USER || svs.anyTypeKind == AnyTypeKind.ANY_OBJECT) { + clause.append(" UNION SELECT DISTINCT any_id FROM "). + append(svs.groupResource().name()). + append(" WHERE resource_id=?"). + append(setParameter(parameters, cond.getResource())); + } + + clause.append(')'); + + return new AnySearchNode.Leaf(defaultSV(svs), clause.toString()); } - protected String getQuery( - final RelationshipTypeCond cond, + protected AnySearchNode getQuery( + final MemberCond cond, final boolean not, final List parameters, final SearchSupport svs) { - StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM "). - append(svs.field().name()).append(" WHERE "); + Set members = check(cond); + + StringBuilder clause = new StringBuilder(); if (not) { - query.append("any_id NOT IN ("); + clause.append(anyId(svs)).append(" NOT IN ("); } else { - query.append("any_id IN ("); + clause.append(anyId(svs)).append(" IN ("); } - query.append("SELECT any_id ").append("FROM "). - append(svs.relationship().name()). - append(" WHERE type=?").append(setParameter(parameters, cond.getRelationshipTypeKey())). - append(" UNION SELECT right_any_id AS any_id FROM "). - append(svs.relationship().name()). - append(" WHERE type=?").append(setParameter(parameters, cond.getRelationshipTypeKey())). - append(')'); - - return query.toString(); - } - - protected String getQuery( - final RelationshipCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { - - Set rightAnyObjects = check(cond); - - StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM "). - append(svs.field().name()).append(" WHERE "); + clause.append("SELECT DISTINCT group_id AS any_id FROM "). + append(new SearchSupport(AnyTypeKind.USER).membership().name()).append(" WHERE "). + append(members.stream(). + map(key -> "any_id=?" + setParameter(parameters, key)). + collect(Collectors.joining(" OR "))). + append(") "); if (not) { - query.append("any_id NOT IN ("); + clause.append("AND ").append(anyId(svs)).append(" NOT IN ("); } else { - query.append("any_id IN ("); + clause.append("OR ").append(anyId(svs)).append(" IN ("); } - query.append("SELECT DISTINCT any_id FROM "). - append(svs.relationship().name()).append(" WHERE "). - append(rightAnyObjects.stream(). - map(key -> "right_any_id=?" + setParameter(parameters, key)). + clause.append("SELECT DISTINCT group_id AS any_id FROM "). + append(new SearchSupport(AnyTypeKind.ANY_OBJECT).membership().name()).append(" WHERE "). + append(members.stream(). + map(key -> "any_id=?" + setParameter(parameters, key)). collect(Collectors.joining(" OR "))). append(')'); - return query.toString(); + return new AnySearchNode.Leaf(defaultSV(svs), clause.toString()); } - protected String getQuery( - final MembershipCond cond, + protected AnySearchNode.Leaf fillAttrQuery( + final String column, + final SearchSupport.SearchView from, + final PlainAttrValue attrValue, + final PlainSchema schema, + final AttrCond cond, final boolean not, - final List parameters, - final SearchSupport svs) { + final List parameters) { - List groupKeys = check(cond); + // activate ignoreCase only for EQ and LIKE operators + boolean ignoreCase = AttrCond.Type.ILIKE == cond.getType() || AttrCond.Type.IEQ == cond.getType(); - String where = groupKeys.stream(). - map(key -> "group_id=?" + setParameter(parameters, key)). - collect(Collectors.joining(" OR ")); + String left = column; + if (ignoreCase && (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum)) { + left = "LOWER(" + left + ')'; + } - StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM "). - append(svs.field().name()).append(" WHERE ("); + StringBuilder clause = new StringBuilder(left); + switch (cond.getType()) { - if (not) { - query.append("any_id NOT IN ("); - } else { - query.append("any_id IN ("); - } + case ILIKE: + case LIKE: + if (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) { + if (not) { + clause.append(" NOT "); + } + clause.append(" LIKE "); + if (ignoreCase) { + clause.append("LOWER(?").append(setParameter(parameters, cond.getExpression())).append(')'); + } else { + clause.append('?').append(setParameter(parameters, cond.getExpression())); + } + if (isOracle()) { + clause.append(" ESCAPE '\\'"); + } + } else { + LOG.error("LIKE is only compatible with string or enum schemas"); + return new AnySearchNode.Leaf(from, ALWAYS_FALSE_CLAUSE); + } + break; - query.append("SELECT DISTINCT any_id FROM "). - append(svs.membership().name()).append(" WHERE "). - append(where). - append(") "); + case IEQ: + case EQ: + default: + if (not) { + clause.append("<>"); + } else { + clause.append('='); + } + if (ignoreCase + && (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum)) { - if (not) { - query.append("AND any_id NOT IN ("); - } else { - query.append("OR any_id IN ("); - } + clause.append("LOWER(?").append(setParameter(parameters, attrValue.getValue())).append(')'); + } else { + clause.append('?').append(setParameter(parameters, attrValue.getValue())); + } + break; - query.append("SELECT DISTINCT any_id FROM "). - append(svs.dyngroupmembership().name()).append(" WHERE "). - append(where). - append("))"); + case GE: + if (not) { + clause.append('<'); + } else { + clause.append(">="); + } + clause.append('?').append(setParameter(parameters, attrValue.getValue())); + break; - return query.toString(); + case GT: + if (not) { + clause.append("<="); + } else { + clause.append('>'); + } + clause.append('?').append(setParameter(parameters, attrValue.getValue())); + break; + + case LE: + if (not) { + clause.append('>'); + } else { + clause.append("<="); + } + clause.append('?').append(setParameter(parameters, attrValue.getValue())); + break; + + case LT: + if (not) { + clause.append(">="); + } else { + clause.append('<'); + } + clause.append('?').append(setParameter(parameters, attrValue.getValue())); + break; + } + + return new AnySearchNode.Leaf( + from, + cond instanceof AnyCond + ? clause.toString() + : from.alias() + ".schema_id='" + schema.getKey() + "' AND " + clause); } - protected String getQuery( - final RoleCond cond, + protected Pair getQuery( + final AttrCond cond, final boolean not, + final Pair checked, final List parameters, final SearchSupport svs) { - StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM "). - append(svs.field().name()).append(" WHERE ("); - + // normalize NULL / NOT NULL checks if (not) { - query.append("any_id NOT IN ("); - } else { - query.append("any_id IN ("); + if (cond.getType() == AttrCond.Type.ISNULL) { + cond.setType(AttrCond.Type.ISNOTNULL); + } else if (cond.getType() == AttrCond.Type.ISNOTNULL) { + cond.setType(AttrCond.Type.ISNULL); + } } - query.append("SELECT DISTINCT any_id FROM "). - append(svs.role().name()).append(" WHERE "). - append("role_id=?").append(setParameter(parameters, cond.getRole())). - append(") "); + SearchSupport.SearchView sv = checked.getLeft().isUniqueConstraint() + ? svs.asSearchViewSupport().uniqueAttr() + : svs.asSearchViewSupport().attr(); - if (not) { - query.append("AND any_id NOT IN ("); - } else { - query.append("OR any_id IN ("); - } + switch (cond.getType()) { + case ISNOTNULL -> { + return Pair.of(true, new AnySearchNode.Leaf( + sv, + sv.alias() + ".schema_id='" + checked.getLeft().getKey() + "'")); + } - query.append("SELECT DISTINCT any_id FROM "). - append(SearchSupport.dynrolemembership().name()).append(" WHERE "). - append("role_id=?").append(setParameter(parameters, cond.getRole())). - append("))"); + case ISNULL -> { + String clause = new StringBuilder(anyId(svs)).append(" NOT IN "). + append('('). + append("SELECT DISTINCT any_id FROM "). + append(sv.name()). + append(" WHERE schema_id=").append("'").append(checked.getLeft().getKey()).append("'"). + append(')').toString(); + return Pair.of(true, new AnySearchNode.Leaf(defaultSV(svs), clause)); + } - return query.toString(); + default -> { + AnySearchNode.Leaf node; + if (not && checked.getLeft().isMultivalue()) { + AnySearchNode.Leaf notNode = fillAttrQuery( + sv.alias() + "." + key(checked.getLeft().getType()), + sv, + checked.getRight(), + checked.getLeft(), + cond, + false, + parameters); + node = new AnySearchNode.Leaf( + sv, + anyId(svs) + " NOT IN (" + + "SELECT any_id FROM " + sv.name() + + " WHERE " + notNode.getClause().replace(sv.alias() + ".", "") + + ")"); + } else { + node = fillAttrQuery( + sv.alias() + "." + key(checked.getLeft().getType()), + sv, + checked.getRight(), + checked.getLeft(), + cond, + not, + parameters); + } + return Pair.of(true, node); + } + } } - protected String getQuery( - final PrivilegeCond cond, + protected AnySearchNode getQuery( + final AnyCond cond, final boolean not, final List parameters, final SearchSupport svs) { - StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM "). - append(svs.field().name()).append(" WHERE ("); + if (JAXRSService.PARAM_REALM.equals(cond.getSchema()) + && !SyncopeConstants.UUID_PATTERN.matcher(cond.getExpression()).matches()) { - if (not) { - query.append("any_id NOT IN ("); - } else { - query.append("any_id IN ("); + Realm realm = realmSearchDAO.findByFullPath(cond.getExpression()). + orElseThrow(() -> new IllegalArgumentException("Invalid Realm full path: " + cond.getExpression())); + cond.setExpression(realm.getKey()); } - query.append("SELECT DISTINCT any_id FROM "). - append(svs.priv().name()).append(" WHERE "). - append("privilege_id=?").append(setParameter(parameters, cond.getPrivilege())). - append(") "); + Triple checked = check(cond, svs.anyTypeKind); - if (not) { - query.append("AND any_id NOT IN ("); - } else { - query.append("OR any_id IN ("); - } + return switch (checked.getRight().getType()) { + case ISNULL -> + new AnySearchNode.Leaf( + defaultSV(svs), + checked.getRight().getSchema() + (not ? " IS NOT NULL" : " IS NULL")); + + case ISNOTNULL -> + new AnySearchNode.Leaf( + defaultSV(svs), + checked.getRight().getSchema() + (not ? " IS NULL" : " IS NOT NULL")); + + default -> + fillAttrQuery( + checked.getRight().getSchema(), + defaultSV(svs), + checked.getMiddle(), + checked.getLeft(), + checked.getRight(), + not, + parameters); + }; + } - query.append("SELECT DISTINCT any_id FROM "). - append(svs.dynpriv().name()).append(" WHERE "). - append("privilege_id=?").append(setParameter(parameters, cond.getPrivilege())). - append("))"); + protected AnySearchNode.Leaf buildAdminRealmsFilter( + final Set realmKeys, + final SearchSupport svs, + final List parameters) { - return query.toString(); + if (realmKeys.isEmpty()) { + return new AnySearchNode.Leaf(defaultSV(svs), StringUtils.substringAfter(anyId(svs), '.') + " IS NOT NULL"); + } + + String realmKeysArg = realmKeys.stream(). + map(realmKey -> "?" + setParameter(parameters, realmKey)). + collect(Collectors.joining(",")); + return new AnySearchNode.Leaf(defaultSV(svs), "realm_id IN (" + realmKeysArg + ")"); } - protected String getQuery( - final DynRealmCond cond, - final boolean not, + protected Triple, Set> getAdminRealmsFilter( + final Realm base, + final boolean recursive, + final Set adminRealms, final List parameters, final SearchSupport svs) { - StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM "). - append(svs.field().name()).append(" WHERE ("); + Set realmKeys = new HashSet<>(); + Set dynRealmKeys = new HashSet<>(); + Set groupOwners = new HashSet<>(); - if (not) { - query.append("any_id NOT IN ("); + if (recursive) { + adminRealms.forEach(realmPath -> RealmUtils.parseGroupOwnerRealm(realmPath).ifPresentOrElse( + goRealm -> groupOwners.add(goRealm.getRight()), + () -> { + if (realmPath.startsWith("/")) { + Realm realm = realmSearchDAO.findByFullPath(realmPath).orElseThrow(() -> { + SyncopeClientException noRealm = + SyncopeClientException.build(ClientExceptionType.InvalidRealm); + noRealm.getElements().add("Invalid realm specified: " + realmPath); + return noRealm; + }); + + realmKeys.addAll(realmSearchDAO.findDescendants(realm.getFullPath(), base.getFullPath())); + } else { + dynRealmDAO.findById(realmPath).ifPresentOrElse( + dynRealm -> dynRealmKeys.add(dynRealm.getKey()), + () -> LOG.warn("Ignoring invalid dynamic realm {}", realmPath)); + } + })); + if (!dynRealmKeys.isEmpty()) { + realmKeys.clear(); + } } else { - query.append("any_id IN ("); + if (adminRealms.stream().anyMatch(r -> r.startsWith(base.getFullPath()))) { + realmKeys.add(base.getKey()); + } } - query.append("SELECT DISTINCT any_id FROM "). - append(SearchSupport.dynrealmmembership().name()).append(" WHERE "). - append("dynRealm_id=?").append(setParameter(parameters, cond.getDynRealm())). - append("))"); - - return query.toString(); + return Triple.of(buildAdminRealmsFilter(realmKeys, svs, parameters), dynRealmKeys, groupOwners); } - protected String getQuery( - final ResourceCond cond, - final boolean not, - final List parameters, + protected void visitNode( + final AnySearchNode node, + final Map counters, + final Set from, + final List where, final SearchSupport svs) { - StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM "). - append(svs.field().name()).append(" WHERE "); + node.asLeaf().ifPresentOrElse( + leaf -> { + from.add(leaf.getFrom()); - if (not) { - query.append("any_id NOT IN ("); - } else { - query.append("any_id IN ("); - } + if (counters.computeIfAbsent(leaf.getFrom(), view -> false) && !leaf.getClause().contains(" IN ")) { + where.add(anyId(svs) + " IN (" + + "SELECT any_id FROM " + leaf.getFrom().name() + + " WHERE " + leaf.getClause().replace(leaf.getFrom().alias() + ".", "") + + ")"); + } else { + counters.put(leaf.getFrom(), true); + where.add(leaf.getClause()); + } + }, + () -> { + List nodeWhere = new ArrayList<>(); + node.getChildren().forEach(child -> visitNode(child, counters, from, nodeWhere, svs)); + where.add(nodeWhere.stream(). + map(w -> "(" + w + ")"). + collect(Collectors.joining(" " + node.getType().name() + " "))); + }); + } - query.append("SELECT DISTINCT any_id FROM "). - append(svs.resource().name()). - append(" WHERE resource_id=?"). - append(setParameter(parameters, cond.getResource())); + protected String buildFrom( + final Set from, + final Set plainSchemas, + final OrderBySupport obs) { - if (svs.anyTypeKind == AnyTypeKind.USER || svs.anyTypeKind == AnyTypeKind.ANY_OBJECT) { - query.append(" UNION SELECT DISTINCT any_id FROM "). - append(svs.groupResource().name()). - append(" WHERE resource_id=?"). - append(setParameter(parameters, cond.getResource())); + String fromString; + if (from.size() == 1) { + SearchSupport.SearchView sv = from.iterator().next(); + fromString = sv.name() + " " + sv.alias(); + } else { + List joins = new ArrayList<>(from); + StringBuilder join = new StringBuilder(joins.get(0).name() + " " + joins.get(0).alias()); + for (int i = 1; i < joins.size(); i++) { + SearchSupport.SearchView sv = joins.get(i); + join.append(" LEFT JOIN "). + append(sv.name()).append(" ").append(sv.alias()). + append(" ON "). + append(joins.get(0).alias()).append(".any_id=").append(sv.alias()).append(".any_id"); + } + fromString = join.toString(); } + return fromString; + } - query.append(')'); - - return query.toString(); + protected String buildWhere(final List where, final AnySearchNode root) { + return where.stream(). + map(w -> "(" + w + ")"). + collect(Collectors.joining(" " + root.getType().name() + " ")); } - protected String getQuery( - final MemberCond cond, - final boolean not, + protected String buildCountQuery( + final Pair> queryInfo, + final AnySearchNode.Leaf filterNode, final List parameters, final SearchSupport svs) { - Set members = check(cond); - - StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM "). - append(svs.field().name()).append(" WHERE "); - - if (not) { - query.append("any_id NOT IN ("); + AnySearchNode root; + if (queryInfo.getLeft().getType() == AnySearchNode.Type.AND) { + root = queryInfo.getLeft(); } else { - query.append("any_id IN ("); + root = new AnySearchNode(AnySearchNode.Type.AND); + root.add(queryInfo.getLeft()); } + root.add(filterNode); - query.append("SELECT DISTINCT group_id AS any_id FROM "). - append(new SearchSupport(AnyTypeKind.USER).membership().name()).append(" WHERE "). - append(members.stream(). - map(key -> "any_id=?" + setParameter(parameters, key)). - collect(Collectors.joining(" OR "))). - append(") "); + Set from = new HashSet<>(); + List where = new ArrayList<>(); + Map counters = new HashMap<>(); + visitNode(root, counters, from, where, svs); - if (not) { - query.append("AND any_id NOT IN ("); - } else { - query.append("OR any_id IN ("); + StringBuilder queryString = new StringBuilder("SELECT COUNT(DISTINCT ").append(anyId(svs)).append(") "); + + queryString.append("FROM ").append(buildFrom(from, queryInfo.getRight(), null)); + + queryString.append(" WHERE ").append(buildWhere(where, root)); + + LOG.debug("Query: {}, parameters: {}", queryString, parameters); + + return queryString.toString(); + } + + @Override + protected long doCount( + final Realm base, + final boolean recursive, + final Set adminRealms, + final SearchCond cond, + final AnyTypeKind kind) { + + List parameters = new ArrayList<>(); + + SearchSupport svs = new SearchViewSupport(kind); + + // 1. get admin realms filter + Triple, Set> filter = + getAdminRealmsFilter(base, recursive, adminRealms, parameters, svs); + + // 2. transform search condition + Optional>> optionalQueryInfo = getQuery( + buildEffectiveCond(cond, filter.getMiddle(), filter.getRight(), kind), parameters, svs); + if (optionalQueryInfo.isEmpty()) { + LOG.error("Invalid search condition: {}", cond); + return 0; } + Pair> queryInfo = optionalQueryInfo.get(); - query.append("SELECT DISTINCT group_id AS any_id FROM "). - append(new SearchSupport(AnyTypeKind.ANY_OBJECT).membership().name()).append(" WHERE "). - append(members.stream(). - map(key -> "any_id=?" + setParameter(parameters, key)). - collect(Collectors.joining(" OR "))). - append(')'); + // 3. generate the query string + String queryString = buildCountQuery(queryInfo, filter.getLeft(), parameters, svs); + + // 4. populate the search query with parameter values + Query countQuery = entityManager.createNativeQuery(queryString); + fillWithParameters(countQuery, parameters); - return query.toString(); + // 5. execute the query and return the result + return ((Number) countQuery.getSingleResult()).intValue(); } - protected void fillAttrQuery( - final StringBuilder query, - final PlainAttrValue attrValue, + protected void parseOrderByForPlainSchema( + final SearchSupport svs, + final OrderBySupport obs, + final OrderBySupport.Item item, + final Sort.Order clause, final PlainSchema schema, - final AttrCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { + final String fieldName) { - // This first branch is required for handling with not conditions given on multivalue fields (SYNCOPE-1419) - if (not && schema.isMultivalue() - && !(cond instanceof AnyCond) - && cond.getType() != AttrCond.Type.ISNULL && cond.getType() != AttrCond.Type.ISNOTNULL) { - - query.append("any_id NOT IN (SELECT DISTINCT any_id FROM "). - append((schema.isUniqueConstraint() - ? svs.asSearchViewSupport().uniqueAttr().name() - : svs.asSearchViewSupport().attr().name())). - append(" WHERE schema_id='").append(schema.getKey()); - fillAttrQuery(query, attrValue, schema, cond, false, parameters, svs); - query.append(')'); + // keep track of involvement of non-mandatory schemas in the order by clauses + obs.nonMandatorySchemas = !"true".equals(schema.getMandatoryCondition()); + + if (schema.isUniqueConstraint()) { + obs.views.add(svs.asSearchViewSupport().uniqueAttr()); + + item.select = new StringBuilder(). + append(svs.asSearchViewSupport().uniqueAttr().alias()).append('.'). + append(key(schema.getType())). + append(" AS ").append(fieldName).toString(); + item.where = new StringBuilder(). + append(svs.asSearchViewSupport().uniqueAttr().alias()). + append(".schema_id='").append(fieldName).append("'").toString(); + item.orderBy = fieldName + ' ' + clause.getDirection().name(); } else { - // activate ignoreCase only for EQ and LIKE operators - boolean ignoreCase = AttrCond.Type.ILIKE == cond.getType() || AttrCond.Type.IEQ == cond.getType(); + obs.views.add(svs.asSearchViewSupport().attr()); - String column = (cond instanceof AnyCond) ? cond.getSchema() : key(schema.getType()); - if (schema.getType().isStringClass() && ignoreCase) { - column = "LOWER (" + column + ')'; - } - if (!(cond instanceof AnyCond)) { - column = "' AND " + column; - } + item.select = new StringBuilder(). + append(svs.asSearchViewSupport().attr().alias()).append('.').append(key(schema.getType())). + append(" AS ").append(fieldName).toString(); + item.where = new StringBuilder(). + append(svs.asSearchViewSupport().attr().alias()). + append(".schema_id='").append(fieldName).append("'").toString(); + item.orderBy = fieldName + ' ' + clause.getDirection().name(); + } + } - switch (cond.getType()) { + protected void parseOrderByForField( + final SearchSupport svs, + final OrderBySupport.Item item, + final String fieldName, + final Sort.Order clause) { - case ISNULL -> - query.append(column).append(not - ? " IS NOT NULL" - : " IS NULL"); + item.select = defaultSV(svs).alias() + '.' + fieldName; + item.where = StringUtils.EMPTY; + item.orderBy = defaultSV(svs).alias() + '.' + fieldName + ' ' + clause.getDirection().name(); + } - case ISNOTNULL -> - query.append(column).append(not - ? " IS NULL" - : " IS NOT NULL"); + protected void parseOrderByForCustom( + final SearchSupport svs, + final Sort.Order clause, + final OrderBySupport.Item item, + final OrderBySupport obs) { - case ILIKE, LIKE -> { - if (schema.getType().isStringClass()) { - query.append(column); - if (not) { - query.append(" NOT "); - } - query.append(" LIKE "); - if (ignoreCase) { - query.append("LOWER(?").append(setParameter(parameters, cond.getExpression())).append(')'); - } else { - query.append('?').append(setParameter(parameters, cond.getExpression())); - } - // workaround for Oracle DB adding explicit escaping string, to search - // for literal _ (underscore) (SYNCOPE-1779) - if (isOracle()) { - query.append(" ESCAPE '\\' "); - } - } else { - if (!(cond instanceof AnyCond)) { - query.append("' AND"); - } - query.append(" 1=2"); - LOG.error("LIKE is only compatible with string or enum schemas"); - } - } - case IEQ, EQ -> { - query.append(column); - if (not) { - query.append("<>"); - } else { - query.append('='); - } - if (schema.getType().isStringClass() && ignoreCase) { - query.append("LOWER(?").append(setParameter(parameters, attrValue.getValue())).append(')'); - } else { - query.append('?').append(setParameter(parameters, attrValue.getValue())); - } - } + // do nothing by default, meant for subclasses + } - case GE -> { - query.append(column); - if (not) { - query.append('<'); - } else { - query.append(">="); - } - query.append('?').append(setParameter(parameters, attrValue.getValue())); - } - case GT -> { - query.append(column); - if (not) { - query.append("<="); - } else { - query.append('>'); - } - query.append('?').append(setParameter(parameters, attrValue.getValue())); - } + protected OrderBySupport parseOrderBy( + final SearchSupport svs, + final List orderBy) { - case LE -> { - query.append(column); - if (not) { - query.append('>'); - } else { - query.append("<="); - } - query.append('?').append(setParameter(parameters, attrValue.getValue())); - } + AnyUtils anyUtils = anyUtilsFactory.getInstance(svs.anyTypeKind); - case LT -> { - query.append(column); - if (not) { - query.append(">="); - } else { - query.append('<'); - } - query.append('?').append(setParameter(parameters, attrValue.getValue())); - } + OrderBySupport obs = new OrderBySupport(); - default -> { - } + Set orderByUniquePlainSchemas = new HashSet<>(); + Set orderByNonUniquePlainSchemas = new HashSet<>(); + orderBy.forEach(clause -> { + OrderBySupport.Item item = new OrderBySupport.Item(); + + parseOrderByForCustom(svs, clause, item, obs); + + if (item.isEmpty()) { + anyUtils.getField(clause.getProperty()).ifPresentOrElse( + field -> { + // Manage difference among external key attribute and internal JPA @Id + String fieldName = "key".equals(clause.getProperty()) ? "id" : clause.getProperty(); + + // Adjust field name to column name + if (ArrayUtils.contains(RELATIONSHIP_FIELDS, fieldName)) { + fieldName += "_id"; + } + + obs.views.add(defaultSV(svs)); + + parseOrderByForField(svs, item, fieldName, clause); + }, + () -> { + plainSchemaDAO.findById(clause.getProperty()).ifPresent(schema -> { + if (schema.isUniqueConstraint()) { + orderByUniquePlainSchemas.add(schema.getKey()); + } else { + orderByNonUniquePlainSchemas.add(schema.getKey()); + } + if (orderByUniquePlainSchemas.size() > 1 || orderByNonUniquePlainSchemas.size() > 1) { + throw syncopeClientException("Order by more than one attribute is not allowed; " + + "remove one from " + (orderByUniquePlainSchemas.size() > 1 + ? orderByUniquePlainSchemas : orderByNonUniquePlainSchemas)).get(); + } + parseOrderByForPlainSchema(svs, obs, item, clause, schema, clause.getProperty()); + }); + }); } - } + + if (item.isEmpty()) { + LOG.warn("Cannot build any valid clause from {}", clause); + } else { + obs.items.add(item); + } + }); + + return obs; } - protected String getQuery( - final AttrCond cond, - final boolean not, + protected String buildSearchQuery( + final Pair> queryInfo, + final AnySearchNode.Leaf filterNode, final List parameters, - final SearchSupport svs) { + final SearchSupport svs, + final List orderBy) { - Pair checked = check(cond, svs.anyTypeKind); + AnySearchNode root; + if (queryInfo.getLeft().getType() == AnySearchNode.Type.AND) { + root = queryInfo.getLeft(); + } else { + root = new AnySearchNode(AnySearchNode.Type.AND); + root.add(queryInfo.getLeft()); + } + root.add(filterNode); - StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM "); - switch (cond.getType()) { - case ISNOTNULL: - query.append(checked.getLeft().isUniqueConstraint() - ? svs.asSearchViewSupport().uniqueAttr().name() - : svs.asSearchViewSupport().attr().name()). - append(" WHERE schema_id=").append("'").append(checked.getLeft().getKey()).append("'"); - break; + Set from = new HashSet<>(); + List where = new ArrayList<>(); + Map counters = new HashMap<>(); + visitNode(root, counters, from, where, svs); - case ISNULL: - query.append(svs.field().name()). - append(" WHERE any_id NOT IN "). - append('('). - append("SELECT DISTINCT any_id FROM "). - append(checked.getLeft().isUniqueConstraint() - ? svs.asSearchViewSupport().uniqueAttr().name() - : svs.asSearchViewSupport().attr().name()). - append(" WHERE schema_id=").append("'").append(checked.getLeft().getKey()).append("'"). - append(')'); - break; + // 3. take ordering into account + OrderBySupport obs = parseOrderBy(svs, orderBy); - default: - if (not && !(cond instanceof AnyCond) && checked.getLeft().isMultivalue()) { - query.append(svs.field().name()).append(" WHERE "); - } else { - query.append((checked.getLeft().isUniqueConstraint() - ? svs.asSearchViewSupport().uniqueAttr().name() - : svs.asSearchViewSupport().attr().name())). - append(" WHERE schema_id='").append(checked.getLeft().getKey()); - } - fillAttrQuery(query, checked.getRight(), checked.getLeft(), cond, not, parameters, svs); + // 4. generate the query string + StringBuilder queryString = new StringBuilder("SELECT DISTINCT ").append(anyId(svs)); + obs.items.forEach(item -> queryString.append(',').append(item.select)); + + from.addAll(obs.views); + queryString.append(" FROM ").append(buildFrom(from, queryInfo.getRight(), obs)); + + queryString.append(" WHERE ").append(buildWhere(where, root)); + + if (!obs.items.isEmpty()) { + queryString.append(" ORDER BY "). + append(obs.items.stream().map(item -> item.orderBy).collect(Collectors.joining(","))); } - return query.toString(); + LOG.debug("Query: {}, parameters: {}", queryString, parameters); + + return queryString.toString(); } - protected String getQuery( - final AnyCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { + @Override + @SuppressWarnings("unchecked") + protected > List doSearch( + final Realm base, + final boolean recursive, + final Set adminRealms, + final SearchCond cond, + final Pageable pageable, + final AnyTypeKind kind) { - if (JAXRSService.PARAM_REALM.equals(cond.getSchema()) - && !SyncopeConstants.UUID_PATTERN.matcher(cond.getExpression()).matches()) { + List parameters = new ArrayList<>(); - Realm realm = realmSearchDAO.findByFullPath(cond.getExpression()). - orElseThrow(() -> new IllegalArgumentException("Invalid Realm full path: " + cond.getExpression())); - cond.setExpression(realm.getKey()); + SearchSupport svs = new SearchViewSupport(kind); + + // 1. get admin realms filter + Triple, Set> filter = + getAdminRealmsFilter(base, recursive, adminRealms, parameters, svs); + + // 2. transform search condition + Optional>> optionalQueryInfo = getQuery( + buildEffectiveCond(cond, filter.getMiddle(), filter.getRight(), kind), parameters, svs); + if (optionalQueryInfo.isEmpty()) { + LOG.error("Invalid search condition: {}", cond); + return List.of(); } + Pair> queryInfo = optionalQueryInfo.get(); - Triple checked = check(cond, svs.anyTypeKind); + // 3. generate the query string + String queryString = buildSearchQuery( + queryInfo, filter.getLeft(), parameters, svs, pageable.getSort().toList()); + + // 4. prepare the search query + Query query = entityManager.createNativeQuery(queryString); - StringBuilder query = new StringBuilder("SELECT DISTINCT any_id FROM "). - append(svs.field().name()).append(" WHERE "); + if (pageable.isPaged()) { + query.setFirstResult(pageable.getPageSize() * pageable.getPageNumber()); + query.setMaxResults(pageable.getPageSize()); + } - fillAttrQuery(query, checked.getMiddle(), checked.getLeft(), checked.getRight(), not, parameters, svs); + // 5. populate the search query with parameter values + fillWithParameters(query, parameters); - return query.toString(); + // 6. prepare the result (avoiding duplicates) + return buildResult(query.getResultList(), kind); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AnyFinder.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AnyFinder.java deleted file mode 100644 index 8b962e652b..0000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AnyFinder.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.dao; - -import jakarta.persistence.EntityManager; -import jakarta.persistence.Query; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.StringJoiner; -import java.util.regex.Pattern; -import org.apache.commons.jexl3.parser.Parser; -import org.apache.commons.jexl3.parser.ParserConstants; -import org.apache.commons.jexl3.parser.Token; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.syncope.common.lib.types.AnyTypeKind; -import org.apache.syncope.common.lib.types.AttrSchemaType; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; -import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.persistence.api.entity.AnyUtils; -import org.apache.syncope.core.persistence.api.entity.DerSchema; -import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; -import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.transaction.annotation.Transactional; - -public abstract class AnyFinder { - - protected static final Logger LOG = LoggerFactory.getLogger(AnyFinder.class); - - /** - * Split an attribute value recurring on provided literals/tokens. - * - * @param attrValue value to be split - * @param literals literals/tokens - * @return split value - */ - protected static List split(final String attrValue, final List literals) { - final List attrValues = new ArrayList<>(); - - if (literals.isEmpty()) { - attrValues.add(attrValue); - } else { - for (String token : attrValue.split(Pattern.quote(literals.get(0)))) { - if (!token.isEmpty()) { - attrValues.addAll(split(token, literals.subList(1, literals.size()))); - } - } - } - - return attrValues; - } - - protected final PlainSchemaDAO plainSchemaDAO; - - protected final EntityManager entityManager; - - protected AnyFinder(final PlainSchemaDAO plainSchemaDAO, final EntityManager entityManager) { - this.plainSchemaDAO = plainSchemaDAO; - this.entityManager = entityManager; - } - - protected String view(final String table) { - return StringUtils.containsIgnoreCase(table, AnyTypeKind.USER.name()) - ? "user_search" - : StringUtils.containsIgnoreCase(table, AnyTypeKind.GROUP.name()) - ? "group_search" - : "anyObject_search"; - } - - protected abstract String queryBegin(String table); - - protected Pair schemaInfo(final AttrSchemaType schemaType, final boolean ignoreCaseMatch) { - String key; - boolean lower = false; - - switch (schemaType) { - case Boolean: - key = "booleanValue"; - break; - - case Date: - key = "dateValue"; - break; - - case Double: - key = "doubleValue"; - break; - - case Long: - key = "longValue"; - break; - - case Binary: - key = "binaryValue"; - break; - - default: - lower = ignoreCaseMatch; - key = "stringValue"; - } - - return Pair.of(key, lower); - } - - protected abstract String attrValueMatch( - AnyUtils anyUtils, - PlainSchema schema, - PlainAttrValue attrValue, - boolean ignoreCaseMatch); - - protected Object getAttrValue( - final PlainSchema schema, - final PlainAttrValue attrValue, - final boolean ignoreCaseMatch) { - - return attrValue.getValue(); - } - - protected > List buildResult(final AnyUtils anyUtils, final List queryResult) { - List result = new ArrayList<>(); - queryResult.forEach(anyKey -> anyUtils.dao().findById(anyKey.toString()).ifPresentOrElse( - result::add, - () -> LOG.error("Could not find any for key {}", anyKey))); - return result; - } - - protected String plainAttrQuery( - final String table, - final AnyUtils anyUtils, - final PlainSchema schema, - final PlainAttrValue attrValue, - final boolean ignoreCaseMatch, - final List queryParams) { - - queryParams.add(schema.getKey()); - queryParams.add(getAttrValue(schema, attrValue, ignoreCaseMatch)); - - return queryBegin(table) + "WHERE " + attrValueMatch(anyUtils, schema, attrValue, ignoreCaseMatch); - } - - @SuppressWarnings("unchecked") - @Transactional(readOnly = true) - public > List findByPlainAttrValue( - final String table, - final AnyUtils anyUtils, - final PlainSchema schema, - final PlainAttrValue attrValue, - final boolean ignoreCaseMatch) { - - if (schema == null) { - LOG.error("No PlainSchema"); - return List.of(); - } - - List queryParams = new ArrayList<>(); - Query query = entityManager.createNativeQuery( - plainAttrQuery(table, anyUtils, schema, attrValue, ignoreCaseMatch, queryParams)); - for (int i = 0; i < queryParams.size(); i++) { - query.setParameter(i + 1, queryParams.get(i)); - } - - return buildResult(anyUtils, query.getResultList()); - } - - @Transactional(readOnly = true) - public > Optional findByPlainAttrUniqueValue( - final String table, - final AnyUtils anyUtils, - final PlainSchema schema, - final PlainAttrUniqueValue attrUniqueValue, - final boolean ignoreCaseMatch) { - - if (schema == null) { - LOG.error("No PlainSchema"); - return Optional.empty(); - } - if (!schema.isUniqueConstraint()) { - LOG.error("This schema has not unique constraint: '{}'", schema.getKey()); - return Optional.empty(); - } - - List result = findByPlainAttrValue(table, anyUtils, schema, attrUniqueValue, ignoreCaseMatch); - return result.isEmpty() - ? Optional.empty() - : Optional.of(result.get(0)); - } - - @SuppressWarnings("unchecked") - private List findByDerAttrValue( - final String table, - final Map> clauses) { - - StringJoiner actualClauses = new StringJoiner(" AND id IN "); - List queryParams = new ArrayList<>(); - - clauses.forEach((clause, parameters) -> { - actualClauses.add(clause); - queryParams.addAll(parameters); - }); - - Query query = entityManager.createNativeQuery( - "SELECT DISTINCT id FROM " + table + " u WHERE id IN " + actualClauses.toString()); - for (int i = 0; i < queryParams.size(); i++) { - query.setParameter(i + 1, queryParams.get(i)); - } - - return query.getResultList(); - } - - @Transactional(readOnly = true) - public > List findByDerAttrValue( - final String table, - final AnyUtils anyUtils, - final DerSchema derSchema, - final String value, - final boolean ignoreCaseMatch) { - - if (derSchema == null) { - LOG.error("No DerSchema"); - return List.of(); - } - - Parser parser = new Parser(derSchema.getExpression()); - - // Schema keys - List identifiers = new ArrayList<>(); - - // Literals - List literals = new ArrayList<>(); - - // Get schema keys and literals - for (Token token = parser.getNextToken(); token != null && StringUtils.isNotBlank(token.toString()); - token = parser.getNextToken()) { - - if (token.kind == ParserConstants.STRING_LITERAL) { - literals.add(token.toString().substring(1, token.toString().length() - 1)); - } - - if (token.kind == ParserConstants.IDENTIFIER) { - identifiers.add(token.toString()); - } - } - - // Sort literals in order to process later literals included into others - literals.sort((l1, l2) -> { - if (l1 == null && l2 == null) { - return 0; - } else if (l1 != null && l2 == null) { - return -1; - } else if (l1 == null) { - return 1; - } else if (l1.length() == l2.length()) { - return 0; - } else if (l1.length() > l2.length()) { - return -1; - } else { - return 1; - } - }); - - // Split value on provided literals - List attrValues = split(value, literals); - - if (attrValues.size() != identifiers.size()) { - LOG.error("Ambiguous JEXL expression resolution: literals and values have different size"); - return List.of(); - } - - Map> clauses = new LinkedHashMap<>(); - - // builder to build the clauses - StringBuilder bld = new StringBuilder(); - - // Contains used identifiers in order to avoid replications - Set used = new HashSet<>(); - - // Create several clauses: one for eanch identifiers - for (int i = 0; i < identifiers.size(); i++) { - if (!used.contains(identifiers.get(i))) { - // verify schema existence and get schema type - PlainSchema schema = plainSchemaDAO.findById(identifiers.get(i)).orElse(null); - - if (schema == null) { - LOG.error("Invalid schema '{}', ignoring", identifiers.get(i)); - } else { - // clear builder - bld.delete(0, bld.length()); - - PlainAttrValue attrValue = schema.isUniqueConstraint() - ? anyUtils.newPlainAttrUniqueValue() - : anyUtils.newPlainAttrValue(); - attrValue.setStringValue(attrValues.get(i)); - - List queryParams = new ArrayList<>(); - bld.append('('). - append(plainAttrQuery(table, anyUtils, schema, attrValue, ignoreCaseMatch, queryParams)). - append(')'); - - used.add(identifiers.get(i)); - - clauses.put(bld.toString(), queryParams); - } - } - } - - LOG.debug("Generated where clauses {}", clauses); - - return buildResult(anyUtils, findByDerAttrValue(table, clauses)); - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AnySearchNode.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AnySearchNode.java new file mode 100644 index 0000000000..075353e128 --- /dev/null +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AnySearchNode.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.syncope.core.persistence.jpa.dao; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; + +public class AnySearchNode { + + public enum Type { + AND, + OR, + LEAF + + } + + public static class Leaf extends AnySearchNode { + + private final SearchSupport.SearchView from; + + private final String clause; + + protected Leaf(final SearchSupport.SearchView from, final String clause) { + super(Type.LEAF); + this.from = from; + this.clause = clause; + } + + public SearchSupport.SearchView getFrom() { + return from; + } + + public String getClause() { + return clause; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(). + appendSuper(super.hashCode()). + append(from). + append(clause). + build(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final Leaf other = (Leaf) obj; + + return new EqualsBuilder(). + appendSuper(super.equals(obj)). + append(from, other.from). + append(clause, other.clause). + build(); + } + + @Override + public String toString() { + return "LeafNode{" + "from=" + from + ", clause=" + clause + '}'; + } + } + + private final Type type; + + private final List children = new ArrayList<>(); + + public AnySearchNode(final Type type) { + this.type = type; + } + + protected Type getType() { + return type; + } + + protected boolean add(final AnySearchNode child) { + if (type == Type.LEAF) { + throw new IllegalArgumentException("Cannot add children to a leaf node"); + } + return children.add(child); + } + + protected List getChildren() { + return children; + } + + protected Optional asLeaf() { + return type == Type.LEAF + ? Optional.of((Leaf) this) + : Optional.empty(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(). + append(type). + append(children). + build(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final AnySearchNode other = (AnySearchNode) obj; + + return new EqualsBuilder(). + append(type, other.type). + append(children, other.children). + build(); + } + + @Override + public String toString() { + return "Node{" + "type=" + type + ", children=" + children + '}'; + } +} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBAnyFinder.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBAnyFinder.java deleted file mode 100644 index 5fd194b942..0000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBAnyFinder.java +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.dao; - -import jakarta.persistence.EntityManager; -import java.time.format.DateTimeFormatter; -import java.util.List; -import java.util.Optional; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; -import org.apache.syncope.core.persistence.api.entity.AnyUtils; -import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; - -public class MariaDBAnyFinder extends AnyFinder { - - public MariaDBAnyFinder(final PlainSchemaDAO plainSchemaDAO, final EntityManager entityManager) { - super(plainSchemaDAO, entityManager); - } - - @Override - protected String queryBegin(final String table) { - throw new UnsupportedOperationException("This method shall never be called"); - } - - @Override - protected String attrValueMatch( - final AnyUtils anyUtils, - final PlainSchema schema, - final PlainAttrValue attrValue, - final boolean ignoreCaseMatch) { - - throw new UnsupportedOperationException("This method shall never be called"); - } - - @Override - protected String plainAttrQuery( - final String table, - final AnyUtils anyUtils, - final PlainSchema schema, - final PlainAttrValue attrValue, - final boolean ignoreCaseMatch, - final List queryParams) { - - queryParams.add(schema.getKey()); - queryParams.add(attrValue.getStringValue()); - queryParams.add(attrValue.getBooleanValue()); - queryParams.add(Optional.ofNullable(attrValue.getDateValue()). - map(DateTimeFormatter.ISO_OFFSET_DATE_TIME::format).orElse(null)); - queryParams.add(attrValue.getLongValue()); - queryParams.add(attrValue.getDoubleValue()); - - SearchViewSupport svs = new SearchViewSupport(anyUtils.anyTypeKind()); - return "SELECT DISTINCT any_id FROM " - + (schema.isUniqueConstraint() ? svs.uniqueAttr().name() : svs.attr().name()) - + " WHERE schema_id = ? AND ((stringValue IS NOT NULL" - + " AND " - + (ignoreCaseMatch ? "LOWER(" : "") + "stringValue" + (ignoreCaseMatch ? ")" : "") - + " = " - + (ignoreCaseMatch ? "LOWER(" : "BINARY ") + "?" + (ignoreCaseMatch ? ")" : "") + ')' - + " OR (booleanValue IS NOT NULL AND booleanValue = ?)" - + " OR (dateValue IS NOT NULL AND dateValue = ?)" - + " OR (longValue IS NOT NULL AND longValue = ?)" - + " OR (doubleValue IS NOT NULL AND doubleValue = ?))"; - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java index 135e620c91..2d1ac6d301 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MariaDBJPAAnySearchDAO.java @@ -31,7 +31,6 @@ import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; -import org.apache.syncope.core.persistence.api.dao.search.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AttrCond; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; @@ -71,14 +70,13 @@ public MariaDBJPAAnySearchDAO( } @Override - protected String getQuery( + protected Pair getQuery( final AttrCond cond, final boolean not, + final Pair checked, final List parameters, final SearchSupport svs) { - Pair checked = check(cond, svs.anyTypeKind); - // normalize NULL / NOT NULL checks if (not) { if (cond.getType() == AttrCond.Type.ISNULL) { @@ -88,22 +86,24 @@ protected String getQuery( } } - StringBuilder query = - new StringBuilder("SELECT DISTINCT any_id FROM ").append(svs.field().name()).append(" WHERE "); switch (cond.getType()) { - case ISNOTNULL: - query.append("JSON_SEARCH(plainAttrs, 'one', '"). - append(checked.getLeft().getKey()). - append("', NULL, '$[*].schema') IS NOT NULL"); - break; + case ISNOTNULL -> { + return Pair.of(true, new AnySearchNode.Leaf( + svs.field(), + "JSON_SEARCH(" + + "plainAttrs, 'one', '" + checked.getLeft().getKey() + "', NULL, '$[*].schema'" + + ") IS NOT NULL")); + } - case ISNULL: - query.append("JSON_SEARCH(plainAttrs, 'one', '"). - append(checked.getLeft().getKey()). - append("', NULL, '$[*].schema') IS NULL"); - break; + case ISNULL -> { + return Pair.of(true, new AnySearchNode.Leaf( + svs.field(), + "JSON_SEARCH(" + + "plainAttrs, 'one', '" + checked.getLeft().getKey() + "', NULL, '$[*].schema'" + + ") IS NULL")); + } - default: + default -> { if (!not && cond.getType() == AttrCond.Type.EQ) { PlainAttr container = anyUtilsFactory.getInstance(svs.anyTypeKind).newPlainAttr(); container.setSchema(checked.getLeft()); @@ -113,20 +113,12 @@ protected String getQuery( container.add(checked.getRight()); } - query.append("JSON_CONTAINS(plainAttrs, '"). - append(POJOHelper.serialize(List.of(container)).replace("'", "''")). - append("')"); + return Pair.of(true, new AnySearchNode.Leaf( + svs.field(), + "JSON_CONTAINS(" + + "plainAttrs, '" + POJOHelper.serialize(List.of(container)).replace("'", "''") + + "')")); } else { - query = new StringBuilder("SELECT DISTINCT any_id FROM "); - if (not && !(cond instanceof AnyCond) && checked.getLeft().isMultivalue()) { - query.append(svs.field().name()).append(" WHERE "); - } else { - query.append((checked.getLeft().isUniqueConstraint() - ? svs.asSearchViewSupport().uniqueAttr().name() - : svs.asSearchViewSupport().attr().name())). - append(" WHERE schema_id='").append(checked.getLeft().getKey()); - } - Optional.ofNullable(checked.getRight().getDateValue()). map(DateTimeFormatter.ISO_OFFSET_DATE_TIME::format). ifPresent(formatted -> { @@ -134,10 +126,9 @@ protected String getQuery( checked.getRight().setStringValue(formatted); }); - fillAttrQuery(query, checked.getRight(), checked.getLeft(), cond, not, parameters, svs); + return super.getQuery(cond, not, checked, parameters, svs); } + } } - - return query.toString(); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLAnyFinder.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLAnyFinder.java deleted file mode 100644 index 27b8498175..0000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLAnyFinder.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.dao; - -import jakarta.persistence.EntityManager; -import java.util.List; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; -import org.apache.syncope.core.persistence.api.entity.AnyUtils; -import org.apache.syncope.core.persistence.api.entity.PlainAttr; -import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; -import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; - -public class MySQLAnyFinder extends AnyFinder { - - public MySQLAnyFinder(final PlainSchemaDAO plainSchemaDAO, final EntityManager entityManager) { - super(plainSchemaDAO, entityManager); - } - - @Override - protected String queryBegin(final String table) { - return "SELECT DISTINCT id FROM " + view(table) + ' '; - } - - @Override - protected String attrValueMatch( - final AnyUtils anyUtils, - final PlainSchema schema, - final PlainAttrValue attrValue, - final boolean ignoreCaseMatch) { - - Pair schemaInfo = schemaInfo(schema.getType(), ignoreCaseMatch); - if (schemaInfo.getRight()) { - return "plainSchema = ? " - + "AND LOWER(" - + (schema.isUniqueConstraint() - ? "attrUniqueValue ->> '$." + schemaInfo.getLeft() + '\'' - : schemaInfo.getLeft()) - + ") = LOWER(?)"; - } else { - PlainAttr container = anyUtils.newPlainAttr(); - container.setSchema(schema); - if (attrValue instanceof PlainAttrUniqueValue plainAttrUniqueValue) { - container.setUniqueValue(plainAttrUniqueValue); - } else { - container.add(attrValue); - } - return "JSON_CONTAINS(plainAttrs, '" + POJOHelper.serialize(List.of(container)).replace("'", "''") + "')"; - } - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java index 1bc16c5f8d..9a75375fe1 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/MySQLJPAAnySearchDAO.java @@ -23,9 +23,8 @@ import java.time.format.DateTimeFormatter; import java.util.List; import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; import org.apache.commons.lang3.tuple.Pair; +import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; @@ -33,9 +32,7 @@ import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; -import org.apache.syncope.core.persistence.api.dao.search.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AttrCond; -import org.apache.syncope.core.persistence.api.entity.AnyUtils; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.PlainAttr; @@ -74,62 +71,6 @@ public MySQLJPAAnySearchDAO( entityManager); } - @Override - protected void processOBS( - final SearchSupport svs, - final OrderBySupport obs, - final StringBuilder where) { - - Set attrs = obs.items.stream(). - map(item -> item.orderBy.substring(0, item.orderBy.indexOf(" "))).collect(Collectors.toSet()); - - obs.views.forEach(searchView -> { - boolean searchViewAddedToWhere = false; - if (searchView.name().equals(svs.field().name())) { - StringBuilder attrWhere = new StringBuilder(); - StringBuilder nullAttrWhere = new StringBuilder(); - - if (svs.nonMandatorySchemas || obs.nonMandatorySchemas) { - where.append(", (SELECT ").append(SELECT_COLS_FROM_VIEW).append(",plainSchema," - + "binaryValue,booleanValue,dateValue,doubleValue,longValue,stringValue,attrUniqueValue " - + "FROM ").append(searchView.name()); - searchViewAddedToWhere = true; - - attrs.forEach(field -> { - if (attrWhere.length() == 0) { - attrWhere.append(" WHERE "); - } else { - attrWhere.append(" OR "); - } - attrWhere.append("JSON_CONTAINS(plainAttrs, '[{\"schema\":\"").append(field).append("\"}]')"); - - nullAttrWhere.append(" UNION SELECT DISTINCT ").append(SELECT_COLS_FROM_VIEW).append(", "). - append("'").append(field).append("'").append(" AS plainSchema, "). - append("null AS binaryValue, "). - append("null AS booleanValue, "). - append("null AS dateValue, "). - append("null AS doubleValue, "). - append("null AS longValue, "). - append("null AS stringValue, "). - append("null AS attrUniqueValue "). - append("FROM ").append(svs.field().name()). - append(" WHERE any_id NOT IN "). - append("(SELECT DISTINCT any_id FROM "). - append(svs.field().name()). - append(" WHERE "). - append("JSON_CONTAINS(plainAttrs, '[{\"schema\":\"").append(field).append("\"}]'))"); - }); - where.append(attrWhere).append(nullAttrWhere).append(')'); - } - } - if (!searchViewAddedToWhere) { - where.append(',').append(searchView.name()); - } - - where.append(' ').append(searchView.alias()); - }); - } - @Override protected void parseOrderByForPlainSchema( final SearchSupport svs, @@ -151,122 +92,96 @@ protected void parseOrderByForPlainSchema( item.orderBy = fieldName + ' ' + clause.getDirection().name(); } - protected void fillAttrQuery( - final AnyUtils anyUtils, - final StringBuilder query, + protected AnySearchNode.Leaf filJSONAttrQuery( + final SearchSupport.SearchView from, final PlainAttrValue attrValue, final PlainSchema schema, final AttrCond cond, final boolean not, - final List parameters, - final SearchSupport svs) { - - // This first branch is required for handling with not conditions given on multivalue fields (SYNCOPE-1419) - if (not && schema.isMultivalue() - && !(cond instanceof AnyCond) - && cond.getType() != AttrCond.Type.ISNULL && cond.getType() != AttrCond.Type.ISNOTNULL) { - - query.append("id NOT IN (SELECT DISTINCT any_id FROM "); - query.append(svs.field().name()).append(" WHERE "); - fillAttrQuery(anyUtils, query, attrValue, schema, cond, false, parameters, svs); - query.append(')'); - } else { - if (!not && cond.getType() == AttrCond.Type.EQ) { - PlainAttr container = anyUtils.newPlainAttr(); - container.setSchema(schema); - if (attrValue instanceof PlainAttrUniqueValue plainAttrUniqueValue) { - container.setUniqueValue(plainAttrUniqueValue); - } else { - container.add(attrValue); - } + final List parameters) { - query.append("JSON_CONTAINS(plainAttrs, '"). - append(POJOHelper.serialize(List.of(container)).replace("'", "''")). - append("')"); - } else { - String key = key(schema.getType()); + String key = key(schema.getType()); - String value = Optional.ofNullable(attrValue.getDateValue()). - map(DateTimeFormatter.ISO_OFFSET_DATE_TIME::format). - orElse(cond.getExpression()); + String value = Optional.ofNullable(attrValue.getDateValue()). + map(DateTimeFormatter.ISO_OFFSET_DATE_TIME::format). + orElse(cond.getExpression()); - boolean lower = schema.getType().isStringClass() - && (cond.getType() == AttrCond.Type.IEQ || cond.getType() == AttrCond.Type.ILIKE); + boolean lower = (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) + && (cond.getType() == AttrCond.Type.IEQ || cond.getType() == AttrCond.Type.ILIKE); - query.append("plainSchema=?").append(setParameter(parameters, cond.getSchema())). - append(" AND "). - append(lower ? "LOWER(" : ""). - append(schema.isUniqueConstraint() - ? "attrUniqueValue ->> '$." + key + '\'' - : key). - append(lower ? ')' : ""); + StringBuilder clause = new StringBuilder("plainSchema=?").append(setParameter(parameters, cond.getSchema())). + append(" AND "). + append(lower ? "LOWER(" : ""). + append(schema.isUniqueConstraint() + ? "attrUniqueValue ->> '$." + key + '\'' + : key). + append(lower ? ')' : ""); - switch (cond.getType()) { - case LIKE: - case ILIKE: - if (not) { - query.append("NOT "); - } - query.append(" LIKE "); - break; - - case GE: - if (not) { - query.append('<'); - } else { - query.append(">="); - } - break; + switch (cond.getType()) { + case LIKE: + case ILIKE: + if (not) { + clause.append("NOT "); + } + clause.append(" LIKE "); + break; - case GT: - if (not) { - query.append("<="); - } else { - query.append('>'); - } - break; + case GE: + if (not) { + clause.append('<'); + } else { + clause.append(">="); + } + break; - case LE: - if (not) { - query.append('>'); - } else { - query.append("<="); - } - break; + case GT: + if (not) { + clause.append("<="); + } else { + clause.append('>'); + } + break; - case LT: - if (not) { - query.append(">="); - } else { - query.append('<'); - } - break; + case LE: + if (not) { + clause.append('>'); + } else { + clause.append("<="); + } + break; - case EQ: - case IEQ: - default: - if (not) { - query.append('!'); - } - query.append('='); + case LT: + if (not) { + clause.append(">="); + } else { + clause.append('<'); } + break; - query.append(lower ? "LOWER(" : ""). - append('?').append(setParameter(parameters, value)). - append(lower ? ")" : ""); - } + case EQ: + case IEQ: + default: + if (not) { + clause.append('!'); + } + clause.append('='); } + + clause.append(lower ? "LOWER(" : ""). + append('?').append(setParameter(parameters, value)). + append(lower ? ")" : ""); + + return new AnySearchNode.Leaf(from, clause.toString()); } @Override - protected String getQuery( + protected Pair getQuery( final AttrCond cond, final boolean not, + final Pair checked, final List parameters, final SearchSupport svs) { - Pair checked = check(cond, svs.anyTypeKind); - // normalize NULL / NOT NULL checks if (not) { if (cond.getType() == AttrCond.Type.ISNULL) { @@ -276,29 +191,66 @@ protected String getQuery( } } - StringBuilder query = - new StringBuilder("SELECT DISTINCT any_id FROM ").append(svs.field().name()).append(" WHERE "); switch (cond.getType()) { - case ISNOTNULL -> - query.append("JSON_SEARCH(plainAttrs, 'one', '"). - append(checked.getLeft().getKey()). - append("', NULL, '$[*].schema') IS NOT NULL"); + case ISNOTNULL -> { + return Pair.of(true, new AnySearchNode.Leaf( + svs.field(), + "JSON_SEARCH(" + + "plainAttrs, 'one', '" + checked.getLeft().getKey() + "', NULL, '$[*].schema'" + + ") IS NOT NULL")); + } - case ISNULL -> - query.append("JSON_SEARCH(plainAttrs, 'one', '"). - append(checked.getLeft().getKey()). - append("', NULL, '$[*].schema') IS NULL"); + case ISNULL -> { + return Pair.of(true, new AnySearchNode.Leaf( + svs.field(), + "JSON_SEARCH(" + + "plainAttrs, 'one', '" + checked.getLeft().getKey() + "', NULL, '$[*].schema'" + + ") IS NULL")); + } default -> { - if (not && !(cond instanceof AnyCond) && checked.getLeft().isMultivalue()) { - query = new StringBuilder("SELECT DISTINCT id AS any_id FROM ").append(svs.table().name()). - append(" WHERE "); + if (!not && cond.getType() == AttrCond.Type.EQ) { + PlainAttr container = anyUtilsFactory.getInstance(svs.anyTypeKind).newPlainAttr(); + container.setSchema(checked.getLeft()); + if (checked.getRight() instanceof PlainAttrUniqueValue plainAttrUniqueValue) { + container.setUniqueValue(plainAttrUniqueValue); + } else { + container.add(checked.getRight()); + } + + return Pair.of(true, new AnySearchNode.Leaf( + svs.field(), + "JSON_CONTAINS(" + + "plainAttrs, '" + POJOHelper.serialize(List.of(container)).replace("'", "''") + + "')")); + } else { + AnySearchNode.Leaf node; + if (not && checked.getLeft().isMultivalue()) { + AnySearchNode.Leaf notNode = filJSONAttrQuery( + svs.field(), + checked.getRight(), + checked.getLeft(), + cond, + false, + parameters); + node = new AnySearchNode.Leaf( + notNode.getFrom(), + "sv.any_id NOT IN (" + + "SELECT any_id FROM " + notNode.getFrom().name() + + " WHERE " + notNode.getClause().replace(notNode.getFrom().alias() + ".", "") + + ")"); + } else { + node = filJSONAttrQuery( + svs.field(), + checked.getRight(), + checked.getLeft(), + cond, + not, + parameters); + } + return Pair.of(true, node); } - fillAttrQuery(anyUtilsFactory.getInstance(svs.anyTypeKind), - query, checked.getRight(), checked.getLeft(), cond, not, parameters, svs); } } - - return query.toString(); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OracleAnyFinder.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OracleAnyFinder.java deleted file mode 100644 index 31c12ed185..0000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OracleAnyFinder.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.dao; - -import jakarta.persistence.EntityManager; -import org.apache.commons.lang3.BooleanUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.syncope.common.lib.types.AttrSchemaType; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; -import org.apache.syncope.core.persistence.api.entity.AnyUtils; -import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; - -public class OracleAnyFinder extends AnyFinder { - - public OracleAnyFinder(final PlainSchemaDAO plainSchemaDAO, final EntityManager entityManager) { - super(plainSchemaDAO, entityManager); - } - - @Override - protected String queryBegin(final String table) { - return "SELECT DISTINCT id FROM " + view(table) + ' '; - } - - @Override - protected Object getAttrValue( - final PlainSchema schema, - final PlainAttrValue attrValue, - final boolean ignoreCaseMatch) { - - return schema.getType() == AttrSchemaType.Boolean - ? BooleanUtils.toStringTrueFalse(attrValue.getBooleanValue()) - : schema.getType() == AttrSchemaType.String && ignoreCaseMatch - ? StringUtils.lowerCase(attrValue.getStringValue()) - : attrValue.getValue(); - } - - @Override - protected String attrValueMatch( - final AnyUtils anyUtils, - final PlainSchema schema, - final PlainAttrValue attrValue, - final boolean ignoreCaseMatch) { - - StringBuilder query = new StringBuilder("plainSchema = ? AND "); - - Pair schemaInfo = schemaInfo(schema.getType(), ignoreCaseMatch); - query.append(schemaInfo.getRight() ? "LOWER(" : ""); - - if (schema.isUniqueConstraint()) { - query.append("u").append(schemaInfo.getLeft()); - } else { - query.append("JSON_VALUE(").append(schemaInfo.getLeft()).append(", '$[*]')"); - } - - query.append(schemaInfo.getRight() ? ")" : ""). - append(" = "). - append(schemaInfo.getRight() ? "LOWER(" : ""). - append('?').append(schemaInfo.getRight() ? ")" : ""); - - return query.toString(); - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OracleJPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OracleJPAAnySearchDAO.java index 9d6ea949eb..6d1e309ecb 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OracleJPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/OracleJPAAnySearchDAO.java @@ -22,10 +22,10 @@ import jakarta.persistence.EntityManagerFactory; import java.time.format.DateTimeFormatter; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; -import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; import org.apache.syncope.common.lib.types.AttrSchemaType; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; @@ -35,9 +35,7 @@ import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; -import org.apache.syncope.core.persistence.api.dao.search.AnyCond; import org.apache.syncope.core.persistence.api.dao.search.AttrCond; -import org.apache.syncope.core.persistence.api.entity.AnyUtils; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; @@ -46,6 +44,22 @@ public class OracleJPAAnySearchDAO extends AbstractJPAAnySearchDAO { + /** + * + * @param schema + * @return JSON_TABLE(plainAttrs, '$[*]?(@.schema == "fullname").uniqueValue' \ + * COLUMNS uniqueValue PATH '$.stringValue') AS fullname + * or JSON_TABLE(plainAttrs, '$[*]?(@.schema == "loginDate").values[*]' \ + * COLUMNS valuez PATH '$.dateValue') AS loginDate + */ + public static String from(final PlainSchema schema) { + return new StringBuilder("JSON_TABLE(plainAttrs, '$[*]?(@.schema == \"").append(schema.getKey()).append("\")."). + append(schema.isUniqueConstraint() ? "uniqueValue" : "values[*]"). + append("' COLUMNS ").append(schema.isUniqueConstraint() ? "uniqueValue" : "valuez"). + append(" PATH '$.").append(key(schema.getType())).append("') AS ").append(schema.getKey()). + toString(); + } + public OracleJPAAnySearchDAO( final RealmSearchDAO realmSearchDAO, final DynRealmDAO dynRealmDAO, @@ -74,65 +88,13 @@ public OracleJPAAnySearchDAO( } @Override - protected void processOBS( - final SearchSupport svs, - final OrderBySupport obs, - final StringBuilder where) { - - Set attrs = obs.items.stream(). - map(item -> item.orderBy.substring(0, item.orderBy.indexOf(" "))).collect(Collectors.toSet()); - - obs.views.forEach(searchView -> { - boolean searchViewAddedToWhere = false; - if (searchView.name().equals(svs.field().name())) { - StringBuilder attrWhere = new StringBuilder(); - StringBuilder nullAttrWhere = new StringBuilder(); - - if (svs.nonMandatorySchemas || obs.nonMandatorySchemas) { - where.append(", (SELECT ").append(SELECT_COLS_FROM_VIEW).append(",plainSchema," - + "ubinaryValue,ubooleanValue,udateValue,udoubleValue,ulongValue,ustringValue," - + "binaryValue,booleanValue,dateValue,doubleValue,longValue,stringValue FROM "). - append(searchView.name()); - searchViewAddedToWhere = true; - - attrs.forEach(field -> { - if (attrWhere.length() == 0) { - attrWhere.append(" WHERE "); - } else { - attrWhere.append(" OR "); - } - attrWhere.append("JSON_EXISTS(plainAttrs, '$[*]?(@.schema == \"").append(field).append("\")')"); - - nullAttrWhere.append(" UNION SELECT DISTINCT ").append(SELECT_COLS_FROM_VIEW).append(","). - append("'").append(field).append("'").append(" AS plainSchema, "). - append("null AS ubinaryValue, "). - append("null AS ubooleanValue, "). - append("null AS udateValue, "). - append("null AS udoubleValue, "). - append("null AS ulongValue, "). - append("null AS ustringValue, "). - append("null AS binaryValue, "). - append("null AS booleanValue, "). - append("null AS dateValue, "). - append("null AS doubleValue, "). - append("null AS longValue, "). - append("null AS stringValue "). - append("FROM ").append(svs.field().name()). - append(" WHERE any_id NOT IN "). - append("(SELECT DISTINCT any_id FROM "). - append(svs.field().name()). - append(" WHERE "). - append("JSON_EXISTS(plainAttrs, '$[*]?(@.schema == \"").append(field).append("\")'))"); - }); - where.append(attrWhere).append(nullAttrWhere).append(')'); - } - } - if (!searchViewAddedToWhere) { - where.append(',').append(searchView.name()); - } + protected SearchSupport.SearchView defaultSV(final SearchSupport svs) { + return svs.table(); + } - where.append(' ').append(searchView.alias()); - }); + @Override + protected String anyId(final SearchSupport svs) { + return defaultSV(svs).alias() + ".id"; } @Override @@ -147,126 +109,115 @@ protected void parseOrderByForPlainSchema( // keep track of involvement of non-mandatory schemas in the order by clauses obs.nonMandatorySchemas = !"true".equals(schema.getMandatoryCondition()); - obs.views.add(svs.field()); + obs.views.add(svs.table()); - item.select = svs.field().alias() + '.' - + (schema.isUniqueConstraint() ? "u" : "") + key(schema.getType()) - + " AS " + fieldName; - item.where = "plainSchema = '" + fieldName + '\''; + item.select = schema.getKey() + "." + + (schema.isUniqueConstraint() ? "uniqueValue" : "valuez") + + " AS " + schema.getKey(); + item.where = StringUtils.EMPTY; item.orderBy = fieldName + ' ' + clause.getDirection().name(); } - protected void fillAttrQuery( - final AnyUtils anyUtils, - final StringBuilder query, + @Override + protected void parseOrderByForField( + final SearchSupport svs, + final OrderBySupport.Item item, + final String fieldName, + final Sort.Order clause) { + + item.select = svs.table().alias() + '.' + fieldName; + item.where = StringUtils.EMPTY; + item.orderBy = svs.table().alias() + '.' + fieldName + ' ' + clause.getDirection().name(); + } + + protected AnySearchNode.Leaf filJSONAttrQuery( + final SearchSupport.SearchView from, final PlainAttrValue attrValue, final PlainSchema schema, final AttrCond cond, final boolean not, - final List parameters, - final SearchSupport svs) { + final List parameters) { - // This first branch is required for handling with not conditions given on multivalue fields (SYNCOPE-1419) - if (not && schema.isMultivalue() - && !(cond instanceof AnyCond) - && cond.getType() != AttrCond.Type.ISNULL && cond.getType() != AttrCond.Type.ISNOTNULL) { - - query.append("id NOT IN (SELECT DISTINCT any_id FROM "); - query.append(svs.field().name()).append(" WHERE "); - fillAttrQuery(anyUtils, query, attrValue, schema, cond, false, parameters, svs); - query.append(')'); - } else { - String key = key(schema.getType()); - - String value = Optional.ofNullable(attrValue.getDateValue()). - map(DateTimeFormatter.ISO_OFFSET_DATE_TIME::format). - orElseGet(() -> schema.getType() == AttrSchemaType.Boolean - ? BooleanUtils.toStringTrueFalse(attrValue.getBooleanValue()) - : cond.getExpression()); - - boolean lower = schema.getType().isStringClass() - && (cond.getType() == AttrCond.Type.IEQ || cond.getType() == AttrCond.Type.ILIKE); - - query.append("plainSchema=?").append(setParameter(parameters, cond.getSchema())). - append(" AND "). - append(lower ? "LOWER(" : ""); - if (schema.isUniqueConstraint()) { - query.append("u").append(key); - } else { - query.append("JSON_VALUE(").append(key).append(", '$[*]')"); - } - query.append(lower ? ')' : ""); - - switch (cond.getType()) { - case LIKE: - case ILIKE: - if (not) { - query.append("NOT "); - } - query.append(" LIKE "); - break; - - case GE: - if (not) { - query.append('<'); - } else { - query.append(">="); - } - break; - - case GT: - if (not) { - query.append("<="); - } else { - query.append('>'); - } - break; - - case LE: - if (not) { - query.append('>'); - } else { - query.append("<="); - } - break; - - case LT: - if (not) { - query.append(">="); - } else { - query.append('<'); - } - break; - - case EQ: - case IEQ: - default: - if (not) { - query.append('!'); - } - query.append('='); - } + String value = Optional.ofNullable(attrValue.getDateValue()). + map(DateTimeFormatter.ISO_OFFSET_DATE_TIME::format). + orElse(cond.getExpression()); - query.append(lower ? "LOWER(" : ""). - append('?').append(setParameter(parameters, value)). - append(lower ? ")" : ""); - // workaround for Oracle DB adding explicit escaping string, to search - // for literal _ (underscore) (SYNCOPE-1779) - if (cond.getType() == AttrCond.Type.ILIKE || cond.getType() == AttrCond.Type.LIKE) { - query.append(" ESCAPE '\\' "); - } + boolean lower = (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) + && (cond.getType() == AttrCond.Type.IEQ || cond.getType() == AttrCond.Type.ILIKE); + + StringBuilder clause = new StringBuilder(lower ? "LOWER(" : ""). + append(schema.getKey()).append('.').append(schema.isUniqueConstraint() ? "uniqueValue" : "valuez"). + append(lower ? ')' : ""); + + switch (cond.getType()) { + case LIKE: + case ILIKE: + if (not) { + clause.append("NOT "); + } + clause.append(" LIKE "); + break; + + case GE: + if (not) { + clause.append('<'); + } else { + clause.append(">="); + } + break; + + case GT: + if (not) { + clause.append("<="); + } else { + clause.append('>'); + } + break; + + case LE: + if (not) { + clause.append('>'); + } else { + clause.append("<="); + } + break; + + case LT: + if (not) { + clause.append(">="); + } else { + clause.append('<'); + } + break; + + case EQ: + case IEQ: + default: + if (not) { + clause.append('!'); + } + clause.append('='); + } + + clause.append(lower ? "LOWER(" : ""). + append('?').append(setParameter(parameters, value)). + append(lower ? ")" : ""); + + // workaround for Oracle DB adding explicit escaping string, to search for literal _ (underscore) + if (cond.getType() == AttrCond.Type.ILIKE || cond.getType() == AttrCond.Type.LIKE) { + clause.append(" ESCAPE '\\'"); } + return new AnySearchNode.Leaf(from, clause.toString()); } @Override - protected String getQuery( + protected Pair getQuery( final AttrCond cond, final boolean not, + final Pair checked, final List parameters, final SearchSupport svs) { - Pair checked = check(cond, svs.anyTypeKind); - // normalize NULL / NOT NULL checks if (not) { if (cond.getType() == AttrCond.Type.ISNULL) { @@ -276,27 +227,83 @@ protected String getQuery( } } - StringBuilder query = - new StringBuilder("SELECT DISTINCT any_id FROM ").append(svs.field().name()).append(" WHERE "); switch (cond.getType()) { - case ISNOTNULL -> - query.append("JSON_EXISTS(plainAttrs, '$[*]?(@.schema == \""). - append(checked.getLeft().getKey()).append("\")')"); + case ISNOTNULL -> { + return Pair.of(false, new AnySearchNode.Leaf( + svs.table(), + "JSON_EXISTS(plainAttrs, '$[*]?(@.schema == \"" + checked.getLeft().getKey() + "\")')")); + } - case ISNULL -> - query.append("NOT JSON_EXISTS(plainAttrs, '$[*]?(@.schema == \""). - append(checked.getLeft().getKey()).append("\")')"); + case ISNULL -> { + return Pair.of(false, new AnySearchNode.Leaf( + svs.table(), + "NOT JSON_EXISTS(plainAttrs, '$[*]?(@.schema == \"" + checked.getLeft().getKey() + "\")')")); + } default -> { - if (not && !(cond instanceof AnyCond) && checked.getLeft().isMultivalue()) { - query = new StringBuilder("SELECT DISTINCT id AS any_id FROM ").append(svs.table().name()). - append(" WHERE "); + AnySearchNode.Leaf node; + if (not && checked.getLeft().isMultivalue()) { + AnySearchNode.Leaf notNode = filJSONAttrQuery( + svs.table(), + checked.getRight(), + checked.getLeft(), + cond, + false, + parameters); + node = new AnySearchNode.Leaf( + notNode.getFrom(), + "id NOT IN (" + + "SELECT id FROM " + notNode.getFrom().name() + "," + from(checked.getLeft()) + + " WHERE " + notNode.getClause().replace(notNode.getFrom().alias() + ".", "") + + ")"); + return Pair.of(false, node); + } else { + node = filJSONAttrQuery( + svs.table(), + checked.getRight(), + checked.getLeft(), + cond, + not, + parameters); } - fillAttrQuery(anyUtilsFactory.getInstance(svs.anyTypeKind), - query, checked.getRight(), checked.getLeft(), cond, not, parameters, svs); + return Pair.of(true, node); } } + } + + @Override + protected void visitNode( + final AnySearchNode node, + final Map counters, + final Set from, + final List where, + final SearchSupport svs) { + + counters.clear(); + super.visitNode(node, counters, from, where, svs); + } + + @Override + protected String buildFrom( + final Set from, + final Set plainSchemas, + final OrderBySupport obs) { + + StringBuilder clause = new StringBuilder(super.buildFrom(from, plainSchemas, obs)); + + plainSchemas.forEach(schema -> plainSchemaDAO.findById(schema). + ifPresent(pschema -> clause.append(",").append(from(pschema)))); + + if (obs != null) { + obs.items.forEach(item -> { + String schema = StringUtils.substringBefore(item.orderBy, ' '); + if (StringUtils.isNotBlank(schema) && !plainSchemas.contains(schema)) { + plainSchemaDAO.findById(schema).ifPresent( + pschema -> clause.append(" LEFT OUTER JOIN ").append(from(pschema)).append(" ON 1=1")); + } + }); + } - return query.toString(); + return clause.toString(); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGAnyFinder.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGAnyFinder.java deleted file mode 100644 index afe257a819..0000000000 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGAnyFinder.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.syncope.core.persistence.jpa.dao; - -import jakarta.persistence.EntityManager; -import java.util.List; -import org.apache.commons.lang3.tuple.Pair; -import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; -import org.apache.syncope.core.persistence.api.entity.AnyUtils; -import org.apache.syncope.core.persistence.api.entity.PlainAttr; -import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; -import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; - -public class PGAnyFinder extends AnyFinder { - - public PGAnyFinder(final PlainSchemaDAO plainSchemaDAO, final EntityManager entityManager) { - super(plainSchemaDAO, entityManager); - } - - @Override - protected String queryBegin(final String table) { - return "SELECT DISTINCT id FROM " + table + " u," - + "jsonb_array_elements(u.plainAttrs) attrs," - + "jsonb_array_elements(COALESCE(attrs -> 'values', '[{}]'::jsonb)) attrValues "; - } - - @Override - protected String attrValueMatch( - final AnyUtils anyUtils, - final PlainSchema schema, - final PlainAttrValue attrValue, - final boolean ignoreCaseMatch) { - - Pair schemaInfo = schemaInfo(schema.getType(), ignoreCaseMatch); - if (schemaInfo.getRight()) { - return "attrs ->> 'schema' = ? " - + "AND LOWER(" - + (schema.isUniqueConstraint() ? "attrs -> 'uniqueValue'" : "attrValues") - + " ->> '" + schemaInfo.getLeft() - + "') = LOWER(?)"; - } - - PlainAttr container = anyUtils.newPlainAttr(); - container.setSchema(schema); - if (attrValue instanceof PlainAttrUniqueValue plainAttrUniqueValue) { - container.setUniqueValue(plainAttrUniqueValue); - } else { - container.add(attrValue); - } - return "plainAttrs::jsonb @> '" + POJOHelper.serialize(List.of(container)).replace("'", "''") + "'::jsonb"; - } -} diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAAnySearchDAO.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAAnySearchDAO.java index 38fd7d37b7..e9562cabd5 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAAnySearchDAO.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/PGJPAAnySearchDAO.java @@ -20,21 +20,15 @@ import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; -import jakarta.persistence.Query; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; -import java.util.stream.Collectors; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; -import org.apache.commons.lang3.tuple.Triple; -import org.apache.syncope.common.lib.SyncopeClientException; -import org.apache.syncope.common.lib.SyncopeConstants; -import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.AttrSchemaType; -import org.apache.syncope.common.rest.api.service.JAXRSService; import org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.DynRealmDAO; @@ -42,33 +36,15 @@ import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO; import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; -import org.apache.syncope.core.persistence.api.dao.search.AnyCond; -import org.apache.syncope.core.persistence.api.dao.search.AnyTypeCond; import org.apache.syncope.core.persistence.api.dao.search.AttrCond; -import org.apache.syncope.core.persistence.api.dao.search.AuxClassCond; -import org.apache.syncope.core.persistence.api.dao.search.DynRealmCond; -import org.apache.syncope.core.persistence.api.dao.search.MemberCond; -import org.apache.syncope.core.persistence.api.dao.search.MembershipCond; -import org.apache.syncope.core.persistence.api.dao.search.PrivilegeCond; -import org.apache.syncope.core.persistence.api.dao.search.RelationshipCond; -import org.apache.syncope.core.persistence.api.dao.search.RelationshipTypeCond; -import org.apache.syncope.core.persistence.api.dao.search.ResourceCond; -import org.apache.syncope.core.persistence.api.dao.search.RoleCond; -import org.apache.syncope.core.persistence.api.dao.search.SearchCond; -import org.apache.syncope.core.persistence.api.entity.Any; -import org.apache.syncope.core.persistence.api.entity.AnyUtils; import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.EntityFactory; import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; -import org.apache.syncope.core.persistence.api.entity.Realm; -import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; public class PGJPAAnySearchDAO extends AbstractJPAAnySearchDAO { - protected static final String ALWAYS_FALSE_ASSERTION = "1=2"; - protected static final String REGEX_CHARS = "!$()*+.:<=>?[\\]^{|}-"; protected static String escapeForLikeRegex(final String input) { @@ -112,6 +88,16 @@ public PGJPAAnySearchDAO( entityManager); } + @Override + protected SearchSupport.SearchView defaultSV(final SearchSupport svs) { + return svs.table(); + } + + @Override + protected String anyId(final SearchSupport svs) { + return defaultSV(svs).alias() + ".id"; + } + @Override protected void parseOrderByForPlainSchema( final SearchSupport svs, @@ -143,734 +129,176 @@ protected void parseOrderByForField( item.orderBy = svs.table().alias() + '.' + fieldName + ' ' + clause.getDirection().name(); } - protected void fillAttrQuery( - final AnyUtils anyUtils, - final StringBuilder query, + protected AnySearchNode.Leaf filJSONAttrQuery( + final SearchSupport.SearchView from, final PlainAttrValue attrValue, final PlainSchema schema, final AttrCond cond, - final boolean not, - final SearchSupport svs) { - - if (not && cond.getType() == AttrCond.Type.ISNULL) { - cond.setType(AttrCond.Type.ISNOTNULL); - fillAttrQuery(anyUtils, query, attrValue, schema, cond, true, svs); - } else if (not) { - query.append("NOT ("); - fillAttrQuery(anyUtils, query, attrValue, schema, cond, false, svs); - query.append(')'); - } else { - String key = key(schema.getType()); - - String value = Optional.ofNullable(attrValue.getDateValue()). - map(DateTimeFormatter.ISO_OFFSET_DATE_TIME::format). - orElse(cond.getExpression()); - - boolean isStr = true; - boolean lower = false; - if (schema.getType().isStringClass()) { - lower = (cond.getType() == AttrCond.Type.IEQ || cond.getType() == AttrCond.Type.ILIKE); - } else if (schema.getType() != AttrSchemaType.Date) { - lower = false; - try { - switch (schema.getType()) { - case Long -> - Long.valueOf(value); - - case Double -> - Double.valueOf(value); - - case Boolean -> { - if (!("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value))) { - throw new IllegalArgumentException(); - } - } - - default -> { + final boolean not) { + + String key = key(schema.getType()); + + String value = Optional.ofNullable(attrValue.getDateValue()). + map(DateTimeFormatter.ISO_OFFSET_DATE_TIME::format). + orElse(cond.getExpression()); + + boolean isStr = true; + boolean lower = false; + if (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) { + lower = (cond.getType() == AttrCond.Type.IEQ || cond.getType() == AttrCond.Type.ILIKE); + } else if (schema.getType() != AttrSchemaType.Date) { + lower = false; + try { + switch (schema.getType()) { + case Long -> + Long.valueOf(value); + + case Double -> + Double.valueOf(value); + + case Boolean -> { + if (!("true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value))) { + throw new IllegalArgumentException(); } } - isStr = false; - } catch (Exception nfe) { - // ignore - } - } - - switch (cond.getType()) { - case ISNULL -> { - } - - case ISNOTNULL -> - query.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*]')"); - - case ILIKE, LIKE -> { - // jsonb_path_exists(Nome, '$[*] ? (@.stringValue like_regex "EL.*" flag "i")') - if (schema.getType().isStringClass()) { - query.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). - append("(@.").append(key).append(" like_regex \""). - append(escapeForLikeRegex(value).replace("%", ".*")). - append("\""). - append(lower ? " flag \"i\"" : "").append(")')"); - } else { - query.append(' ').append(ALWAYS_FALSE_ASSERTION); - LOG.error("LIKE is only compatible with string or enum schemas"); - } - } - - case IEQ, EQ -> { - query.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). - append("(@.").append(key); - - if (StringUtils.containsAny(value, REGEX_CHARS) || lower) { - query.append(" like_regex \"^"). - append(escapeForLikeRegex(value).replace("'", "''")). - append("$\""); - } else { - query.append(" == ").append(escapeIfString(value, isStr)); + default -> { } - - query.append(lower ? " flag \"i\"" : "").append(")')"); } - case GE -> - query.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). - append("(@.").append(key).append(" >= "). - append(escapeIfString(value, isStr)).append(")')"); - - case GT -> - query.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). - append("(@.").append(key).append(" > "). - append(escapeIfString(value, isStr)).append(")')"); - - case LE -> - query.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). - append("(@.").append(key).append(" <= "). - append(escapeIfString(value, isStr)).append(")')"); - - case LT -> - query.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). - append("(@.").append(key).append(" < "). - append(escapeIfString(value, isStr)).append(")')"); - - default -> { - } + isStr = false; + } catch (Exception nfe) { + // ignore } - // shouldn't occour: processed before } - } - - @Override - protected String getQuery( - final AttrCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { - - Pair checked = check(cond, svs.anyTypeKind); - - StringBuilder query = new StringBuilder(); + StringBuilder clause = new StringBuilder(); switch (cond.getType()) { - case ISNOTNULL -> - query.append(not ? " NOT " : ' '). - append("jsonb_path_exists(").append(checked.getLeft().getKey()).append(",'$[*]')"); - - case ISNULL -> - query.append(not ? ' ' : " NOT "). - append("jsonb_path_exists(").append(checked.getLeft().getKey()).append(",'$[*]')"); - - default -> - fillAttrQuery( - anyUtilsFactory.getInstance(svs.anyTypeKind), - query, checked.getRight(), checked.getLeft(), cond, not, svs); - } - - return query.toString(); - } - - @Override - protected String getQuery( - final AnyTypeCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { - - StringBuilder query = new StringBuilder("type_id"); - - if (not) { - query.append("<>"); - } else { - query.append('='); - } - - query.append('?').append(setParameter(parameters, cond.getAnyTypeKey())); - - return query.toString(); - } - - @Override - protected String getQuery( - final AuxClassCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { - - StringBuilder query = new StringBuilder(); - - if (not) { - query.append("id NOT IN ("); - } else { - query.append("id IN ("); - } - - query.append("SELECT DISTINCT any_id FROM "). - append(svs.auxClass().name()). - append(" WHERE anyTypeClass_id=?"). - append(setParameter(parameters, cond.getAuxClass())). - append(')'); - - return query.toString(); - } - - @Override - protected String getQuery( - final RoleCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { - - StringBuilder query = new StringBuilder().append('('); - - if (not) { - query.append("id NOT IN ("); - } else { - query.append("id IN ("); - } - - query.append("SELECT DISTINCT any_id FROM "). - append(svs.role().name()).append(" WHERE "). - append("role_id=?").append(setParameter(parameters, cond.getRole())). - append(") "); - - if (not) { - query.append("AND id NOT IN ("); - } else { - query.append("OR id IN ("); - } - - query.append("SELECT DISTINCT any_id FROM "). - append(SearchSupport.dynrolemembership().name()).append(" WHERE "). - append("role_id=?").append(setParameter(parameters, cond.getRole())). - append(')'); - - query.append(')'); - - return query.toString(); - } - - @Override - protected String getQuery( - final PrivilegeCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { - - StringBuilder query = new StringBuilder().append('('); - - if (not) { - query.append("id NOT IN ("); - } else { - query.append("id IN ("); - } - - query.append("SELECT DISTINCT any_id FROM "). - append(svs.priv().name()).append(" WHERE "). - append("privilege_id=?").append(setParameter(parameters, cond.getPrivilege())). - append(") "); - - if (not) { - query.append("AND id NOT IN ("); - } else { - query.append("OR id IN ("); - } - - query.append("SELECT DISTINCT any_id FROM "). - append(svs.dynpriv().name()).append(" WHERE "). - append("privilege_id=?").append(setParameter(parameters, cond.getPrivilege())). - append(')'); - - query.append(')'); - - return query.toString(); - } - - @Override - protected String getQuery( - final DynRealmCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { - - StringBuilder query = new StringBuilder(); - - if (not) { - query.append("id NOT IN ("); - } else { - query.append("id IN ("); - } - - query.append("SELECT DISTINCT any_id FROM "). - append(SearchSupport.dynrealmmembership().name()).append(" WHERE "). - append("dynRealm_id=?").append(setParameter(parameters, cond.getDynRealm())). - append(')'); - - return query.toString(); - } - - @Override - protected String getQuery( - final ResourceCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { - - StringBuilder query = new StringBuilder(); - - if (not) { - query.append("id NOT IN ("); - } else { - query.append("id IN ("); - } - - query.append("SELECT DISTINCT any_id FROM "). - append(svs.resource().name()). - append(" WHERE resource_id=?"). - append(setParameter(parameters, cond.getResource())); - - if (svs.anyTypeKind == AnyTypeKind.USER || svs.anyTypeKind == AnyTypeKind.ANY_OBJECT) { - query.append(" UNION SELECT DISTINCT any_id FROM "). - append(svs.groupResource().name()). - append(" WHERE resource_id=?"). - append(setParameter(parameters, cond.getResource())); - } - - query.append(')'); - - return query.toString(); - } - - @Override - protected String getQuery( - final MemberCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { - - Set members = check(cond); - - StringBuilder query = new StringBuilder().append('('); - - if (not) { - query.append("id NOT IN ("); - } else { - query.append("id IN ("); - } - - query.append("SELECT DISTINCT group_id AS any_id FROM "). - append(new SearchSupport(AnyTypeKind.USER).membership().name()).append(" WHERE "). - append(members.stream(). - map(key -> "any_id=?" + setParameter(parameters, key)). - collect(Collectors.joining(" OR "))). - append(") "); - - if (not) { - query.append("AND id NOT IN ("); - } else { - query.append("OR id IN ("); - } - - query.append("SELECT DISTINCT group_id AS any_id FROM "). - append(new SearchSupport(AnyTypeKind.ANY_OBJECT).membership().name()).append(" WHERE "). - append(members.stream(). - map(key -> "any_id=?" + setParameter(parameters, key)). - collect(Collectors.joining(" OR "))). - append(')'); - - query.append(')'); - - return query.toString(); - } - - @Override - protected String getQuery( - final RelationshipTypeCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { - - StringBuilder query = new StringBuilder().append('('); + case ILIKE: + case LIKE: + // jsonb_path_exists(Nome, '$[*] ? (@.stringValue like_regex "EL.*" flag "i")') + if (schema.getType() == AttrSchemaType.String || schema.getType() == AttrSchemaType.Enum) { + clause.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). + append("(@.").append(key).append(" like_regex \""). + append(escapeForLikeRegex(value).replace("%", ".*")). + append("\""). + append(lower ? " flag \"i\"" : "").append(")')"); + } else { + LOG.error("LIKE is only compatible with string or enum schemas"); + clause.append(' ').append(ALWAYS_FALSE_CLAUSE); + } + break; + + case IEQ: + case EQ: + default: + clause.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). + append("(@.").append(key); + + if (StringUtils.containsAny(value, REGEX_CHARS) || lower) { + clause.append(" like_regex \"^"). + append(escapeForLikeRegex(value).replace("'", "''")). + append("$\""); + } else { + clause.append(" == ").append(escapeIfString(value, isStr)); + } - if (not) { - query.append("id NOT IN ("); - } else { - query.append("id IN ("); + clause.append(lower ? " flag \"i\"" : "").append(")')"); + break; + + case GE: + clause.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). + append("(@.").append(key).append(" >= "). + append(escapeIfString(value, isStr)).append(")')"); + break; + + case GT: + clause.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). + append("(@.").append(key).append(" > "). + append(escapeIfString(value, isStr)).append(")')"); + break; + + case LE: + clause.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). + append("(@.").append(key).append(" <= "). + append(escapeIfString(value, isStr)).append(")')"); + break; + + case LT: + clause.append("jsonb_path_exists(").append(schema.getKey()).append(", '$[*] ? "). + append("(@.").append(key).append(" < "). + append(escapeIfString(value, isStr)).append(")')"); + break; } - query.append("SELECT any_id ").append("FROM "). - append(svs.relationship().name()). - append(" WHERE type=?").append(setParameter(parameters, cond.getRelationshipTypeKey())). - append(" UNION SELECT right_any_id AS any_id FROM "). - append(svs.relationship().name()). - append(" WHERE type=?").append(setParameter(parameters, cond.getRelationshipTypeKey())). - append(')'); - - query.append(')'); - - return query.toString(); - } - - @Override - protected String getQuery( - final RelationshipCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { - - Set rightAnyObjectKeys = check(cond); - - StringBuilder query = new StringBuilder().append('('); - if (not) { - query.append("id NOT IN ("); - } else { - query.append("id IN ("); + clause.insert(0, "NOT "); } - query.append("SELECT DISTINCT any_id FROM "). - append(svs.relationship().name()).append(" WHERE "). - append(rightAnyObjectKeys.stream(). - map(key -> "right_any_id=?" + setParameter(parameters, key)). - collect(Collectors.joining(" OR "))). - append(')'); - - query.append(')'); - - return query.toString(); + return new AnySearchNode.Leaf(from, clause.toString()); } @Override - protected String getQuery( - final MembershipCond cond, + protected Pair getQuery( + final AttrCond cond, final boolean not, + final Pair checked, final List parameters, final SearchSupport svs) { - List groupKeys = check(cond); - - String where = groupKeys.stream(). - map(key -> "group_id=?" + setParameter(parameters, key)). - collect(Collectors.joining(" OR ")); - - StringBuilder query = new StringBuilder().append('('); - + // normalize NULL / NOT NULL checks if (not) { - query.append("id NOT IN ("); - } else { - query.append("id IN ("); - } - - query.append("SELECT DISTINCT any_id FROM "). - append(svs.membership().name()).append(" WHERE "). - append('(').append(where).append(')'). - append(") "); - - if (not) { - query.append("AND id NOT IN ("); - } else { - query.append("OR id IN ("); + if (cond.getType() == AttrCond.Type.ISNULL) { + cond.setType(AttrCond.Type.ISNOTNULL); + } else if (cond.getType() == AttrCond.Type.ISNOTNULL) { + cond.setType(AttrCond.Type.ISNULL); + } } - query.append("SELECT DISTINCT any_id FROM "). - append(svs.dyngroupmembership().name()).append(" WHERE "). - append('(').append(where).append(')'). - append(')'); + return switch (cond.getType()) { + case ISNOTNULL -> + Pair.of(true, new AnySearchNode.Leaf( + svs.table(), + "jsonb_path_exists(" + checked.getLeft().getKey() + ",'$[*]')")); - query.append(')'); + case ISNULL -> + Pair.of(true, new AnySearchNode.Leaf( + svs.table(), + "NOT jsonb_path_exists(" + checked.getLeft().getKey() + ",'$[*]')")); - return query.toString(); + default -> + Pair.of(true, filJSONAttrQuery( + svs.table(), + checked.getRight(), + checked.getLeft(), + cond, + not)); + }; } @Override - protected String getQuery( - final AnyCond cond, - final boolean not, - final List parameters, + protected void visitNode( + final AnySearchNode node, + final Map counters, + final Set from, + final List where, final SearchSupport svs) { - if (JAXRSService.PARAM_REALM.equals(cond.getSchema()) - && !SyncopeConstants.UUID_PATTERN.matcher(cond.getExpression()).matches()) { - - Realm realm = realmSearchDAO.findByFullPath(cond.getExpression()). - orElseThrow(() -> new IllegalArgumentException("Invalid Realm full path: " + cond.getExpression())); - cond.setExpression(realm.getKey()); - } - - Triple checked = check(cond, svs.anyTypeKind); - - StringBuilder query = new StringBuilder(); - - plainSchemaDAO.findById(cond.getSchema()).ifPresentOrElse( - schema -> fillAttrQuery( - anyUtilsFactory.getInstance(svs.anyTypeKind), - query, checked.getMiddle(), checked.getLeft(), checked.getRight(), not, svs), - () -> fillAttrQuery( - query, checked.getMiddle(), checked.getLeft(), checked.getRight(), not, parameters, svs)); - - return query.toString(); + counters.clear(); + super.visitNode(node, counters, from, where, svs); } @Override - protected String buildAdminRealmsFilter( - final Set realmKeys, - final SearchSupport svs, - final List parameters) { - - if (realmKeys.isEmpty()) { - return "realm_id IS NOT NULL"; - } - - String realmKeysArg = realmKeys.stream(). - map(realmKey -> "?" + setParameter(parameters, realmKey)). - collect(Collectors.joining(",")); - return "realm_id IN (" + realmKeysArg + ')'; - } - - @Override - protected long doCount( - final Realm base, - final boolean recursive, - final Set adminRealms, - final SearchCond cond, - final AnyTypeKind kind) { - - List parameters = new ArrayList<>(); - - SearchSupport svs = buildSearchSupport(kind); - - Triple, Set> filter = - getAdminRealmsFilter(base, recursive, adminRealms, svs, parameters); - - Pair> queryInfo = - getQuery(buildEffectiveCond(cond, filter.getMiddle(), filter.getRight(), kind), parameters, svs); - - StringBuilder queryString = - new StringBuilder("SELECT count(").append(svs.table().alias()).append(".id").append(')'); - - buildFromAndWhere(queryString, queryInfo, filter.getLeft(), svs, null); - - Query countQuery = entityManager.createNativeQuery(queryString.toString()); - fillWithParameters(countQuery, parameters); - - return ((Number) countQuery.getSingleResult()).longValue(); - } - - @Override - @SuppressWarnings("unchecked") - protected > List doSearch( - final Realm base, - final boolean recursive, - final Set adminRealms, - final SearchCond cond, - final Pageable pageable, - final AnyTypeKind kind) { - - try { - List parameters = new ArrayList<>(); - - SearchSupport svs = buildSearchSupport(kind); - - Triple, Set> filter = - getAdminRealmsFilter(base, recursive, adminRealms, svs, parameters); - - SearchCond effectiveCond = buildEffectiveCond(cond, filter.getMiddle(), filter.getRight(), kind); - - // 1. get the query string from the search condition - Pair> queryInfo = getQuery(effectiveCond, parameters, svs); - - // 2. take into account realms and ordering - OrderBySupport obs = parseOrderBy(svs, pageable.getSort().get()); - - StringBuilder queryString = new StringBuilder("SELECT ").append(svs.table().alias()).append(".id"); - obs.items.forEach(item -> queryString.append(',').append(item.select)); - - buildFromAndWhere(queryString, queryInfo, filter.getLeft(), svs, obs); - - LOG.debug("Query: {}, parameters: {}", queryString, parameters); - - queryString.append(buildOrderBy(obs)); - - LOG.debug("Query with auth and order by statements: {}, parameters: {}", queryString, parameters); - - // 3. prepare the search query - Query query = entityManager.createNativeQuery(queryString.toString()); - - if (pageable.isPaged()) { - query.setFirstResult(pageable.getPageSize() * pageable.getPageNumber()); - query.setMaxResults(pageable.getPageSize()); - } - - // 5. populate the search query with parameter values - fillWithParameters(query, parameters); - - // 6. Prepare the result (avoiding duplicates) - return buildResult(query.getResultList(), kind); - } catch (SyncopeClientException e) { - throw e; - } catch (Exception e) { - LOG.error("While searching for {}", kind, e); - } - - return List.of(); - } - - @Override - protected void queryOp( - final StringBuilder query, - final String op, - final Pair> leftInfo, - final Pair> rightInfo) { - - query.append('('). - append(leftInfo.getKey()). - append(' ').append(op).append(' '). - append(rightInfo.getKey()). - append(')'); - } - - @Override - protected void fillAttrQuery( - final StringBuilder query, - final PlainAttrValue attrValue, - final PlainSchema schema, - final AttrCond cond, - final boolean not, - final List parameters, - final SearchSupport svs) { - - if (not && cond.getType() == AttrCond.Type.ISNULL) { - cond.setType(AttrCond.Type.ISNOTNULL); - fillAttrQuery(query, attrValue, schema, cond, true, parameters, svs); - } else if (not) { - query.append("NOT ("); - fillAttrQuery(query, attrValue, schema, cond, false, parameters, svs); - query.append(')'); - } else if (not && cond.getType() == AttrCond.Type.ISNULL) { - cond.setType(AttrCond.Type.ISNOTNULL); - fillAttrQuery(query, attrValue, schema, cond, true, parameters, svs); - } else { - boolean lower = schema.getType().isStringClass() - && (cond.getType() == AttrCond.Type.IEQ || cond.getType() == AttrCond.Type.ILIKE); - - String column = cond.getSchema(); - if (lower) { - column = "LOWER (" + column + ')'; - } - - switch (cond.getType()) { - - case ISNULL -> - query.append(column).append(" IS NULL"); - - case ISNOTNULL -> - query.append(column).append(" IS NOT NULL"); - - case ILIKE, LIKE -> { - if (schema.getType().isStringClass()) { - query.append(column).append(" LIKE "); - if (lower) { - query.append("LOWER(?").append(setParameter(parameters, cond.getExpression())).append(')'); - } else { - query.append('?').append(setParameter(parameters, cond.getExpression())); - } - } else { - query.append(' ').append(ALWAYS_FALSE_ASSERTION); - LOG.error("LIKE is only compatible with string or enum schemas"); - } - } - - case IEQ, EQ -> { - query.append(column).append('='); - - if (lower) { - query.append("LOWER(?").append(setParameter(parameters, attrValue.getValue())).append(')'); - } else { - query.append('?').append(setParameter(parameters, attrValue.getValue())); - } - } - - case GE -> { - query.append(column); - if (not) { - query.append('<'); - } else { - query.append(">="); - } - query.append('?').append(setParameter(parameters, attrValue.getValue())); - } - - case GT -> { - query.append(column); - if (not) { - query.append("<="); - } else { - query.append('>'); - } - query.append('?').append(setParameter(parameters, attrValue.getValue())); - } - - case LE -> { - query.append(column); - if (not) { - query.append('>'); - } else { - query.append("<="); - } - query.append('?').append(setParameter(parameters, attrValue.getValue())); - } - - case LT -> { - query.append(column); - if (not) { - query.append(">="); - } else { - query.append('<'); - } - query.append('?').append(setParameter(parameters, attrValue.getValue())); - } - - default -> { - } - } - } - } - - protected void buildFromAndWhere( - final StringBuilder queryString, - final Pair> queryInfo, - final String realmsFilter, - final SearchSupport svs, + protected String buildFrom( + final Set from, + final Set plainSchemas, final OrderBySupport obs) { - queryString.append(" FROM ").append(svs.table().name()).append(' ').append(svs.table().alias()); + StringBuilder clause = new StringBuilder(super.buildFrom(from, plainSchemas, obs)); - Set schemas = queryInfo.getRight(); + Set schemas = new HashSet<>(plainSchemas); if (obs != null) { - obs.views.stream(). - filter(view -> !svs.field().name().equals(view.name()) && !svs.table().name().equals(view.name())). - map(view -> view.name() + ' ' + view.alias()). - forEach(view -> queryString.append(',').append(view)); - obs.items.forEach(item -> { String schema = StringUtils.substringBefore(item.orderBy, ' '); if (StringUtils.isNotBlank(schema)) { @@ -880,42 +308,13 @@ protected void buildFromAndWhere( } // i.e jsonb_path_query(plainattrs, '$[*] ? (@.schema=="Nome")."values"') AS Nome - schemas.forEach(schema -> plainSchemaDAO.findById(schema).ifPresentOrElse( - pschema -> queryString.append(','). + schemas.forEach(schema -> plainSchemaDAO.findById(schema).ifPresent( + pschema -> clause.append(','). append("jsonb_path_query_array(plainattrs, '$[*] ? (@.schema==\""). append(schema).append("\")."). append("\"").append(pschema.isUniqueConstraint() ? "uniqueValue" : "values").append("\"')"). - append(" AS ").append(schema), - () -> LOG.warn("Ignoring invalid schema '{}'", schema))); - - StringBuilder where = new StringBuilder(); - - if (queryInfo.getLeft().length() > 0) { - where.append(" WHERE ").append(queryInfo.getLeft()); - } - - if (queryInfo.getLeft().length() == 0) { - where.append(" WHERE "); - } else { - where.append(" AND "); - } - where.append(realmsFilter); - - if (obs != null) { - String obsWhere = obs.views.stream(). - filter(view -> !svs.field().name().equals(view.name()) && !svs.table().name().equals(view.name())). - map(view -> "t.id=" + view.alias() + ".any_id"). - collect(Collectors.joining(" AND ")); - if (!obsWhere.isEmpty()) { - if (where.length() == 0) { - where.append(" WHERE "); - } else { - where.append(" AND "); - } - where.append(obsWhere); - } - } + append(" AS ").append(schema))); - queryString.append(where); + return clause.toString(); } } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java index 2786163b67..3add4130f8 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AbstractAnyRepoExt.java @@ -40,15 +40,13 @@ import org.apache.syncope.core.persistence.api.entity.AnyUtils; import org.apache.syncope.core.persistence.api.entity.DerSchema; import org.apache.syncope.core.persistence.api.entity.DynRealm; -import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; -import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.Schema; import org.apache.syncope.core.persistence.api.entity.VirSchema; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.user.User; -import org.apache.syncope.core.persistence.jpa.dao.AnyFinder; +import org.apache.syncope.core.persistence.common.dao.AnyFinder; import org.apache.syncope.core.persistence.jpa.entity.AbstractAttributable; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAnyObject; import org.apache.syncope.core.persistence.jpa.entity.group.JPAGroup; @@ -139,28 +137,9 @@ public A authFind(final String key) { return any; } - @Override - @SuppressWarnings("unchecked") - public List findByPlainAttrValue( - final PlainSchema schema, - final PlainAttrValue attrValue, - final boolean ignoreCaseMatch) { - - return anyFinder.findByPlainAttrValue(table, anyUtils, schema, attrValue, ignoreCaseMatch); - } - - @Override - public Optional findByPlainAttrUniqueValue( - final PlainSchema schema, - final PlainAttrUniqueValue attrUniqueValue, - final boolean ignoreCaseMatch) { - - return anyFinder.findByPlainAttrUniqueValue(table, anyUtils, schema, attrUniqueValue, ignoreCaseMatch); - } - @Override public List findByDerAttrValue(final DerSchema derSchema, final String value, final boolean ignoreCaseMatch) { - return anyFinder.findByDerAttrValue(table, anyUtils, derSchema, value, ignoreCaseMatch); + return anyFinder.findByDerAttrValue(anyUtils.anyTypeKind(), derSchema, value, ignoreCaseMatch); } @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) @@ -239,7 +218,8 @@ protected void checkBeforeSave(final A any) { new ArrayList<>(((AbstractAttributable) any).getPlainAttrsList()).stream(). filter(attr -> attr.getUniqueValue() != null). forEach(attr -> { - Optional other = findByPlainAttrUniqueValue(attr.getSchema(), attr.getUniqueValue(), false); + Optional other = anyFinder.findByPlainAttrUniqueValue( + anyUtils.anyTypeKind(), attr.getSchema(), attr.getUniqueValue()); if (other.isEmpty() || other.get().getKey().equals(any.getKey())) { LOG.debug("No duplicate value found for {}={}", attr.getSchema().getKey(), attr.getUniqueValue().getValueAsString()); diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtImpl.java index 6bff4c4e72..abe9be08d6 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyObjectRepoExtImpl.java @@ -49,7 +49,7 @@ import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.user.URelationship; import org.apache.syncope.core.persistence.api.utils.RealmUtils; -import org.apache.syncope.core.persistence.jpa.dao.AnyFinder; +import org.apache.syncope.core.persistence.common.dao.AnyFinder; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAARelationship; import org.apache.syncope.core.persistence.jpa.entity.user.JPAURelationship; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyRepoExt.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyRepoExt.java index 93bdaec31e..a04e3bf278 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyRepoExt.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/AnyRepoExt.java @@ -25,9 +25,6 @@ import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.DerSchema; -import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; -import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.Schema; public interface AnyRepoExt> { @@ -36,11 +33,6 @@ public interface AnyRepoExt> { A authFind(String key); - List findByPlainAttrValue(PlainSchema schema, PlainAttrValue attrValue, boolean ignoreCaseMatch); - - Optional findByPlainAttrUniqueValue( - PlainSchema schema, PlainAttrUniqueValue attrUniqueValue, boolean ignoreCaseMatch); - List findByDerAttrValue(DerSchema schema, String value, boolean ignoreCaseMatch); AllowedSchemas findAllowedSchemas(A any, Class reference); 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 88b1879cd9..7f3508ecc0 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 @@ -56,7 +56,7 @@ import org.apache.syncope.core.persistence.api.search.SearchCondConverter; import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; import org.apache.syncope.core.persistence.api.utils.RealmUtils; -import org.apache.syncope.core.persistence.jpa.dao.AnyFinder; +import org.apache.syncope.core.persistence.common.dao.AnyFinder; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAADynGroupMembership; import org.apache.syncope.core.persistence.jpa.entity.anyobject.JPAAMembership; import org.apache.syncope.core.persistence.jpa.entity.group.JPATypeExtension; diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OraclePlainSchemaRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OraclePlainSchemaRepoExtImpl.java index 729336b27c..561704e53d 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OraclePlainSchemaRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/OraclePlainSchemaRepoExtImpl.java @@ -24,6 +24,7 @@ import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory; import org.apache.syncope.core.persistence.api.entity.PlainAttr; import org.apache.syncope.core.persistence.api.entity.PlainSchema; +import org.apache.syncope.core.persistence.jpa.dao.OracleJPAAnySearchDAO; import org.apache.syncope.core.persistence.jpa.dao.SearchSupport; public class OraclePlainSchemaRepoExtImpl extends AbstractPlainSchemaRepoExt { @@ -40,9 +41,8 @@ public OraclePlainSchemaRepoExtImpl( public > boolean hasAttrs(final PlainSchema schema, final Class reference) { Query query = entityManager.createNativeQuery( "SELECT COUNT(id) FROM " - + new SearchSupport(getAnyTypeKind(reference)).field().name() - + " WHERE plainSchema = ?"); - query.setParameter(1, schema.getKey()); + + new SearchSupport(getAnyTypeKind(reference)).table().name() + "," + + OracleJPAAnySearchDAO.from(schema)); return ((Number) query.getSingleResult()).intValue() > 0; } diff --git a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java index 20d04067b3..8a1d108e3e 100644 --- a/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java +++ b/core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/repo/UserRepoExtImpl.java @@ -45,7 +45,7 @@ import org.apache.syncope.core.persistence.api.entity.user.UMembership; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.api.utils.RealmUtils; -import org.apache.syncope.core.persistence.jpa.dao.AnyFinder; +import org.apache.syncope.core.persistence.common.dao.AnyFinder; import org.apache.syncope.core.persistence.jpa.entity.user.JPALinkedAccount; import org.apache.syncope.core.persistence.jpa.entity.user.JPAUMembership; import org.apache.syncope.core.spring.security.AuthContextUtils; diff --git a/core/persistence-jpa/src/main/resources/META-INF/oracle/views.xml b/core/persistence-jpa/src/main/resources/META-INF/oracle/views.xml index 0bc2c43286..c813b1cfa1 100644 --- a/core/persistence-jpa/src/main/resources/META-INF/oracle/views.xml +++ b/core/persistence-jpa/src/main/resources/META-INF/oracle/views.xml @@ -47,28 +47,6 @@ under the License. - - CREATE VIEW user_search AS - - SELECT u.id as any_id, u.*, attrs.* - FROM SyncopeUser u LEFT OUTER JOIN JSON_TABLE(u.plainAttrs, '$[*]' COLUMNS ( - plainSchema PATH '$.schema', - NESTED PATH '$.uniqueValue' COLUMNS( - ubinaryValue PATH '$.binaryValue', - ubooleanValue PATH '$.booleanValue', - udateValue PATH '$.dateValue', - ulongValue PATH '$.longValue', - udoubleValue PATH '$.doubleValue', - ustringValue PATH '$.stringValue'), - NESTED PATH '$.values[*]' COLUMNS( - binaryValue FORMAT JSON WITH WRAPPER PATH '$.binaryValue', - booleanValue FORMAT JSON WITH WRAPPER PATH '$.booleanValue', - dateValue FORMAT JSON WITH WRAPPER PATH '$.dateValue', - longValue FORMAT JSON WITH WRAPPER PATH '$.longValue', - doubleValue FORMAT JSON WITH WRAPPER PATH '$.doubleValue', - stringValue FORMAT JSON WITH WRAPPER PATH '$.stringValue') - )) AS attrs ON 1=1 - CREATE VIEW user_search_urelationship AS diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyMatchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyMatchTest.java index 816602dede..e3eb53e501 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyMatchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnyMatchTest.java @@ -62,10 +62,10 @@ public void byResourceCond() { ResourceCond resourceCond = new ResourceCond(); resourceCond.setResource("resource-testdb2"); - assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(resourceCond))); + assertTrue(anyMatcher.matches(user, SearchCond.of(resourceCond))); resourceCond.setResource("ws-target-resource-delete"); - assertFalse(anyMatcher.matches(user, SearchCond.getLeaf(resourceCond))); + assertFalse(anyMatcher.matches(user, SearchCond.of(resourceCond))); } @Test @@ -74,11 +74,11 @@ public void anyObjectMatch() { RelationshipCond relationshipCond = new RelationshipCond(); relationshipCond.setAnyObject("8559d14d-58c2-46eb-a2d4-a7d35161e8f8"); - assertTrue(anyMatcher.matches(anyObject, SearchCond.getLeaf(relationshipCond))); + assertTrue(anyMatcher.matches(anyObject, SearchCond.of(relationshipCond))); RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond(); relationshipTypeCond.setRelationshipTypeKey("neighborhood"); - assertTrue(anyMatcher.matches(anyObject, SearchCond.getLeaf(relationshipTypeCond))); + assertTrue(anyMatcher.matches(anyObject, SearchCond.of(relationshipTypeCond))); } @Test @@ -87,24 +87,24 @@ public void userMatch() { MembershipCond groupCond = new MembershipCond(); groupCond.setGroup("secretary"); - assertFalse(anyMatcher.matches(user, SearchCond.getLeaf(groupCond))); + assertFalse(anyMatcher.matches(user, SearchCond.of(groupCond))); groupCond.setGroup("root"); - assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(groupCond))); + assertTrue(anyMatcher.matches(user, SearchCond.of(groupCond))); RoleCond roleCond = new RoleCond(); roleCond.setRole("Other"); - assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(roleCond))); + assertTrue(anyMatcher.matches(user, SearchCond.of(roleCond))); user = userDAO.findById("c9b2dec2-00a7-4855-97c0-d854842b4b24").orElseThrow(); RelationshipCond relationshipCond = new RelationshipCond(); relationshipCond.setAnyObject("fc6dbc3a-6c07-4965-8781-921e7401a4a5"); - assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(relationshipCond))); + assertTrue(anyMatcher.matches(user, SearchCond.of(relationshipCond))); RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond(); relationshipTypeCond.setRelationshipTypeKey("neighborhood"); - assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(relationshipTypeCond))); + assertTrue(anyMatcher.matches(user, SearchCond.of(relationshipTypeCond))); } @Test @@ -115,11 +115,11 @@ public void groupMatch() { anyCond.setSchema("name"); anyCond.setExpression("root"); anyCond.setType(AttrCond.Type.EQ); - assertTrue(anyMatcher.matches(group, SearchCond.getLeaf(anyCond))); + assertTrue(anyMatcher.matches(group, SearchCond.of(anyCond))); AttrCond attrCond = new AttrCond(); attrCond.setSchema("show"); attrCond.setType(AttrCond.Type.ISNOTNULL); - assertTrue(anyMatcher.matches(group, SearchCond.getLeaf(attrCond))); + assertTrue(anyMatcher.matches(group, SearchCond.of(attrCond))); } } diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java index b173bcc1a5..efb8d50b26 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/AnySearchTest.java @@ -116,6 +116,61 @@ public void adjustLoginDateForLocalSystem() throws ParseException { userDAO.save(rossini); } + @Test + public void searchTwoPlainSchemas() { + AttrCond firstnameCond = new AttrCond(AttrCond.Type.EQ); + firstnameCond.setSchema("firstname"); + firstnameCond.setExpression("Gioacchino"); + + AttrCond surnameCond = new AttrCond(AttrCond.Type.EQ); + surnameCond.setSchema("surname"); + surnameCond.setExpression("Rossini"); + + SearchCond cond = SearchCond.and(SearchCond.of(firstnameCond), SearchCond.of(surnameCond)); + assertTrue(cond.isValid()); + + List users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + + surnameCond = new AttrCond(AttrCond.Type.EQ); + surnameCond.setSchema("surname"); + surnameCond.setExpression("Verdi"); + + cond = SearchCond.and(SearchCond.of(firstnameCond), SearchCond.negate(surnameCond)); + assertTrue(cond.isValid()); + + users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + + AttrCond fullnameCond = new AttrCond(AttrCond.Type.EQ); + fullnameCond.setSchema("fullname"); + fullnameCond.setExpression("Vincenzo Bellini"); + + AttrCond userIdCond = new AttrCond(AttrCond.Type.EQ); + userIdCond.setSchema("userId"); + userIdCond.setExpression("bellini@apache.org"); + + cond = SearchCond.and(SearchCond.of(fullnameCond), SearchCond.of(userIdCond)); + assertTrue(cond.isValid()); + + users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + + userIdCond = new AttrCond(AttrCond.Type.EQ); + userIdCond.setSchema("userId"); + userIdCond.setExpression("rossini@apache.org"); + + cond = SearchCond.and(SearchCond.of(fullnameCond), SearchCond.negate(userIdCond)); + assertTrue(cond.isValid()); + + users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + } + @Test public void searchWithLikeCondition() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.LIKE); @@ -129,12 +184,12 @@ public void searchWithLikeCondition() { loginDateCond.setSchema("loginDate"); loginDateCond.setExpression(LOGIN_DATE_VALUE); - SearchCond subCond = SearchCond.getAnd( - SearchCond.getLeaf(fullnameLeafCond), SearchCond.getLeaf(groupCond)); + SearchCond subCond = SearchCond.and( + SearchCond.of(fullnameLeafCond), SearchCond.of(groupCond)); assertTrue(subCond.isValid()); - SearchCond cond = SearchCond.getAnd(subCond, SearchCond.getLeaf(loginDateCond)); + SearchCond cond = SearchCond.and(subCond, SearchCond.of(loginDateCond)); assertTrue(cond.isValid()); @@ -156,12 +211,12 @@ public void searchCaseInsensitiveWithLikeCondition() { loginDateCond.setSchema("loginDate"); loginDateCond.setExpression(LOGIN_DATE_VALUE); - SearchCond subCond = SearchCond.getAnd( - SearchCond.getLeaf(fullnameLeafCond), SearchCond.getLeaf(groupCond)); + SearchCond subCond = SearchCond.and( + SearchCond.of(fullnameLeafCond), SearchCond.of(groupCond)); assertTrue(subCond.isValid()); - SearchCond cond = SearchCond.getAnd(subCond, SearchCond.getLeaf(loginDateCond)); + SearchCond cond = SearchCond.and(subCond, SearchCond.of(loginDateCond)); assertTrue(cond.isValid()); @@ -176,7 +231,7 @@ public void searchWithNotAttrCond() { fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("Giuseppe Verdi"); - SearchCond cond = SearchCond.getNotLeaf(fullnameLeafCond); + SearchCond cond = SearchCond.negate(fullnameLeafCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); @@ -194,7 +249,7 @@ public void searchWithNotAnyCond() { usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("verdi"); - SearchCond cond = SearchCond.getNotLeaf(usernameLeafCond); + SearchCond cond = SearchCond.negate(usernameLeafCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); @@ -210,7 +265,7 @@ public void searchCaseInsensitiveWithNotCondition() { fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("giuseppe verdi"); - SearchCond cond = SearchCond.getNotLeaf(fullnameLeafCond); + SearchCond cond = SearchCond.negate(fullnameLeafCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); @@ -228,7 +283,7 @@ public void searchByBoolean() { coolLeafCond.setSchema("cool"); coolLeafCond.setExpression("true"); - SearchCond cond = SearchCond.getLeaf(coolLeafCond); + SearchCond cond = SearchCond.of(coolLeafCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); @@ -244,13 +299,13 @@ public void searchByRealm() { anyCond.setSchema("realm"); anyCond.setExpression("c5b75db1-fce7-470f-b780-3b9934d82a9d"); - List users = searchDAO.search(SearchCond.getLeaf(anyCond), AnyTypeKind.USER); + List users = searchDAO.search(SearchCond.of(anyCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); assertEquals("rossini", users.get(0).getUsername()); anyCond.setExpression("/even"); - users = searchDAO.search(SearchCond.getLeaf(anyCond), AnyTypeKind.USER); + users = searchDAO.search(SearchCond.of(anyCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); assertEquals("rossini", users.get(0).getUsername()); @@ -269,12 +324,12 @@ public void searchByPageAndSize() { loginDateCond.setSchema("loginDate"); loginDateCond.setExpression(LOGIN_DATE_VALUE); - SearchCond subCond = SearchCond.getAnd( - SearchCond.getLeaf(fullnameLeafCond), SearchCond.getLeaf(groupCond)); + SearchCond subCond = SearchCond.and( + SearchCond.of(fullnameLeafCond), SearchCond.of(groupCond)); assertTrue(subCond.isValid()); - SearchCond cond = SearchCond.getAnd(subCond, SearchCond.getLeaf(loginDateCond)); + SearchCond cond = SearchCond.and(subCond, SearchCond.of(loginDateCond)); assertTrue(cond.isValid()); @@ -300,13 +355,13 @@ public void searchByGroup() { MembershipCond groupCond = new MembershipCond(); groupCond.setGroup("child"); - List matchingChild = searchDAO.search(SearchCond.getLeaf(groupCond), AnyTypeKind.USER); + List matchingChild = searchDAO.search(SearchCond.of(groupCond), AnyTypeKind.USER); assertNotNull(matchingChild); assertTrue(matchingChild.stream().anyMatch(user -> "verdi".equals(user.getUsername()))); groupCond.setGroup("otherchild"); - List matchingOtherChild = searchDAO.search(SearchCond.getLeaf(groupCond), AnyTypeKind.USER); + List matchingOtherChild = searchDAO.search(SearchCond.of(groupCond), AnyTypeKind.USER); assertNotNull(matchingOtherChild); assertTrue(matchingOtherChild.stream().anyMatch(user -> "rossini".equals(user.getUsername()))); @@ -317,14 +372,14 @@ public void searchByGroup() { groupCond.setGroup("%child"); - List matchingStar = searchDAO.search(SearchCond.getLeaf(groupCond), AnyTypeKind.USER); + List matchingStar = searchDAO.search(SearchCond.of(groupCond), AnyTypeKind.USER); assertNotNull(matchingStar); assertTrue(matchingStar.stream().anyMatch(user -> "verdi".equals(user.getUsername()))); assertTrue(matchingStar.stream().anyMatch(user -> "rossini".equals(user.getUsername()))); assertEquals(union, matchingStar.stream().map(User::getUsername).collect(Collectors.toSet())); matchingStar = searchDAO.search(realmDAO.getRoot(), false, SyncopeConstants.FULL_ADMIN_REALMS, - SearchCond.getLeaf(groupCond), Pageable.unpaged(), AnyTypeKind.USER); + SearchCond.of(groupCond), Pageable.unpaged(), AnyTypeKind.USER); assertNotNull(matchingStar); assertTrue(matchingStar.stream().anyMatch(user -> "verdi".equals(user.getUsername()))); assertTrue(matchingStar.stream().noneMatch(user -> "rossini".equals(user.getUsername()))); @@ -335,7 +390,7 @@ public void searchByRole() { RoleCond roleCond = new RoleCond(); roleCond.setRole("Other"); - List users = searchDAO.search(SearchCond.getLeaf(roleCond), AnyTypeKind.USER); + List users = searchDAO.search(SearchCond.of(roleCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); } @@ -345,7 +400,7 @@ public void searchByPrivilege() { PrivilegeCond privilegeCond = new PrivilegeCond(); privilegeCond.setPrivilege("postMighty"); - List users = searchDAO.search(SearchCond.getLeaf(privilegeCond), AnyTypeKind.USER); + List users = searchDAO.search(SearchCond.of(privilegeCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); } @@ -355,14 +410,14 @@ public void searchByIsNull() { AttrCond coolLeafCond = new AttrCond(AttrCond.Type.ISNULL); coolLeafCond.setSchema("cool"); - List users = searchDAO.search(SearchCond.getLeaf(coolLeafCond), AnyTypeKind.USER); + List users = searchDAO.search(SearchCond.of(coolLeafCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(4, users.size()); coolLeafCond = new AttrCond(AttrCond.Type.ISNOTNULL); coolLeafCond.setSchema("cool"); - users = searchDAO.search(SearchCond.getLeaf(coolLeafCond), AnyTypeKind.USER); + users = searchDAO.search(SearchCond.of(coolLeafCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); } @@ -372,7 +427,7 @@ public void searchByAuxClass() { AuxClassCond ac = new AuxClassCond(); ac.setAuxClass("csv"); - List groups = searchDAO.search(SearchCond.getLeaf(ac), AnyTypeKind.GROUP); + List groups = searchDAO.search(SearchCond.of(ac), AnyTypeKind.GROUP); assertNotNull(groups); assertEquals(2, groups.size()); } @@ -385,7 +440,7 @@ public void searchByResource() { ResourceCond ws1 = new ResourceCond(); ws1.setResource("ws-target-resource-list-mappings-2"); - SearchCond searchCondition = SearchCond.getAnd(SearchCond.getNotLeaf(ws2), SearchCond.getLeaf(ws1)); + SearchCond searchCondition = SearchCond.and(SearchCond.negate(ws2), SearchCond.of(ws1)); assertTrue(searchCondition.isValid()); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); @@ -399,7 +454,7 @@ public void searchByBooleanAttrCond() { booleanCond.setSchema("show"); booleanCond.setExpression("true"); - List matchingGroups = searchDAO.search(SearchCond.getLeaf(booleanCond), AnyTypeKind.GROUP); + List matchingGroups = searchDAO.search(SearchCond.of(booleanCond), AnyTypeKind.GROUP); assertNotNull(matchingGroups); assertFalse(matchingGroups.isEmpty()); } @@ -414,9 +469,9 @@ public void searchByUsernameAndKey() { idRightCond.setSchema("key"); idRightCond.setExpression("2"); - SearchCond searchCondition = SearchCond.getAnd( - SearchCond.getLeaf(usernameLeafCond), - SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.and( + SearchCond.of(usernameLeafCond), + SearchCond.of(idRightCond)); List matching = searchDAO.search(searchCondition, AnyTypeKind.USER); assertNotNull(matching); @@ -435,9 +490,9 @@ public void searchByGroupNameAndKey() { idRightCond.setSchema("key"); idRightCond.setExpression("37d15e4c-cdc1-460b-a591-8505c8133806"); - SearchCond searchCondition = SearchCond.getAnd( - SearchCond.getLeaf(groupNameLeafCond), - SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.and( + SearchCond.of(groupNameLeafCond), + SearchCond.of(idRightCond)); assertTrue(searchCondition.isValid()); @@ -458,9 +513,9 @@ public void searchByUsernameAndFullname() { idRightCond.setSchema("fullname"); idRightCond.setExpression("Giuseppe V%"); - SearchCond searchCondition = SearchCond.getOr( - SearchCond.getLeaf(usernameLeafCond), - SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.or( + SearchCond.of(usernameLeafCond), + SearchCond.of(idRightCond)); List matchingUsers = searchDAO.search(searchCondition, AnyTypeKind.USER); assertNotNull(matchingUsers); @@ -477,9 +532,9 @@ public void searchByUsernameAndFullnameIgnoreCase() { idRightCond.setSchema("fullname"); idRightCond.setExpression("gIuseppe v%"); - SearchCond searchCondition = SearchCond.getOr( - SearchCond.getLeaf(usernameLeafCond), - SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.or( + SearchCond.of(usernameLeafCond), + SearchCond.of(idRightCond)); List matchingUsers = searchDAO.search(searchCondition, AnyTypeKind.USER); assertNotNull(matchingUsers); @@ -492,7 +547,7 @@ public void searchByKey() { idLeafCond.setSchema("key"); idLeafCond.setExpression("74cd8ece-715a-44a4-a736-e17b46c4e7e6"); - SearchCond searchCondition = SearchCond.getLeaf(idLeafCond); + SearchCond searchCondition = SearchCond.of(idLeafCond); assertTrue(searchCondition.isValid()); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); @@ -506,7 +561,7 @@ public void searchByType() { AnyTypeCond tcond = new AnyTypeCond(); tcond.setAnyTypeKey("PRINTER"); - SearchCond searchCondition = SearchCond.getLeaf(tcond); + SearchCond searchCondition = SearchCond.of(tcond); assertTrue(searchCondition.isValid()); List printers = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); @@ -528,8 +583,8 @@ public void searchByRelationshipType() { AnyTypeCond tcond = new AnyTypeCond(); tcond.setAnyTypeKey("PRINTER"); - SearchCond searchCondition = SearchCond.getAnd( - SearchCond.getLeaf(relationshipTypeCond), SearchCond.getLeaf(tcond)); + SearchCond searchCondition = SearchCond.and( + SearchCond.of(relationshipTypeCond), SearchCond.of(tcond)); assertTrue(searchCondition.isValid()); List anyObjects = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); @@ -539,7 +594,7 @@ public void searchByRelationshipType() { assertTrue(anyObjects.stream().anyMatch(any -> "8559d14d-58c2-46eb-a2d4-a7d35161e8f8".equals(any.getKey()))); // 2. search for users involved in "neighborhood" relationship - searchCondition = SearchCond.getLeaf(relationshipTypeCond); + searchCondition = SearchCond.of(relationshipTypeCond); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); @@ -552,7 +607,7 @@ public void searchByAnyCondDate() { creationDateCond.setSchema("creationDate"); creationDateCond.setExpression("2021-04-15 12:45:00"); - SearchCond searchCondition = SearchCond.getLeaf(creationDateCond); + SearchCond searchCondition = SearchCond.of(creationDateCond); assertTrue(searchCondition.isValid()); List anyObjects = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); @@ -567,7 +622,7 @@ public void searchByAttrCondDate() { loginDateCond.setSchema("loginDate"); loginDateCond.setExpression("2009-05-27"); - SearchCond searchCondition = SearchCond.getLeaf(loginDateCond); + SearchCond searchCondition = SearchCond.of(loginDateCond); assertTrue(searchCondition.isValid()); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); @@ -584,8 +639,8 @@ public void userOrderBy() { AttrCond idRightCond = new AttrCond(AttrCond.Type.LIKE); idRightCond.setSchema("fullname"); idRightCond.setExpression("Giuseppe V%"); - SearchCond searchCondition = SearchCond.getOr( - SearchCond.getLeaf(usernameLeafCond), SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.or( + SearchCond.of(usernameLeafCond), SearchCond.of(idRightCond)); List orderByClauses = new ArrayList<>(); orderByClauses.add(new Sort.Order(Sort.Direction.DESC, "username")); @@ -604,7 +659,7 @@ public void groupOrderBy() { AnyCond idLeafCond = new AnyCond(AnyCond.Type.LIKE); idLeafCond.setSchema("name"); idLeafCond.setExpression("%r"); - SearchCond searchCondition = SearchCond.getLeaf(idLeafCond); + SearchCond searchCondition = SearchCond.of(idLeafCond); assertTrue(searchCondition.isValid()); Sort.Order orderByClause = new Sort.Order(Sort.DEFAULT_DIRECTION, "name"); @@ -622,7 +677,7 @@ public void groupOrderBy() { public void member() { MemberCond memberCond = new MemberCond(); memberCond.setMember("1417acbe-cbf6-4277-9372-e75e04f97000"); - SearchCond searchCondition = SearchCond.getLeaf(memberCond); + SearchCond searchCondition = SearchCond.of(memberCond); assertTrue(searchCondition.isValid()); List groups = searchDAO.search(searchCondition, AnyTypeKind.GROUP); @@ -693,7 +748,7 @@ public void changePwdDate() { AnyCond changePwdDateCond = new AnyCond(AttrCond.Type.ISNULL); changePwdDateCond.setSchema("changePwdDate"); - SearchCond cond = SearchCond.getAnd(SearchCond.getNotLeaf(statusCond), SearchCond.getLeaf(changePwdDateCond)); + SearchCond cond = SearchCond.and(SearchCond.negate(statusCond), SearchCond.of(changePwdDateCond)); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); @@ -710,7 +765,7 @@ public void issue202() { ws1.setResource("ws-target-resource-list-mappings-1"); SearchCond searchCondition = - SearchCond.getAnd(SearchCond.getNotLeaf(ws2), SearchCond.getNotLeaf(ws1)); + SearchCond.and(SearchCond.negate(ws2), SearchCond.negate(ws1)); assertTrue(searchCondition.isValid()); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); @@ -725,7 +780,7 @@ public void issue242() { cond.setSchema("key"); cond.setExpression("test%"); - SearchCond searchCondition = SearchCond.getLeaf(cond); + SearchCond searchCondition = SearchCond.of(cond); assertTrue(searchCondition.isValid()); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); @@ -739,7 +794,7 @@ public void issueSYNCOPE46() { cond.setSchema("username"); cond.setExpression("%ossin%"); - SearchCond searchCondition = SearchCond.getLeaf(cond); + SearchCond searchCondition = SearchCond.of(cond); assertTrue(searchCondition.isValid()); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); @@ -756,8 +811,7 @@ public void issueSYNCOPE433() { likeCond.setSchema("username"); likeCond.setExpression("%ossin%"); - SearchCond searchCond = SearchCond.getOr( - SearchCond.getLeaf(isNullCond), SearchCond.getLeaf(likeCond)); + SearchCond searchCond = SearchCond.or(SearchCond.of(isNullCond), SearchCond.of(likeCond)); long count = searchDAO.count( realmDAO.getRoot(), true, SyncopeConstants.FULL_ADMIN_REALMS, searchCond, AnyTypeKind.USER); @@ -774,13 +828,13 @@ public void issueSYNCOPE929() { genderCond.setSchema("gender"); genderCond.setExpression("M"); - SearchCond orCond = SearchCond.getOr(SearchCond.getLeaf(rossiniCond), SearchCond.getLeaf(genderCond)); + SearchCond orCond = SearchCond.or(SearchCond.of(rossiniCond), SearchCond.of(genderCond)); AttrCond belliniCond = new AttrCond(AttrCond.Type.EQ); belliniCond.setSchema("surname"); belliniCond.setExpression("Bellini"); - SearchCond searchCond = SearchCond.getAnd(orCond, SearchCond.getLeaf(belliniCond)); + SearchCond searchCond = SearchCond.and(orCond, SearchCond.of(belliniCond)); List users = searchDAO.search(searchCond, AnyTypeKind.USER); assertNotNull(users); @@ -821,7 +875,7 @@ public void issueSYNCOPE980() { MembershipCond groupCond = new MembershipCond(); groupCond.setGroup("citizen"); - SearchCond searchCondition = SearchCond.getLeaf(groupCond); + SearchCond searchCondition = SearchCond.of(groupCond); List matching = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); assertEquals(2, matching.size()); @@ -829,7 +883,7 @@ public void issueSYNCOPE980() { AnyTypeCond anyTypeCond = new AnyTypeCond(); anyTypeCond.setAnyTypeKey(service.getKey()); - searchCondition = SearchCond.getAnd(SearchCond.getLeaf(groupCond), SearchCond.getLeaf(anyTypeCond)); + searchCondition = SearchCond.and(SearchCond.of(groupCond), SearchCond.of(anyTypeCond)); matching = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); assertEquals(1, matching.size()); @@ -849,7 +903,7 @@ public void issueSYNCOPE983() { realmDAO.getRoot(), true, SyncopeConstants.FULL_ADMIN_REALMS, - SearchCond.getLeaf(fullnameLeafCond), + SearchCond.of(fullnameLeafCond), Pageable.unpaged(Sort.by(orderByClauses)), AnyTypeKind.USER); assertFalse(users.isEmpty()); @@ -863,7 +917,7 @@ public void issueSYNCOPE1416() { AttrCond idRightCond = new AttrCond(AttrCond.Type.ISNOTNULL); idRightCond.setSchema("firstname"); - SearchCond searchCondition = SearchCond.getAnd(SearchCond.getLeaf(idLeftCond), SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.and(SearchCond.of(idLeftCond), SearchCond.of(idRightCond)); List orderByClauses = List.of(new Sort.Order(Sort.Direction.ASC, "ctype")); @@ -876,7 +930,7 @@ public void issueSYNCOPE1416() { AttrCond fullnameCond = new AttrCond(AttrCond.Type.ISNOTNULL); fullnameCond.setSchema("fullname"); - SearchCond cond = SearchCond.getLeaf(fullnameCond); + SearchCond cond = SearchCond.of(fullnameCond); assertTrue(cond.isValid()); users = searchDAO.search(cond, AnyTypeKind.USER); @@ -885,7 +939,7 @@ public void issueSYNCOPE1416() { fullnameCond = new AttrCond(AttrCond.Type.ISNULL); fullnameCond.setSchema("fullname"); - cond = SearchCond.getLeaf(fullnameCond); + cond = SearchCond.of(fullnameCond); assertTrue(cond.isValid()); users = searchDAO.search(cond, AnyTypeKind.USER); @@ -898,7 +952,7 @@ public void issueSYNCOPE1419() { loginDateCond.setSchema("loginDate"); loginDateCond.setExpression(LOGIN_DATE_VALUE); - SearchCond cond = SearchCond.getNotLeaf(loginDateCond); + SearchCond cond = SearchCond.negate(loginDateCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java index 6105bcf5a2..179ea8e749 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/inner/UserTest.java @@ -27,7 +27,6 @@ import java.time.OffsetDateTime; import java.util.List; -import java.util.Optional; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; @@ -36,10 +35,7 @@ import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.user.UMembership; -import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrUniqueValue; -import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.jpa.AbstractTest; import org.apache.syncope.core.spring.security.Encryptor; @@ -141,35 +137,6 @@ public void findByInvalidDerAttrExpression() { derSchemaDAO.findById("noschema").orElseThrow(), "Antonio, Maria", false).isEmpty()); } - @Test - public void findByPlainAttrUniqueValue() { - UPlainAttrUniqueValue fullnameValue = entityFactory.newEntity(UPlainAttrUniqueValue.class); - fullnameValue.setStringValue("Gioacchino Rossini"); - - PlainSchema fullname = plainSchemaDAO.findById("fullname").orElseThrow(); - - Optional found = userDAO.findByPlainAttrUniqueValue(fullname, fullnameValue, false); - assertTrue(found.isPresent()); - - fullnameValue.setStringValue("Gioacchino ROSSINI"); - - found = userDAO.findByPlainAttrUniqueValue(fullname, fullnameValue, false); - assertFalse(found.isPresent()); - - found = userDAO.findByPlainAttrUniqueValue(fullname, fullnameValue, true); - assertTrue(found.isPresent()); - } - - @Test - public void findByPlainAttrBooleanValue() { - UPlainAttrValue coolValue = entityFactory.newEntity(UPlainAttrValue.class); - coolValue.setBooleanValue(true); - - List list = userDAO.findByPlainAttrValue( - plainSchemaDAO.findById("cool").orElseThrow(), coolValue, false); - assertEquals(1, list.size()); - } - @Test public void findByKey() { assertTrue(userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").isPresent()); diff --git a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java index 0f0c6ca389..b7f6365639 100644 --- a/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java +++ b/core/persistence-jpa/src/test/java/org/apache/syncope/core/persistence/jpa/outer/AnySearchTest.java @@ -111,7 +111,7 @@ public void searchByDynMembership() { RoleCond roleCond = new RoleCond(); roleCond.setRole(role.getKey()); - List users = searchDAO.search(SearchCond.getLeaf(roleCond), AnyTypeKind.USER); + List users = searchDAO.search(SearchCond.of(roleCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); assertEquals("c9b2dec2-00a7-4855-97c0-d854842b4b24", users.get(0).getKey()); @@ -141,7 +141,7 @@ public void searchAsGroupOwner() { List users = searchDAO.search( realmDAO.getRoot(), true, Set.of(SyncopeConstants.ROOT_REALM), - SearchCond.getLeaf(anyCond), PageRequest.of(0, 100), AnyTypeKind.USER); + SearchCond.of(anyCond), PageRequest.of(0, 100), AnyTypeKind.USER); assertNotNull(users); assertTrue(users.stream().anyMatch(user -> rossini.getKey().equals(user.getKey()))); @@ -149,7 +149,7 @@ public void searchAsGroupOwner() { users = searchDAO.search( group.getRealm(), true, Set.of(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey())), - SearchCond.getLeaf(anyCond), PageRequest.of(0, 100), AnyTypeKind.USER); + SearchCond.of(anyCond), PageRequest.of(0, 100), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); assertEquals(rossini.getKey(), users.get(0).getKey()); @@ -160,7 +160,7 @@ public void searchByMembershipAttribute() { AttrCond attrCond = new AttrCond(AttrCond.Type.EQ); attrCond.setSchema("ctype"); attrCond.setExpression("otherchildctype"); - SearchCond cond = SearchCond.getLeaf(attrCond); + SearchCond cond = SearchCond.of(attrCond); List results = searchDAO.search(cond, AnyTypeKind.ANY_OBJECT); assertTrue(results.isEmpty()); @@ -196,7 +196,7 @@ public void issueSYNCOPE95() { coolLeafCond.setSchema("cool"); coolLeafCond.setExpression("true"); - SearchCond cond = SearchCond.getLeaf(coolLeafCond); + SearchCond cond = SearchCond.of(coolLeafCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); @@ -214,8 +214,8 @@ public void issueSYNCOPE1417() { AttrCond idRightCond = new AttrCond(AttrCond.Type.LIKE); idRightCond.setSchema("fullname"); idRightCond.setExpression("Giuseppe V%"); - SearchCond searchCondition = SearchCond.getOr( - SearchCond.getLeaf(usernameLeafCond), SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.or( + SearchCond.of(usernameLeafCond), SearchCond.of(idRightCond)); List orderByClauses = new ArrayList<>(); orderByClauses.add(new Sort.Order(Sort.Direction.DESC, "surname")); @@ -255,7 +255,7 @@ public void issueSYNCOPE1512() { titleCond.setSchema("title"); titleCond.setExpression("syncope's group"); - List matching = searchDAO.search(SearchCond.getLeaf(titleCond), AnyTypeKind.GROUP); + List matching = searchDAO.search(SearchCond.of(titleCond), AnyTypeKind.GROUP); assertEquals(1, matching.size()); assertEquals(group.getKey(), matching.get(0).getKey()); @@ -263,7 +263,7 @@ public void issueSYNCOPE1512() { originalNameCond.setSchema("originalName"); originalNameCond.setExpression("syncope's group"); - matching = searchDAO.search(SearchCond.getLeaf(originalNameCond), AnyTypeKind.GROUP); + matching = searchDAO.search(SearchCond.of(originalNameCond), AnyTypeKind.GROUP); assertEquals(1, matching.size()); assertEquals(group.getKey(), matching.get(0).getKey()); } @@ -275,7 +275,7 @@ public void issueSYNCOPE1790() { emailCond.setSchema("email"); emailCond.setExpression("verdi@syncope.org"); - SearchCond cond = SearchCond.getLeaf(emailCond); + SearchCond cond = SearchCond.of(emailCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); 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 ace35377f5..97a8186f38 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 @@ -96,11 +96,11 @@ public void misc() { DynRealmCond dynRealmCond = new DynRealmCond(); dynRealmCond.setDynRealm(actual.getKey()); - List matching = searchDAO.search(SearchCond.getLeaf(dynRealmCond), AnyTypeKind.USER); + List matching = searchDAO.search(SearchCond.of(dynRealmCond), AnyTypeKind.USER); assertNotNull(matching); assertFalse(matching.isEmpty()); User user = matching.get(0); - assertTrue(anyMatchDAO.matches(user, SearchCond.getLeaf(dynRealmCond))); + assertTrue(anyMatchDAO.matches(user, SearchCond.of(dynRealmCond))); assertTrue(userDAO.findDynRealms(user.getKey()).contains(actual.getKey())); } @@ -139,11 +139,11 @@ public void issueSYNCOPE1806() { // 2. verify that dynamic members are the same DynRealmCond dynRealmCond1 = new DynRealmCond(); dynRealmCond1.setDynRealm(realm1.getKey()); - List matching1 = searchDAO.search(SearchCond.getLeaf(dynRealmCond1), AnyTypeKind.USER); + List matching1 = searchDAO.search(SearchCond.of(dynRealmCond1), AnyTypeKind.USER); DynRealmCond dynRealmCond2 = new DynRealmCond(); dynRealmCond2.setDynRealm(realm2.getKey()); - List matching2 = searchDAO.search(SearchCond.getLeaf(dynRealmCond2), AnyTypeKind.USER); + List matching2 = searchDAO.search(SearchCond.of(dynRealmCond2), AnyTypeKind.USER); assertEquals(matching1, matching2); assertEquals(1, matching1.size()); @@ -159,8 +159,8 @@ public void issueSYNCOPE1806() { 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); + matching1 = searchDAO.search(SearchCond.of(dynRealmCond1), AnyTypeKind.USER); + matching2 = searchDAO.search(SearchCond.of(dynRealmCond2), AnyTypeKind.USER); assertEquals(matching1, matching2); assertEquals(2, matching1.size()); assertTrue(matching1.stream().anyMatch(u -> "c9b2dec2-00a7-4855-97c0-d854842b4b24".equals(u.getKey()))); diff --git a/core/persistence-jpa/src/test/resources/simplelogger.properties b/core/persistence-jpa/src/test/resources/simplelogger.properties index 828cc0f105..eadbb79293 100644 --- a/core/persistence-jpa/src/test/resources/simplelogger.properties +++ b/core/persistence-jpa/src/test/resources/simplelogger.properties @@ -17,8 +17,9 @@ # See http://www.slf4j.org/api/org/slf4j/impl/SimpleLogger.html # Possible values: "trace", "debug", "info", "warn", or "error" +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=YYYY-mm-dd HH:mm:ss.SSS org.slf4j.simpleLogger.defaultLogLevel=debug org.slf4j.simpleLogger.log.org.springframework.jdbc.core.JdbcTemplate=error org.slf4j.simpleLogger.log.org.springframework.jdbc.datasource.DataSourceUtils=error org.slf4j.simpleLogger.log.com.github.dockerjava.zerodep.shaded=error - diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java index f5a716fa90..e46cb9ebbf 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/PersistenceContext.java @@ -90,6 +90,7 @@ import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; import org.apache.syncope.core.persistence.common.CommonPersistenceContext; import org.apache.syncope.core.persistence.common.RuntimeDomainLoader; +import org.apache.syncope.core.persistence.common.dao.AnyFinder; import org.apache.syncope.core.persistence.neo4j.content.XMLContentExporter; import org.apache.syncope.core.persistence.neo4j.content.XMLContentLoader; import org.apache.syncope.core.persistence.neo4j.dao.Neo4jAnyMatchDAO; @@ -467,6 +468,12 @@ public SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory( return new SyncopeNeo4jRepositoryFactory(neo4jOperations, mappingContext); } + @ConditionalOnMissingBean + @Bean + public AnyFinder anyFinder(final @Lazy PlainSchemaDAO plainSchemaDAO, final @Lazy AnySearchDAO anySearchDAO) { + return new AnyFinder(plainSchemaDAO, anySearchDAO); + } + @ConditionalOnMissingBean @Bean public AccessTokenDAO accessTokenDAO(final SyncopeNeo4jRepositoryFactory neo4jRepositoryFactory) { @@ -519,6 +526,7 @@ public AnyObjectRepoExt anyObjectRepoExt( final @Lazy DynRealmDAO dynRealmDAO, final @Lazy UserDAO userDAO, final @Lazy GroupDAO groupDAO, + final @Lazy AnyFinder anyFinder, final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient, final NodeValidator nodeValidator, @@ -534,6 +542,7 @@ public AnyObjectRepoExt anyObjectRepoExt( dynRealmDAO, userDAO, groupDAO, + anyFinder, neo4jTemplate, neo4jClient, nodeValidator, @@ -955,6 +964,7 @@ public GroupRepoExt groupRepoExt( final @Lazy UserDAO userDAO, final @Lazy AnyObjectDAO anyObjectDAO, final AnySearchDAO anySearchDAO, + final @Lazy AnyFinder anyFinder, final SearchCondVisitor searchCondVisitor, final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient, @@ -974,6 +984,7 @@ public GroupRepoExt groupRepoExt( userDAO, anyObjectDAO, anySearchDAO, + anyFinder, searchCondVisitor, neo4jTemplate, neo4jClient, @@ -1476,6 +1487,7 @@ public UserRepoExt userRepoExt( final @Lazy GroupDAO groupDAO, final DelegationDAO delegationDAO, final FIQLQueryDAO fiqlQueryDAO, + final @Lazy AnyFinder anyFinder, final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient, final NodeValidator nodeValidator, @@ -1494,6 +1506,7 @@ public UserRepoExt userRepoExt( groupDAO, delegationDAO, fiqlQueryDAO, + anyFinder, securityProperties, neo4jTemplate, neo4jClient, diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java index 0fd7c73516..591ef5d6d9 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/Neo4jAnySearchDAO.java @@ -108,8 +108,6 @@ protected static record QueryInfo( } - protected static final String ALWAYS_FALSE_ASSERTION = "1=2"; - protected static String setParameter(final Map parameters, final Object parameter) { String name = "param" + parameters.size(); parameters.put(name, parameter); @@ -480,7 +478,7 @@ protected void fillAttrQuery( " =~ \"" + (lower ? "(?i)" : "") + AnyRepoExt.escapeForLikeRegex(value).replace("%", ".*") + '"'); } else { - query.append(ALWAYS_FALSE_ASSERTION); + query.append(ALWAYS_FALSE_CLAUSE); LOG.error("LIKE is only compatible with string or enum schemas"); } } @@ -582,7 +580,7 @@ protected void fillAttrQuery( query.append('$').append(setParameter(parameters, cond.getExpression().replace("%", ".*"))); } } else { - query.append(' ').append(ALWAYS_FALSE_ASSERTION); + query.append(' ').append(ALWAYS_FALSE_CLAUSE); LOG.error("LIKE is only compatible with string or enum schemas"); } } @@ -762,50 +760,50 @@ protected QueryInfo getQuery(final AnyTypeKind kind, final SearchCond cond, fina switch (cond.getType()) { case LEAF, NOT_LEAF -> { - cond.getLeaf(AnyTypeCond.class). + cond.asLeaf(AnyTypeCond.class). filter(leaf -> AnyTypeKind.ANY_OBJECT == kind). ifPresent(leaf -> query.append(getQuery(leaf, not, parameters))); - cond.getLeaf(AuxClassCond.class). + cond.asLeaf(AuxClassCond.class). ifPresent(leaf -> query.append(getQuery(leaf, not, parameters))); - cond.getLeaf(RelationshipTypeCond.class). + cond.asLeaf(RelationshipTypeCond.class). filter(leaf -> AnyTypeKind.GROUP != kind). ifPresent(leaf -> query.append(getQuery(kind, leaf, not, parameters))); - cond.getLeaf(RelationshipCond.class). + cond.asLeaf(RelationshipCond.class). filter(leaf -> AnyTypeKind.GROUP != kind). ifPresent(leaf -> query.append(getQuery(kind, leaf, not, parameters))); - cond.getLeaf(MembershipCond.class). + cond.asLeaf(MembershipCond.class). filter(leaf -> AnyTypeKind.GROUP != kind). ifPresent(leaf -> query.append(getQuery(leaf, not, parameters))); - cond.getLeaf(MemberCond.class). + cond.asLeaf(MemberCond.class). filter(leaf -> AnyTypeKind.GROUP == kind). ifPresent(leaf -> query.append(getQuery(leaf, not, parameters))); - cond.getLeaf(RoleCond.class). + cond.asLeaf(RoleCond.class). filter(leaf -> AnyTypeKind.USER == kind). ifPresent(leaf -> query.append(getQuery(leaf, not, parameters))); - cond.getLeaf(PrivilegeCond.class). + cond.asLeaf(PrivilegeCond.class). filter(leaf -> AnyTypeKind.USER == kind). ifPresent(leaf -> query.append(getQuery(leaf, not, parameters))); - cond.getLeaf(DynRealmCond.class). + cond.asLeaf(DynRealmCond.class). ifPresent(leaf -> query.append(getQuery(leaf, not, parameters))); - cond.getLeaf(ResourceCond.class). + cond.asLeaf(ResourceCond.class). ifPresent(leaf -> query.append(getQuery(kind, leaf, not, parameters))); - cond.getLeaf(AnyCond.class).ifPresentOrElse( + cond.asLeaf(AnyCond.class).ifPresentOrElse( anyCond -> { Pair anyCondResult = getQuery(kind, anyCond, not, parameters); query.append(anyCondResult.getLeft()); Optional.ofNullable(anyCondResult.getRight()).ifPresent(involvedFields::add); }, - () -> cond.getLeaf(AttrCond.class).ifPresent(leaf -> { + () -> cond.asLeaf(AttrCond.class).ifPresent(leaf -> { Pair attrCondResult = getQuery(kind, leaf, not, parameters); query.append(attrCondResult.getLeft()); involvedPlainSchemas.add(attrCondResult.getRight()); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractAnyRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractAnyRepoExt.java index 8857f06f90..490be35fb0 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractAnyRepoExt.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AbstractAnyRepoExt.java @@ -28,12 +28,7 @@ import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; -import java.util.stream.Collectors; import javax.cache.Cache; -import org.apache.commons.jexl3.parser.Parser; -import org.apache.commons.jexl3.parser.ParserConstants; -import org.apache.commons.jexl3.parser.Token; -import org.apache.commons.lang3.StringUtils; import org.apache.syncope.core.persistence.api.dao.AllowedSchemas; import org.apache.syncope.core.persistence.api.dao.AnyTypeClassDAO; import org.apache.syncope.core.persistence.api.dao.AnyTypeDAO; @@ -49,21 +44,18 @@ import org.apache.syncope.core.persistence.api.entity.AnyUtils; import org.apache.syncope.core.persistence.api.entity.DerSchema; import org.apache.syncope.core.persistence.api.entity.ExternalResource; -import org.apache.syncope.core.persistence.api.entity.PlainAttr; -import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; -import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.Schema; import org.apache.syncope.core.persistence.api.entity.VirSchema; import org.apache.syncope.core.persistence.api.entity.anyobject.AnyObject; import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.user.User; +import org.apache.syncope.core.persistence.common.dao.AnyFinder; import org.apache.syncope.core.persistence.neo4j.dao.AbstractDAO; import org.apache.syncope.core.persistence.neo4j.entity.AbstractAny; import org.apache.syncope.core.persistence.neo4j.entity.EntityCacheKey; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jDynRealm; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; -import org.apache.syncope.core.provisioning.api.serialization.POJOHelper; import org.apache.syncope.core.spring.security.AuthContextUtils; import org.springframework.data.neo4j.core.Neo4jClient; import org.springframework.data.neo4j.core.Neo4jTemplate; @@ -108,6 +100,8 @@ protected static List split(final String attrValue, final List l protected final DynRealmDAO dynRealmDAO; + protected final AnyFinder anyFinder; + protected final AnyUtils anyUtils; protected AbstractAnyRepoExt( @@ -117,6 +111,7 @@ protected AbstractAnyRepoExt( final DerSchemaDAO derSchemaDAO, final VirSchemaDAO virSchemaDAO, final DynRealmDAO dynRealmDAO, + final AnyFinder anyFinder, final AnyUtils anyUtils, final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient) { @@ -128,6 +123,7 @@ protected AbstractAnyRepoExt( this.derSchemaDAO = derSchemaDAO; this.virSchemaDAO = virSchemaDAO; this.dynRealmDAO = dynRealmDAO; + this.anyFinder = anyFinder; this.anyUtils = anyUtils; } @@ -174,173 +170,9 @@ public A authFind(final String key) { return any; } - @Override - @SuppressWarnings("unchecked") - public List findByPlainAttrValue( - final PlainSchema schema, - final PlainAttrValue attrValue, - final boolean ignoreCaseMatch) { - - if (schema == null) { - LOG.error("No PlainSchema"); - return List.of(); - } - - PlainAttr attr = anyUtils.newPlainAttr(); - attr.setSchema(schema); - if (attrValue instanceof PlainAttrUniqueValue plainAttrUniqueValue) { - attr.setUniqueValue(plainAttrUniqueValue); - } else { - attr.add(attrValue); - } - - String op; - Map parameters; - if (ignoreCaseMatch) { - op = "=~"; - parameters = Map.of("value", "(?i)" + AnyRepoExt.escapeForLikeRegex(POJOHelper.serialize(attr))); - } else { - op = "="; - parameters = Map.of("value", POJOHelper.serialize(attr)); - } - return toList( - neo4jClient.query( - "MATCH (n:" + AnyRepoExt.node(anyUtils.anyTypeKind()) + ") " - + "WHERE n.`plainAttrs." + schema.getKey() + "` " + op + " $value RETURN n.id"). - bindAll(parameters).fetch().all(), - "n.id", - anyUtils.anyClass(), - cache()); - } - - @Override - public Optional findByPlainAttrUniqueValue( - final PlainSchema schema, - final PlainAttrUniqueValue attrUniqueValue, - final boolean ignoreCaseMatch) { - - if (schema == null) { - LOG.error("No PlainSchema"); - return Optional.empty(); - } - if (!schema.isUniqueConstraint()) { - LOG.error("This schema has not unique constraint: '{}'", schema.getKey()); - return Optional.empty(); - } - - List result = findByPlainAttrValue(schema, attrUniqueValue, ignoreCaseMatch); - return result.isEmpty() - ? Optional.empty() - : Optional.of(result.get(0)); - } - @Override public List findByDerAttrValue(final DerSchema derSchema, final String value, final boolean ignoreCaseMatch) { - if (derSchema == null) { - LOG.error("No DerSchema"); - return List.of(); - } - - Parser parser = new Parser(derSchema.getExpression()); - - // Schema keys - List identifiers = new ArrayList<>(); - - // Literals - List literals = new ArrayList<>(); - - // Get schema keys and literals - for (Token token = parser.getNextToken(); token != null && StringUtils.isNotBlank(token.toString()); - token = parser.getNextToken()) { - - if (token.kind == ParserConstants.STRING_LITERAL) { - literals.add(token.toString().substring(1, token.toString().length() - 1)); - } - - if (token.kind == ParserConstants.IDENTIFIER) { - identifiers.add(token.toString()); - } - } - - // Sort literals in order to process later literals included into others - literals.sort((l1, l2) -> { - if (l1 == null && l2 == null) { - return 0; - } else if (l1 != null && l2 == null) { - return -1; - } else if (l1 == null) { - return 1; - } else if (l1.length() == l2.length()) { - return 0; - } else if (l1.length() > l2.length()) { - return -1; - } else { - return 1; - } - }); - - // Split value on provided literals - List attrValues = split(value, literals); - - if (attrValues.size() != identifiers.size()) { - LOG.error("Ambiguous JEXL expression resolution: literals and values have different size"); - return List.of(); - } - - // Contains used identifiers in order to avoid replications - Set used = new HashSet<>(); - - // Create several clauses: one for eanch identifiers - List clauses = new ArrayList<>(); - Map parameters = new HashMap<>(); - for (int i = 0; i < identifiers.size(); i++) { - if (!used.contains(identifiers.get(i))) { - // verify schema existence and get schema type - PlainSchema schema = plainSchemaDAO.findById(identifiers.get(i)).orElse(null); - - if (schema == null) { - LOG.error("Invalid schema '{}', ignoring", identifiers.get(i)); - } else { - PlainAttr attr = anyUtils.newPlainAttr(); - attr.setSchema(schema); - if (schema.isUniqueConstraint()) { - PlainAttrUniqueValue attrValue = anyUtils.newPlainAttrUniqueValue(); - attrValue.setStringValue(attrValues.get(i)); - attr.setUniqueValue(attrValue); - } else { - PlainAttrValue attrValue = anyUtils.newPlainAttrValue(); - attrValue.setStringValue(attrValues.get(i)); - attr.add(attrValue); - } - - String op; - if (ignoreCaseMatch) { - op = "=~"; - parameters.put( - identifiers.get(i), - "(?i)" + AnyRepoExt.escapeForLikeRegex(POJOHelper.serialize(attr))); - } else { - op = "="; - parameters.put(identifiers.get(i), POJOHelper.serialize(attr)); - } - clauses.add("n.`plainAttrs." + schema.getKey() + "` " + op + " $" + identifiers.get(i)); - - used.add(identifiers.get(i)); - } - } - } - - LOG.debug("Generated where clauses {}", clauses); - - return toList( - neo4jClient.query( - "MATCH (n:" + AnyRepoExt.node(anyUtils.anyTypeKind()) + ") " - + "WHERE " + clauses.stream().collect(Collectors.joining(" AND ")) - + " RETURN n.id"). - bindAll(parameters).fetch().all(), - "n.id", - anyUtils.anyClass(), - cache()); + return anyFinder.findByDerAttrValue(anyUtils.anyTypeKind(), derSchema, value, ignoreCaseMatch); } @Transactional(propagation = Propagation.REQUIRES_NEW, readOnly = true) @@ -462,7 +294,8 @@ public List findByResourcesContaining(final ExternalResource resource) { protected void checkBeforeSave(final A any) { // check UNIQUE constraints any.getPlainAttrs().stream().filter(attr -> attr.getUniqueValue() != null).forEach(attr -> { - Optional other = findByPlainAttrUniqueValue(attr.getSchema(), attr.getUniqueValue(), false); + Optional other = anyFinder.findByPlainAttrUniqueValue( + anyUtils.anyTypeKind(), attr.getSchema(), attr.getUniqueValue()); if (other.isEmpty() || other.get().getKey().equals(any.getKey())) { LOG.debug("No duplicate value found for {}={}", attr.getSchema().getKey(), attr.getUniqueValue().getValueAsString()); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExtImpl.java index 55060cf293..b1bcfc7aa3 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExtImpl.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyObjectRepoExtImpl.java @@ -51,6 +51,7 @@ import org.apache.syncope.core.persistence.api.entity.group.Group; import org.apache.syncope.core.persistence.api.entity.user.URelationship; import org.apache.syncope.core.persistence.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.common.dao.AnyFinder; import org.apache.syncope.core.persistence.neo4j.entity.EntityCacheKey; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; @@ -89,6 +90,7 @@ public AnyObjectRepoExtImpl( final DynRealmDAO dynRealmDAO, final UserDAO userDAO, final GroupDAO groupDAO, + final AnyFinder anyFinder, final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient, final NodeValidator nodeValidator, @@ -101,6 +103,7 @@ public AnyObjectRepoExtImpl( derSchemaDAO, virSchemaDAO, dynRealmDAO, + anyFinder, anyUtilsFactory.getInstance(AnyTypeKind.ANY_OBJECT), neo4jTemplate, neo4jClient); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyRepoExt.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyRepoExt.java index 8aa602c186..b8a0dfcffc 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyRepoExt.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/AnyRepoExt.java @@ -27,9 +27,6 @@ import org.apache.syncope.core.persistence.api.entity.Any; import org.apache.syncope.core.persistence.api.entity.DerSchema; import org.apache.syncope.core.persistence.api.entity.ExternalResource; -import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; -import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.Schema; import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAMembership; import org.apache.syncope.core.persistence.neo4j.entity.anyobject.Neo4jAnyObject; @@ -79,11 +76,6 @@ static String membNode(final AnyTypeKind anyTypeKind) { A authFind(String key); - List findByPlainAttrValue(PlainSchema schema, PlainAttrValue attrValue, boolean ignoreCaseMatch); - - Optional findByPlainAttrUniqueValue( - PlainSchema schema, PlainAttrUniqueValue attrUniqueValue, boolean ignoreCaseMatch); - List findByDerAttrValue(DerSchema schema, String value, boolean ignoreCaseMatch); AllowedSchemas findAllowedSchemas(A any, Class reference); diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java index 2c1a4789a1..8a748a00d3 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/GroupRepoExtImpl.java @@ -60,6 +60,7 @@ import org.apache.syncope.core.persistence.api.search.SearchCondConverter; import org.apache.syncope.core.persistence.api.search.SearchCondVisitor; import org.apache.syncope.core.persistence.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.common.dao.AnyFinder; import org.apache.syncope.core.persistence.neo4j.entity.EntityCacheKey; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyType; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; @@ -114,7 +115,8 @@ public GroupRepoExtImpl( final AnyMatchDAO anyMatchDAO, final UserDAO userDAO, final AnyObjectDAO anyObjectDAO, - final AnySearchDAO searchDAO, + final AnySearchDAO anySearchDAO, + final AnyFinder anyFinder, final SearchCondVisitor searchCondVisitor, final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient, @@ -128,6 +130,7 @@ public GroupRepoExtImpl( derSchemaDAO, virSchemaDAO, dynRealmDAO, + anyFinder, anyUtilsFactory.getInstance(AnyTypeKind.GROUP), neo4jTemplate, neo4jClient); @@ -135,7 +138,7 @@ public GroupRepoExtImpl( this.anyMatchDAO = anyMatchDAO; this.userDAO = userDAO; this.anyObjectDAO = anyObjectDAO; - this.anySearchDAO = searchDAO; + this.anySearchDAO = anySearchDAO; this.searchCondVisitor = searchCondVisitor; this.nodeValidator = nodeValidator; this.groupCache = groupCache; diff --git a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java index 38366fa2f8..96379b9c8e 100644 --- a/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java +++ b/core/persistence-neo4j/src/main/java/org/apache/syncope/core/persistence/neo4j/dao/repo/UserRepoExtImpl.java @@ -53,6 +53,7 @@ import org.apache.syncope.core.persistence.api.entity.user.URelationship; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.api.utils.RealmUtils; +import org.apache.syncope.core.persistence.common.dao.AnyFinder; import org.apache.syncope.core.persistence.neo4j.entity.EntityCacheKey; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jAnyTypeClass; import org.apache.syncope.core.persistence.neo4j.entity.Neo4jExternalResource; @@ -105,6 +106,7 @@ public UserRepoExtImpl( final GroupDAO groupDAO, final DelegationDAO delegationDAO, final FIQLQueryDAO fiqlQueryDAO, + final AnyFinder anyFinder, final SecurityProperties securityProperties, final Neo4jTemplate neo4jTemplate, final Neo4jClient neo4jClient, @@ -118,6 +120,7 @@ public UserRepoExtImpl( derSchemaDAO, virSchemaDAO, dynRealmDAO, + anyFinder, anyUtilsFactory.getInstance(AnyTypeKind.USER), neo4jTemplate, neo4jClient); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyMatchTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyMatchTest.java index 8a85d0aef6..0e25257862 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyMatchTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnyMatchTest.java @@ -62,10 +62,10 @@ public void byResourceCond() { ResourceCond resourceCond = new ResourceCond(); resourceCond.setResource("resource-testdb2"); - assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(resourceCond))); + assertTrue(anyMatcher.matches(user, SearchCond.of(resourceCond))); resourceCond.setResource("ws-target-resource-delete"); - assertFalse(anyMatcher.matches(user, SearchCond.getLeaf(resourceCond))); + assertFalse(anyMatcher.matches(user, SearchCond.of(resourceCond))); } @Test @@ -74,11 +74,11 @@ public void anyObjectMatch() { RelationshipCond relationshipCond = new RelationshipCond(); relationshipCond.setAnyObject("8559d14d-58c2-46eb-a2d4-a7d35161e8f8"); - assertTrue(anyMatcher.matches(anyObject, SearchCond.getLeaf(relationshipCond))); + assertTrue(anyMatcher.matches(anyObject, SearchCond.of(relationshipCond))); RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond(); relationshipTypeCond.setRelationshipTypeKey("neighborhood"); - assertTrue(anyMatcher.matches(anyObject, SearchCond.getLeaf(relationshipTypeCond))); + assertTrue(anyMatcher.matches(anyObject, SearchCond.of(relationshipTypeCond))); } @Test @@ -87,24 +87,24 @@ public void userMatch() { MembershipCond groupCond = new MembershipCond(); groupCond.setGroup("secretary"); - assertFalse(anyMatcher.matches(user, SearchCond.getLeaf(groupCond))); + assertFalse(anyMatcher.matches(user, SearchCond.of(groupCond))); groupCond.setGroup("root"); - assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(groupCond))); + assertTrue(anyMatcher.matches(user, SearchCond.of(groupCond))); RoleCond roleCond = new RoleCond(); roleCond.setRole("Other"); - assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(roleCond))); + assertTrue(anyMatcher.matches(user, SearchCond.of(roleCond))); user = userDAO.findById("c9b2dec2-00a7-4855-97c0-d854842b4b24").orElseThrow(); RelationshipCond relationshipCond = new RelationshipCond(); relationshipCond.setAnyObject("fc6dbc3a-6c07-4965-8781-921e7401a4a5"); - assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(relationshipCond))); + assertTrue(anyMatcher.matches(user, SearchCond.of(relationshipCond))); RelationshipTypeCond relationshipTypeCond = new RelationshipTypeCond(); relationshipTypeCond.setRelationshipTypeKey("neighborhood"); - assertTrue(anyMatcher.matches(user, SearchCond.getLeaf(relationshipTypeCond))); + assertTrue(anyMatcher.matches(user, SearchCond.of(relationshipTypeCond))); } @Test @@ -115,11 +115,11 @@ public void groupMatch() { anyCond.setSchema("name"); anyCond.setExpression("root"); anyCond.setType(AttrCond.Type.EQ); - assertTrue(anyMatcher.matches(group, SearchCond.getLeaf(anyCond))); + assertTrue(anyMatcher.matches(group, SearchCond.of(anyCond))); AttrCond attrCond = new AttrCond(); attrCond.setSchema("show"); attrCond.setType(AttrCond.Type.ISNOTNULL); - assertTrue(anyMatcher.matches(group, SearchCond.getLeaf(attrCond))); + assertTrue(anyMatcher.matches(group, SearchCond.of(attrCond))); } } diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnySearchTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnySearchTest.java index 735d54cf33..4e79c75c32 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnySearchTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/AnySearchTest.java @@ -130,8 +130,8 @@ public void orOfThree() { cond3.setSchema("username"); cond3.setExpression("notfound"); - SearchCond cond = SearchCond.getOr(List.of( - SearchCond.getLeaf(cond1), SearchCond.getLeaf(cond2), SearchCond.getLeaf(cond3))); + SearchCond cond = SearchCond.or(List.of( + SearchCond.of(cond1), SearchCond.of(cond2), SearchCond.of(cond3))); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); @@ -141,6 +141,61 @@ public void orOfThree() { assertTrue(users.stream().anyMatch(u -> "puccini".equals(u.getUsername()))); } + @Test + public void searchTwoPlainSchemas() { + AttrCond firstnameCond = new AttrCond(AttrCond.Type.EQ); + firstnameCond.setSchema("firstname"); + firstnameCond.setExpression("Gioacchino"); + + AttrCond surnameCond = new AttrCond(AttrCond.Type.EQ); + surnameCond.setSchema("surname"); + surnameCond.setExpression("Rossini"); + + SearchCond cond = SearchCond.and(SearchCond.of(firstnameCond), SearchCond.of(surnameCond)); + assertTrue(cond.isValid()); + + List users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + + surnameCond = new AttrCond(AttrCond.Type.EQ); + surnameCond.setSchema("surname"); + surnameCond.setExpression("Verdi"); + + cond = SearchCond.and(SearchCond.of(firstnameCond), SearchCond.negate(surnameCond)); + assertTrue(cond.isValid()); + + users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + + AttrCond fullnameCond = new AttrCond(AttrCond.Type.EQ); + fullnameCond.setSchema("fullname"); + fullnameCond.setExpression("Vincenzo Bellini"); + + AttrCond userIdCond = new AttrCond(AttrCond.Type.EQ); + userIdCond.setSchema("userId"); + userIdCond.setExpression("bellini@apache.org"); + + cond = SearchCond.and(SearchCond.of(fullnameCond), SearchCond.of(userIdCond)); + assertTrue(cond.isValid()); + + users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + + userIdCond = new AttrCond(AttrCond.Type.EQ); + userIdCond.setSchema("userId"); + userIdCond.setExpression("rossini@apache.org"); + + cond = SearchCond.and(SearchCond.of(fullnameCond), SearchCond.negate(userIdCond)); + assertTrue(cond.isValid()); + + users = searchDAO.search(cond, AnyTypeKind.USER); + assertNotNull(users); + assertEquals(1, users.size()); + } + @Test public void searchWithLikeCondition() { AttrCond fullnameLeafCond = new AttrCond(AttrCond.Type.LIKE); @@ -154,12 +209,12 @@ public void searchWithLikeCondition() { loginDateCond.setSchema("loginDate"); loginDateCond.setExpression(LOGIN_DATE_VALUE); - SearchCond subCond = SearchCond.getAnd( - SearchCond.getLeaf(fullnameLeafCond), SearchCond.getLeaf(groupCond)); + SearchCond subCond = SearchCond.and( + SearchCond.of(fullnameLeafCond), SearchCond.of(groupCond)); assertTrue(subCond.isValid()); - SearchCond cond = SearchCond.getAnd(subCond, SearchCond.getLeaf(loginDateCond)); + SearchCond cond = SearchCond.and(subCond, SearchCond.of(loginDateCond)); assertTrue(cond.isValid()); @@ -181,12 +236,12 @@ public void searchCaseInsensitiveWithLikeCondition() { loginDateCond.setSchema("loginDate"); loginDateCond.setExpression(LOGIN_DATE_VALUE); - SearchCond subCond = SearchCond.getAnd( - SearchCond.getLeaf(fullnameLeafCond), SearchCond.getLeaf(groupCond)); + SearchCond subCond = SearchCond.and( + SearchCond.of(fullnameLeafCond), SearchCond.of(groupCond)); assertTrue(subCond.isValid()); - SearchCond cond = SearchCond.getAnd(subCond, SearchCond.getLeaf(loginDateCond)); + SearchCond cond = SearchCond.and(subCond, SearchCond.of(loginDateCond)); assertTrue(cond.isValid()); @@ -201,7 +256,7 @@ public void searchWithNotAttrCond() { fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("Giuseppe Verdi"); - SearchCond cond = SearchCond.getNotLeaf(fullnameLeafCond); + SearchCond cond = SearchCond.negate(fullnameLeafCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); @@ -219,7 +274,7 @@ public void searchWithNotAnyCond() { usernameLeafCond.setSchema("username"); usernameLeafCond.setExpression("verdi"); - SearchCond cond = SearchCond.getNotLeaf(usernameLeafCond); + SearchCond cond = SearchCond.negate(usernameLeafCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); @@ -235,7 +290,7 @@ public void searchCaseInsensitiveWithNotCondition() { fullnameLeafCond.setSchema("fullname"); fullnameLeafCond.setExpression("giuseppe verdi"); - SearchCond cond = SearchCond.getNotLeaf(fullnameLeafCond); + SearchCond cond = SearchCond.negate(fullnameLeafCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); @@ -253,7 +308,7 @@ public void searchByBoolean() { coolLeafCond.setSchema("cool"); coolLeafCond.setExpression("true"); - SearchCond cond = SearchCond.getLeaf(coolLeafCond); + SearchCond cond = SearchCond.of(coolLeafCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); @@ -269,13 +324,13 @@ public void searchByRealm() { anyCond.setSchema("realm"); anyCond.setExpression("c5b75db1-fce7-470f-b780-3b9934d82a9d"); - List users = searchDAO.search(SearchCond.getLeaf(anyCond), AnyTypeKind.USER); + List users = searchDAO.search(SearchCond.of(anyCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); assertEquals("rossini", users.get(0).getUsername()); anyCond.setExpression("/even"); - users = searchDAO.search(SearchCond.getLeaf(anyCond), AnyTypeKind.USER); + users = searchDAO.search(SearchCond.of(anyCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); assertEquals("rossini", users.get(0).getUsername()); @@ -294,12 +349,12 @@ public void searchByPageAndSize() { loginDateCond.setSchema("loginDate"); loginDateCond.setExpression(LOGIN_DATE_VALUE); - SearchCond subCond = SearchCond.getAnd( - SearchCond.getLeaf(fullnameLeafCond), SearchCond.getLeaf(groupCond)); + SearchCond subCond = SearchCond.and( + SearchCond.of(fullnameLeafCond), SearchCond.of(groupCond)); assertTrue(subCond.isValid()); - SearchCond cond = SearchCond.getAnd(subCond, SearchCond.getLeaf(loginDateCond)); + SearchCond cond = SearchCond.and(subCond, SearchCond.of(loginDateCond)); assertTrue(cond.isValid()); @@ -325,13 +380,13 @@ public void searchByGroup() { MembershipCond groupCond = new MembershipCond(); groupCond.setGroup("child"); - List matchingChild = searchDAO.search(SearchCond.getLeaf(groupCond), AnyTypeKind.USER); + List matchingChild = searchDAO.search(SearchCond.of(groupCond), AnyTypeKind.USER); assertNotNull(matchingChild); assertTrue(matchingChild.stream().anyMatch(user -> "verdi".equals(user.getUsername()))); groupCond.setGroup("otherchild"); - List matchingOtherChild = searchDAO.search(SearchCond.getLeaf(groupCond), AnyTypeKind.USER); + List matchingOtherChild = searchDAO.search(SearchCond.of(groupCond), AnyTypeKind.USER); assertNotNull(matchingOtherChild); assertTrue(matchingOtherChild.stream().anyMatch(user -> "rossini".equals(user.getUsername()))); @@ -342,14 +397,14 @@ public void searchByGroup() { groupCond.setGroup("%child"); - List matchingStar = searchDAO.search(SearchCond.getLeaf(groupCond), AnyTypeKind.USER); + List matchingStar = searchDAO.search(SearchCond.of(groupCond), AnyTypeKind.USER); assertNotNull(matchingStar); assertTrue(matchingStar.stream().anyMatch(user -> "verdi".equals(user.getUsername()))); assertTrue(matchingStar.stream().anyMatch(user -> "rossini".equals(user.getUsername()))); assertEquals(union, matchingStar.stream().map(User::getUsername).collect(Collectors.toSet())); matchingStar = searchDAO.search(realmDAO.getRoot(), false, SyncopeConstants.FULL_ADMIN_REALMS, - SearchCond.getLeaf(groupCond), Pageable.unpaged(), AnyTypeKind.USER); + SearchCond.of(groupCond), Pageable.unpaged(), AnyTypeKind.USER); assertNotNull(matchingStar); assertTrue(matchingStar.stream().anyMatch(user -> "verdi".equals(user.getUsername()))); assertTrue(matchingStar.stream().noneMatch(user -> "rossini".equals(user.getUsername()))); @@ -360,7 +415,7 @@ public void searchByRole() { RoleCond roleCond = new RoleCond(); roleCond.setRole("Other"); - List users = searchDAO.search(SearchCond.getLeaf(roleCond), AnyTypeKind.USER); + List users = searchDAO.search(SearchCond.of(roleCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); } @@ -370,7 +425,7 @@ public void searchByPrivilege() { PrivilegeCond privilegeCond = new PrivilegeCond(); privilegeCond.setPrivilege("postMighty"); - List users = searchDAO.search(SearchCond.getLeaf(privilegeCond), AnyTypeKind.USER); + List users = searchDAO.search(SearchCond.of(privilegeCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); } @@ -380,14 +435,14 @@ public void searchByIsNull() { AttrCond coolLeafCond = new AttrCond(AttrCond.Type.ISNULL); coolLeafCond.setSchema("cool"); - List users = searchDAO.search(SearchCond.getLeaf(coolLeafCond), AnyTypeKind.USER); + List users = searchDAO.search(SearchCond.of(coolLeafCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(4, users.size()); coolLeafCond = new AttrCond(AttrCond.Type.ISNOTNULL); coolLeafCond.setSchema("cool"); - users = searchDAO.search(SearchCond.getLeaf(coolLeafCond), AnyTypeKind.USER); + users = searchDAO.search(SearchCond.of(coolLeafCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); } @@ -397,7 +452,7 @@ public void searchByAuxClass() { AuxClassCond ac = new AuxClassCond(); ac.setAuxClass("csv"); - List groups = searchDAO.search(SearchCond.getLeaf(ac), AnyTypeKind.GROUP); + List groups = searchDAO.search(SearchCond.of(ac), AnyTypeKind.GROUP); assertNotNull(groups); assertEquals(2, groups.size()); } @@ -410,7 +465,7 @@ public void searchByResource() { ResourceCond ws1 = new ResourceCond(); ws1.setResource("ws-target-resource-list-mappings-2"); - SearchCond searchCondition = SearchCond.getAnd(SearchCond.getNotLeaf(ws2), SearchCond.getLeaf(ws1)); + SearchCond searchCondition = SearchCond.and(SearchCond.negate(ws2), SearchCond.of(ws1)); assertTrue(searchCondition.isValid()); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); @@ -424,7 +479,7 @@ public void searchByBooleanAttrCond() { booleanCond.setSchema("show"); booleanCond.setExpression("true"); - List matchingGroups = searchDAO.search(SearchCond.getLeaf(booleanCond), AnyTypeKind.GROUP); + List matchingGroups = searchDAO.search(SearchCond.of(booleanCond), AnyTypeKind.GROUP); assertNotNull(matchingGroups); assertFalse(matchingGroups.isEmpty()); } @@ -439,9 +494,9 @@ public void searchByUsernameAndKey() { idRightCond.setSchema("key"); idRightCond.setExpression("2"); - SearchCond searchCondition = SearchCond.getAnd( - SearchCond.getLeaf(usernameLeafCond), - SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.and( + SearchCond.of(usernameLeafCond), + SearchCond.of(idRightCond)); List matching = searchDAO.search(searchCondition, AnyTypeKind.USER); assertNotNull(matching); @@ -460,9 +515,9 @@ public void searchByGroupNameAndKey() { idRightCond.setSchema("key"); idRightCond.setExpression("37d15e4c-cdc1-460b-a591-8505c8133806"); - SearchCond searchCondition = SearchCond.getAnd( - SearchCond.getLeaf(groupNameLeafCond), - SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.and( + SearchCond.of(groupNameLeafCond), + SearchCond.of(idRightCond)); assertTrue(searchCondition.isValid()); @@ -483,9 +538,9 @@ public void searchByUsernameAndFullname() { idRightCond.setSchema("fullname"); idRightCond.setExpression("Giuseppe V%"); - SearchCond searchCondition = SearchCond.getOr( - SearchCond.getLeaf(usernameLeafCond), - SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.or( + SearchCond.of(usernameLeafCond), + SearchCond.of(idRightCond)); List matchingUsers = searchDAO.search(searchCondition, AnyTypeKind.USER); assertNotNull(matchingUsers); @@ -502,9 +557,9 @@ public void searchByUsernameAndFullnameIgnoreCase() { idRightCond.setSchema("fullname"); idRightCond.setExpression("gIuseppe v%"); - SearchCond searchCondition = SearchCond.getOr( - SearchCond.getLeaf(usernameLeafCond), - SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.or( + SearchCond.of(usernameLeafCond), + SearchCond.of(idRightCond)); List matchingUsers = searchDAO.search(searchCondition, AnyTypeKind.USER); assertNotNull(matchingUsers); @@ -517,7 +572,7 @@ public void searchByKey() { idLeafCond.setSchema("key"); idLeafCond.setExpression("74cd8ece-715a-44a4-a736-e17b46c4e7e6"); - SearchCond searchCondition = SearchCond.getLeaf(idLeafCond); + SearchCond searchCondition = SearchCond.of(idLeafCond); assertTrue(searchCondition.isValid()); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); @@ -531,7 +586,7 @@ public void searchByType() { AnyTypeCond tcond = new AnyTypeCond(); tcond.setAnyTypeKey("PRINTER"); - SearchCond searchCondition = SearchCond.getLeaf(tcond); + SearchCond searchCondition = SearchCond.of(tcond); assertTrue(searchCondition.isValid()); List printers = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); @@ -553,8 +608,8 @@ public void searchByRelationshipType() { AnyTypeCond tcond = new AnyTypeCond(); tcond.setAnyTypeKey("PRINTER"); - SearchCond searchCondition = SearchCond.getAnd( - SearchCond.getLeaf(relationshipTypeCond), SearchCond.getLeaf(tcond)); + SearchCond searchCondition = SearchCond.and( + SearchCond.of(relationshipTypeCond), SearchCond.of(tcond)); assertTrue(searchCondition.isValid()); List anyObjects = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); @@ -564,7 +619,7 @@ public void searchByRelationshipType() { assertTrue(anyObjects.stream().anyMatch(any -> "8559d14d-58c2-46eb-a2d4-a7d35161e8f8".equals(any.getKey()))); // 2. search for users involved in "neighborhood" relationship - searchCondition = SearchCond.getLeaf(relationshipTypeCond); + searchCondition = SearchCond.of(relationshipTypeCond); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); @@ -577,7 +632,7 @@ public void searchByAnyCondDate() { creationDateCond.setSchema("creationDate"); creationDateCond.setExpression("2021-04-15 12:45:00"); - SearchCond searchCondition = SearchCond.getLeaf(creationDateCond); + SearchCond searchCondition = SearchCond.of(creationDateCond); assertTrue(searchCondition.isValid()); List anyObjects = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); @@ -592,7 +647,7 @@ public void searchByAttrCondDate() { loginDateCond.setSchema("loginDate"); loginDateCond.setExpression("2009-05-27"); - SearchCond searchCondition = SearchCond.getLeaf(loginDateCond); + SearchCond searchCondition = SearchCond.of(loginDateCond); assertTrue(searchCondition.isValid()); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); @@ -609,8 +664,8 @@ public void userOrderBy() { AttrCond idRightCond = new AttrCond(AttrCond.Type.LIKE); idRightCond.setSchema("fullname"); idRightCond.setExpression("Giuseppe V%"); - SearchCond searchCondition = SearchCond.getOr( - SearchCond.getLeaf(usernameLeafCond), SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.or( + SearchCond.of(usernameLeafCond), SearchCond.of(idRightCond)); List orderByClauses = new ArrayList<>(); orderByClauses.add(new Sort.Order(Sort.Direction.DESC, "username")); @@ -631,7 +686,7 @@ public void groupOrderBy() { AnyCond idLeafCond = new AnyCond(AnyCond.Type.LIKE); idLeafCond.setSchema("name"); idLeafCond.setExpression("%r"); - SearchCond searchCondition = SearchCond.getLeaf(idLeafCond); + SearchCond searchCondition = SearchCond.of(idLeafCond); assertTrue(searchCondition.isValid()); Sort.Order orderByClause = new Sort.Order(Sort.DEFAULT_DIRECTION, "name"); @@ -649,7 +704,7 @@ public void groupOrderBy() { public void member() { MemberCond memberCond = new MemberCond(); memberCond.setMember("1417acbe-cbf6-4277-9372-e75e04f97000"); - SearchCond searchCondition = SearchCond.getLeaf(memberCond); + SearchCond searchCondition = SearchCond.of(memberCond); assertTrue(searchCondition.isValid()); List groups = searchDAO.search(searchCondition, AnyTypeKind.GROUP); @@ -720,7 +775,7 @@ public void changePwdDate() { AnyCond changePwdDateCond = new AnyCond(AttrCond.Type.ISNULL); changePwdDateCond.setSchema("changePwdDate"); - SearchCond cond = SearchCond.getAnd(SearchCond.getNotLeaf(statusCond), SearchCond.getLeaf(changePwdDateCond)); + SearchCond cond = SearchCond.and(SearchCond.negate(statusCond), SearchCond.of(changePwdDateCond)); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); @@ -737,7 +792,7 @@ public void issue202() { ws1.setResource("ws-target-resource-list-mappings-1"); SearchCond searchCondition = - SearchCond.getAnd(SearchCond.getNotLeaf(ws2), SearchCond.getNotLeaf(ws1)); + SearchCond.and(SearchCond.negate(ws2), SearchCond.negate(ws1)); assertTrue(searchCondition.isValid()); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); @@ -752,7 +807,7 @@ public void issue242() { cond.setSchema("key"); cond.setExpression("test%"); - SearchCond searchCondition = SearchCond.getLeaf(cond); + SearchCond searchCondition = SearchCond.of(cond); assertTrue(searchCondition.isValid()); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); @@ -766,7 +821,7 @@ public void issueSYNCOPE46() { cond.setSchema("username"); cond.setExpression("%ossin%"); - SearchCond searchCondition = SearchCond.getLeaf(cond); + SearchCond searchCondition = SearchCond.of(cond); assertTrue(searchCondition.isValid()); List users = searchDAO.search(searchCondition, AnyTypeKind.USER); @@ -783,8 +838,8 @@ public void issueSYNCOPE433() { likeCond.setSchema("username"); likeCond.setExpression("%ossin%"); - SearchCond searchCond = SearchCond.getOr( - SearchCond.getLeaf(isNullCond), SearchCond.getLeaf(likeCond)); + SearchCond searchCond = SearchCond.or( + SearchCond.of(isNullCond), SearchCond.of(likeCond)); long count = searchDAO.count( realmDAO.getRoot(), true, SyncopeConstants.FULL_ADMIN_REALMS, searchCond, AnyTypeKind.USER); @@ -801,13 +856,13 @@ public void issueSYNCOPE929() { genderCond.setSchema("gender"); genderCond.setExpression("M"); - SearchCond orCond = SearchCond.getOr(SearchCond.getLeaf(rossiniCond), SearchCond.getLeaf(genderCond)); + SearchCond orCond = SearchCond.or(SearchCond.of(rossiniCond), SearchCond.of(genderCond)); AttrCond belliniCond = new AttrCond(AttrCond.Type.EQ); belliniCond.setSchema("surname"); belliniCond.setExpression("Bellini"); - SearchCond searchCond = SearchCond.getAnd(orCond, SearchCond.getLeaf(belliniCond)); + SearchCond searchCond = SearchCond.and(orCond, SearchCond.of(belliniCond)); List users = searchDAO.search(searchCond, AnyTypeKind.USER); assertNotNull(users); @@ -846,7 +901,7 @@ public void issueSYNCOPE980() { MembershipCond groupCond = new MembershipCond(); groupCond.setGroup("citizen"); - SearchCond searchCondition = SearchCond.getLeaf(groupCond); + SearchCond searchCondition = SearchCond.of(groupCond); List matching = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); assertEquals(2, matching.size()); @@ -854,7 +909,7 @@ public void issueSYNCOPE980() { AnyTypeCond anyTypeCond = new AnyTypeCond(); anyTypeCond.setAnyTypeKey(service.getKey()); - searchCondition = SearchCond.getAnd(SearchCond.getLeaf(groupCond), SearchCond.getLeaf(anyTypeCond)); + searchCondition = SearchCond.and(SearchCond.of(groupCond), SearchCond.of(anyTypeCond)); matching = searchDAO.search(searchCondition, AnyTypeKind.ANY_OBJECT); assertEquals(1, matching.size()); @@ -874,7 +929,7 @@ public void issueSYNCOPE983() { realmDAO.getRoot(), true, SyncopeConstants.FULL_ADMIN_REALMS, - SearchCond.getLeaf(fullnameLeafCond), + SearchCond.of(fullnameLeafCond), Pageable.unpaged(Sort.by(orderByClauses)), AnyTypeKind.USER); assertFalse(users.isEmpty()); @@ -888,7 +943,7 @@ public void issueSYNCOPE1416() { AttrCond idRightCond = new AttrCond(AttrCond.Type.ISNOTNULL); idRightCond.setSchema("firstname"); - SearchCond searchCondition = SearchCond.getAnd(SearchCond.getLeaf(idLeftCond), SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.and(SearchCond.of(idLeftCond), SearchCond.of(idRightCond)); List orderByClauses = List.of(new Sort.Order(Sort.Direction.ASC, "ctype")); @@ -901,7 +956,7 @@ public void issueSYNCOPE1416() { AttrCond fullnameCond = new AttrCond(AttrCond.Type.ISNOTNULL); fullnameCond.setSchema("fullname"); - SearchCond cond = SearchCond.getLeaf(fullnameCond); + SearchCond cond = SearchCond.of(fullnameCond); assertTrue(cond.isValid()); users = searchDAO.search(cond, AnyTypeKind.USER); @@ -910,7 +965,7 @@ public void issueSYNCOPE1416() { fullnameCond = new AttrCond(AttrCond.Type.ISNULL); fullnameCond.setSchema("fullname"); - cond = SearchCond.getLeaf(fullnameCond); + cond = SearchCond.of(fullnameCond); assertTrue(cond.isValid()); users = searchDAO.search(cond, AnyTypeKind.USER); @@ -923,7 +978,7 @@ public void issueSYNCOPE1419() { loginDateCond.setSchema("loginDate"); loginDateCond.setExpression(LOGIN_DATE_VALUE); - SearchCond cond = SearchCond.getNotLeaf(loginDateCond); + SearchCond cond = SearchCond.negate(loginDateCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/UserTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/UserTest.java index 6653097058..4ffd32334d 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/UserTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/inner/UserTest.java @@ -27,7 +27,6 @@ import java.time.OffsetDateTime; import java.util.List; -import java.util.Optional; import org.apache.syncope.common.lib.types.CipherAlgorithm; import org.apache.syncope.core.persistence.api.dao.DerSchemaDAO; import org.apache.syncope.core.persistence.api.dao.ExternalResourceDAO; @@ -36,10 +35,7 @@ import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO; import org.apache.syncope.core.persistence.api.dao.SecurityQuestionDAO; import org.apache.syncope.core.persistence.api.dao.UserDAO; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.user.UMembership; -import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrUniqueValue; -import org.apache.syncope.core.persistence.api.entity.user.UPlainAttrValue; import org.apache.syncope.core.persistence.api.entity.user.User; import org.apache.syncope.core.persistence.neo4j.AbstractTest; import org.apache.syncope.core.spring.security.Encryptor; @@ -141,35 +137,6 @@ public void findByInvalidDerAttrExpression() { derSchemaDAO.findById("noschema").orElseThrow(), "Antonio, Maria", false).isEmpty()); } - @Test - public void findByPlainAttrUniqueValue() { - UPlainAttrUniqueValue fullnameValue = entityFactory.newEntity(UPlainAttrUniqueValue.class); - fullnameValue.setStringValue("Gioacchino Rossini"); - - PlainSchema fullname = plainSchemaDAO.findById("fullname").orElseThrow(); - - Optional found = userDAO.findByPlainAttrUniqueValue(fullname, fullnameValue, false); - assertTrue(found.isPresent()); - - fullnameValue.setStringValue("Gioacchino ROSSINI"); - - found = userDAO.findByPlainAttrUniqueValue(fullname, fullnameValue, false); - assertFalse(found.isPresent()); - - found = userDAO.findByPlainAttrUniqueValue(fullname, fullnameValue, true); - assertTrue(found.isPresent()); - } - - @Test - public void findByPlainAttrBooleanValue() { - UPlainAttrValue coolValue = entityFactory.newEntity(UPlainAttrValue.class); - coolValue.setBooleanValue(true); - - List list = userDAO.findByPlainAttrValue( - plainSchemaDAO.findById("cool").orElseThrow(), coolValue, false); - assertEquals(1, list.size()); - } - @Test public void findByKey() { assertTrue(userDAO.findById("1417acbe-cbf6-4277-9372-e75e04f97000").isPresent()); diff --git a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnySearchTest.java b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnySearchTest.java index 94fbff1e75..9cfed09ebe 100644 --- a/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnySearchTest.java +++ b/core/persistence-neo4j/src/test/java/org/apache/syncope/core/persistence/neo4j/outer/AnySearchTest.java @@ -110,7 +110,7 @@ public void searchByDynMembership() { RoleCond roleCond = new RoleCond(); roleCond.setRole(role.getKey()); - List users = searchDAO.search(SearchCond.getLeaf(roleCond), AnyTypeKind.USER); + List users = searchDAO.search(SearchCond.of(roleCond), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); assertEquals("c9b2dec2-00a7-4855-97c0-d854842b4b24", users.get(0).getKey()); @@ -138,7 +138,7 @@ public void searchAsGroupOwner() { List users = searchDAO.search( realmDAO.getRoot(), true, Set.of(SyncopeConstants.ROOT_REALM), - SearchCond.getLeaf(anyCond), PageRequest.of(0, 100), AnyTypeKind.USER); + SearchCond.of(anyCond), PageRequest.of(0, 100), AnyTypeKind.USER); assertNotNull(users); assertTrue(users.stream().anyMatch(user -> rossini.getKey().equals(user.getKey()))); @@ -146,7 +146,7 @@ public void searchAsGroupOwner() { users = searchDAO.search( group.getRealm(), true, Set.of(RealmUtils.getGroupOwnerRealm(group.getRealm().getFullPath(), group.getKey())), - SearchCond.getLeaf(anyCond), PageRequest.of(0, 100), AnyTypeKind.USER); + SearchCond.of(anyCond), PageRequest.of(0, 100), AnyTypeKind.USER); assertNotNull(users); assertEquals(1, users.size()); assertEquals(rossini.getKey(), users.get(0).getKey()); @@ -160,7 +160,7 @@ public void searchByMembershipAttribute() { AttrCond attrCond = new AttrCond(AttrCond.Type.EQ); attrCond.setSchema("ctype"); attrCond.setExpression("otherchildctype"); - SearchCond cond = SearchCond.getAnd(SearchCond.getLeaf(typeCond), SearchCond.getLeaf(attrCond)); + SearchCond cond = SearchCond.and(SearchCond.of(typeCond), SearchCond.of(attrCond)); long count = searchDAO.count( realmSearchDAO.findByFullPath(SyncopeConstants.ROOT_REALM).orElseThrow(), @@ -209,7 +209,7 @@ public void issueSYNCOPE95() { coolLeafCond.setSchema("cool"); coolLeafCond.setExpression("true"); - SearchCond cond = SearchCond.getLeaf(coolLeafCond); + SearchCond cond = SearchCond.of(coolLeafCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); @@ -227,8 +227,8 @@ public void issueSYNCOPE1417() { AttrCond idRightCond = new AttrCond(AttrCond.Type.LIKE); idRightCond.setSchema("fullname"); idRightCond.setExpression("Giuseppe V%"); - SearchCond searchCondition = SearchCond.getOr( - SearchCond.getLeaf(usernameLeafCond), SearchCond.getLeaf(idRightCond)); + SearchCond searchCondition = SearchCond.or( + SearchCond.of(usernameLeafCond), SearchCond.of(idRightCond)); List orderByClauses = new ArrayList<>(); orderByClauses.add(new Sort.Order(Sort.Direction.DESC, "surname")); @@ -266,7 +266,7 @@ public void issueSYNCOPE1512() { titleCond.setSchema("title"); titleCond.setExpression("syncope's group"); - List matching = searchDAO.search(SearchCond.getLeaf(titleCond), AnyTypeKind.GROUP); + List matching = searchDAO.search(SearchCond.of(titleCond), AnyTypeKind.GROUP); assertEquals(1, matching.size()); assertEquals(group.getKey(), matching.get(0).getKey()); @@ -274,7 +274,7 @@ public void issueSYNCOPE1512() { originalNameCond.setSchema("originalName"); originalNameCond.setExpression("syncope's group"); - matching = searchDAO.search(SearchCond.getLeaf(originalNameCond), AnyTypeKind.GROUP); + matching = searchDAO.search(SearchCond.of(originalNameCond), AnyTypeKind.GROUP); assertEquals(1, matching.size()); assertEquals(group.getKey(), matching.get(0).getKey()); } @@ -286,7 +286,7 @@ public void issueSYNCOPE1790() { emailCond.setSchema("email"); emailCond.setExpression("verdi@syncope.org"); - SearchCond cond = SearchCond.getLeaf(emailCond); + SearchCond cond = SearchCond.of(emailCond); assertTrue(cond.isValid()); List users = searchDAO.search(cond, AnyTypeKind.USER); 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 272b2bdf12..d8b4b6f5e1 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 @@ -94,11 +94,11 @@ public void misc() { DynRealmCond dynRealmCond = new DynRealmCond(); dynRealmCond.setDynRealm(actual.getKey()); - List matching = searchDAO.search(SearchCond.getLeaf(dynRealmCond), AnyTypeKind.USER); + List matching = searchDAO.search(SearchCond.of(dynRealmCond), AnyTypeKind.USER); assertNotNull(matching); assertFalse(matching.isEmpty()); User user = matching.get(0); - assertTrue(anyMatchDAO.matches(user, SearchCond.getLeaf(dynRealmCond))); + assertTrue(anyMatchDAO.matches(user, SearchCond.of(dynRealmCond))); assertTrue(userDAO.findDynRealms(user.getKey()).contains(actual.getKey())); } @@ -135,11 +135,11 @@ public void issueSYNCOPE1806() { // 2. verify that dynamic members are the same DynRealmCond dynRealmCond1 = new DynRealmCond(); dynRealmCond1.setDynRealm(realm1.getKey()); - List matching1 = searchDAO.search(SearchCond.getLeaf(dynRealmCond1), AnyTypeKind.USER); + List matching1 = searchDAO.search(SearchCond.of(dynRealmCond1), AnyTypeKind.USER); DynRealmCond dynRealmCond2 = new DynRealmCond(); dynRealmCond2.setDynRealm(realm2.getKey()); - List matching2 = searchDAO.search(SearchCond.getLeaf(dynRealmCond2), AnyTypeKind.USER); + List matching2 = searchDAO.search(SearchCond.of(dynRealmCond2), AnyTypeKind.USER); assertEquals(matching1, matching2); assertEquals(1, matching1.size()); @@ -153,8 +153,8 @@ public void issueSYNCOPE1806() { "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); + matching1 = searchDAO.search(SearchCond.of(dynRealmCond1), AnyTypeKind.USER); + matching2 = searchDAO.search(SearchCond.of(dynRealmCond2), AnyTypeKind.USER); assertEquals(matching1, matching2); assertEquals(2, matching1.size()); assertTrue(matching1.stream().anyMatch(u -> "c9b2dec2-00a7-4855-97c0-d854842b4b24".equals(u.getKey()))); diff --git a/core/persistence-neo4j/src/test/resources/simplelogger.properties b/core/persistence-neo4j/src/test/resources/simplelogger.properties index 973e0096ff..257ee927eb 100644 --- a/core/persistence-neo4j/src/test/resources/simplelogger.properties +++ b/core/persistence-neo4j/src/test/resources/simplelogger.properties @@ -17,5 +17,7 @@ # See http://www.slf4j.org/api/org/slf4j/impl/SimpleLogger.html # Possible values: "trace", "debug", "info", "warn", or "error" +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=YYYY-mm-dd HH:mm:ss.SSS org.slf4j.simpleLogger.defaultLogLevel=debug diff --git a/core/provisioning-api/src/test/resources/simplelogger.properties b/core/provisioning-api/src/test/resources/simplelogger.properties index 973e0096ff..030d4d091c 100644 --- a/core/provisioning-api/src/test/resources/simplelogger.properties +++ b/core/provisioning-api/src/test/resources/simplelogger.properties @@ -17,5 +17,6 @@ # See http://www.slf4j.org/api/org/slf4j/impl/SimpleLogger.html # Possible values: "trace", "debug", "info", "warn", or "error" +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=YYYY-mm-dd HH:mm:ss.SSS org.slf4j.simpleLogger.defaultLogLevel=debug - diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java index 2d3dc57590..1207d072a3 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/GroupDataBinderImpl.java @@ -452,10 +452,11 @@ protected static void populateTransitiveResources( PropagationByResource propByRes = new PropagationByResource<>(); group.getResources().forEach(resource -> { // exclude from propagation those objects that have that resource assigned by some other membership(s) - if (!any.getResources().contains(resource) && any.getMemberships().stream() - .filter(otherGrpMemb -> !otherGrpMemb.getRightEnd().equals(group)) - .noneMatch(otherGrpMemb -> otherGrpMemb.getRightEnd().getResources().stream() - .anyMatch(r -> resource.getKey().equals(r.getKey())))) { + if (!any.getResources().contains(resource) + && any.getMemberships().stream(). + filter(m -> !m.getRightEnd().equals(group)). + noneMatch(m -> m.getRightEnd().getResources().contains(resource))) { + propByRes.add(ResourceOperation.DELETE, resource.getKey()); } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java index 6db78bcc0d..173f9fe7f0 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/GroupMemberProvisionTaskJobDelegate.java @@ -91,7 +91,7 @@ protected String doExecute(final JobExecutionContext context) { MembershipCond membershipCond = new MembershipCond(); membershipCond.setGroup(groupKey); - List users = searchDAO.search(SearchCond.getLeaf(membershipCond), AnyTypeKind.USER); + List users = searchDAO.search(SearchCond.of(membershipCond), AnyTypeKind.USER); Collection gResources = groupDAO.findAllResourceKeys(groupKey); setStatus("About to " + (action == ProvisionAction.DEPROVISION ? "de" : "") + "provision " @@ -117,7 +117,7 @@ protected String doExecute(final JobExecutionContext context) { membershipCond = new MembershipCond(); membershipCond.setGroup(groupKey); - List anyObjects = searchDAO.search(SearchCond.getLeaf(membershipCond), AnyTypeKind.ANY_OBJECT); + List anyObjects = searchDAO.search(SearchCond.of(membershipCond), AnyTypeKind.ANY_OBJECT); setStatus("About to " + (action == ProvisionAction.DEPROVISION ? "de" : "") + "provision " + anyObjects.size() + " any objects from " + gResources); diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultInboundCorrelationRule.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultInboundCorrelationRule.java index 9d06215d4b..2a24594d20 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultInboundCorrelationRule.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultInboundCorrelationRule.java @@ -89,11 +89,11 @@ public SearchCond getSearchCond(final LiveSyncDelta syncDelta, final Provision p cond.setType(type); cond.setExpression(expression); - searchConds.add(SearchCond.getLeaf(cond)); + searchConds.add(SearchCond.of(cond)); }); return conf.isOrSchemas() - ? SearchCond.getOr(searchConds) - : SearchCond.getAnd(searchConds); + ? SearchCond.or(searchConds) + : SearchCond.and(searchConds); } } diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java index a95426e8f8..5510823ce6 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/DefaultRealmPullResultHandler.java @@ -571,7 +571,7 @@ protected OpEvent.Outcome delete(final SyncDelta delta, final List realms Set adminRealms = Set.of(realm.getFullPath()); AnyCond keyCond = new AnyCond(AttrCond.Type.ISNOTNULL); keyCond.setSchema("key"); - SearchCond allMatchingCond = SearchCond.getLeaf(keyCond); + SearchCond allMatchingCond = SearchCond.of(keyCond); long users = searchDAO.count( realmDAO.getRoot(), true, adminRealms, allMatchingCond, AnyTypeKind.USER); long groups = searchDAO.count( diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java index d115862a36..faf4ce71f3 100644 --- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java +++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/InboundMatcher.java @@ -32,7 +32,6 @@ import org.apache.syncope.common.lib.to.Provision; import org.apache.syncope.common.lib.types.AnyTypeKind; import org.apache.syncope.common.lib.types.MatchType; -import org.apache.syncope.core.persistence.api.attrvalue.ParsingValidationException; import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO; import org.apache.syncope.core.persistence.api.dao.AnySearchDAO; import org.apache.syncope.core.persistence.api.dao.GroupDAO; @@ -51,9 +50,6 @@ import org.apache.syncope.core.persistence.api.entity.DerSchema; import org.apache.syncope.core.persistence.api.entity.ExternalResource; import org.apache.syncope.core.persistence.api.entity.Implementation; -import org.apache.syncope.core.persistence.api.entity.PlainAttrUniqueValue; -import org.apache.syncope.core.persistence.api.entity.PlainAttrValue; -import org.apache.syncope.core.persistence.api.entity.PlainSchema; import org.apache.syncope.core.persistence.api.entity.Realm; import org.apache.syncope.core.persistence.api.entity.VirSchema; import org.apache.syncope.core.persistence.api.entity.policy.InboundCorrelationRuleEntity; @@ -77,7 +73,6 @@ import org.identityconnectors.framework.common.objects.SyncDeltaBuilder; import org.identityconnectors.framework.common.objects.SyncDeltaType; import org.identityconnectors.framework.common.objects.SyncToken; -import org.identityconnectors.framework.common.objects.Uid; import org.identityconnectors.framework.common.objects.filter.FilterBuilder; import org.identityconnectors.framework.spi.SearchResultsHandler; import org.slf4j.Logger; @@ -272,7 +267,7 @@ public List matchByConnObjectKeyValue( AnyCond cond = new AnyCond(AttrCond.Type.IEQ); cond.setSchema("username"); cond.setExpression(finalConnObjectKeyValue); - anys.addAll(anySearchDAO.search(SearchCond.getLeaf(cond), AnyTypeKind.USER)); + anys.addAll(anySearchDAO.search(SearchCond.of(cond), AnyTypeKind.USER)); } else { userDAO.findByUsername(finalConnObjectKeyValue).ifPresent(anys::add); } @@ -283,7 +278,7 @@ public List matchByConnObjectKeyValue( AnyCond cond = new AnyCond(AttrCond.Type.IEQ); cond.setSchema("name"); cond.setExpression(finalConnObjectKeyValue); - anys.addAll(anySearchDAO.search(SearchCond.getLeaf(cond), AnyTypeKind.GROUP)); + anys.addAll(anySearchDAO.search(SearchCond.of(cond), AnyTypeKind.GROUP)); } else { groupDAO.findByName(finalConnObjectKeyValue).ifPresent(anys::add); } @@ -292,7 +287,7 @@ public List matchByConnObjectKeyValue( AnyCond cond = new AnyCond(AttrCond.Type.IEQ); cond.setSchema("name"); cond.setExpression(finalConnObjectKeyValue); - anys.addAll(anySearchDAO.search(SearchCond.getLeaf(cond), AnyTypeKind.ANY_OBJECT)); + anys.addAll(anySearchDAO.search(SearchCond.of(cond), AnyTypeKind.ANY_OBJECT)); } else { anys.addAll(anyObjectDAO.findByName(finalConnObjectKeyValue)); } @@ -304,29 +299,15 @@ public List matchByConnObjectKeyValue( } else if (intAttrName.getSchemaType() != null) { switch (intAttrName.getSchemaType()) { case PLAIN -> { - PlainAttrValue value = intAttrName.getSchema().isUniqueConstraint() - ? anyUtils.newPlainAttrUniqueValue() - : anyUtils.newPlainAttrValue(); - try { - value.parseValue((PlainSchema) intAttrName.getSchema(), finalConnObjectKeyValue); - } catch (ParsingValidationException e) { - LOG.error("While parsing provided {} {}", Uid.NAME, value, e); - value.setStringValue(finalConnObjectKeyValue); - } - - if (intAttrName.getSchema().isUniqueConstraint()) { - anyUtils.dao().findByPlainAttrUniqueValue((PlainSchema) intAttrName.getSchema(), - (PlainAttrUniqueValue) value, ignoreCaseMatch). - ifPresent(anys::add); - } else { - anys.addAll(anyUtils.dao().findByPlainAttrValue((PlainSchema) intAttrName.getSchema(), - value, ignoreCaseMatch)); - } + AttrCond attrCond = new AttrCond(ignoreCaseMatch ? AttrCond.Type.IEQ : AttrCond.Type.EQ); + attrCond.setSchema(intAttrName.getSchema().getKey()); + attrCond.setExpression(finalConnObjectKeyValue); + anys.addAll(anySearchDAO.search(SearchCond.of(attrCond), anyTypeKind)); } case DERIVED -> - anys.addAll(anyUtils.dao().findByDerAttrValue((DerSchema) intAttrName.getSchema(), - finalConnObjectKeyValue, ignoreCaseMatch)); + anys.addAll(anyUtils.dao().findByDerAttrValue( + (DerSchema) intAttrName.getSchema(), finalConnObjectKeyValue, ignoreCaseMatch)); default -> { } diff --git a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java index 6f0b7b7375..901c778708 100644 --- a/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java +++ b/core/spring/src/main/java/org/apache/syncope/core/spring/security/AuthDataAccessor.java @@ -200,7 +200,7 @@ public Triple authenticate(final String domain, final Aut attrCond.setSchema(authAttrValues[i]); attrCond.setExpression(authentication.getName()); try { - List users = anySearchDAO.search(SearchCond.getLeaf(attrCond), AnyTypeKind.USER); + List users = anySearchDAO.search(SearchCond.of(attrCond), AnyTypeKind.USER); if (users.size() == 1) { user = users.get(0); } else { diff --git a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAO.java b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAO.java index bd8e1ff76a..dd76f04bbd 100644 --- a/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAO.java +++ b/ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAO.java @@ -302,74 +302,74 @@ protected Query getQuery(final SearchCond cond, final AnyTypeKind kind) { switch (cond.getType()) { case LEAF: case NOT_LEAF: - query = cond.getLeaf(AnyTypeCond.class). + query = cond.asLeaf(AnyTypeCond.class). filter(leaf -> AnyTypeKind.ANY_OBJECT == kind). map(this::getQuery). orElse(null); if (query == null) { - query = cond.getLeaf(RelationshipTypeCond.class). + query = cond.asLeaf(RelationshipTypeCond.class). filter(leaf -> AnyTypeKind.GROUP != kind). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(RelationshipCond.class). + query = cond.asLeaf(RelationshipCond.class). filter(leaf -> AnyTypeKind.GROUP != kind). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(MembershipCond.class). + query = cond.asLeaf(MembershipCond.class). filter(leaf -> AnyTypeKind.GROUP != kind). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(MemberCond.class). + query = cond.asLeaf(MemberCond.class). filter(leaf -> AnyTypeKind.GROUP == kind). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(RoleCond.class). + query = cond.asLeaf(RoleCond.class). filter(leaf -> AnyTypeKind.USER == kind). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(PrivilegeCond.class). + query = cond.asLeaf(PrivilegeCond.class). filter(leaf -> AnyTypeKind.USER == kind). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(DynRealmCond.class). + query = cond.asLeaf(DynRealmCond.class). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(AuxClassCond.class). + query = cond.asLeaf(AuxClassCond.class). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(ResourceCond.class). + query = cond.asLeaf(ResourceCond.class). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(AnyCond.class).map(ac -> getQuery(ac, kind)). - or(() -> cond.getLeaf(AttrCond.class).map(ac -> getQuery(ac, kind))). + query = cond.asLeaf(AnyCond.class).map(ac -> getQuery(ac, kind)). + or(() -> cond.asLeaf(AttrCond.class).map(ac -> getQuery(ac, kind))). orElse(null); } diff --git a/ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAOTest.java b/ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAOTest.java index 4fc5a7fbbe..5dfa37e9b7 100644 --- a/ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAOTest.java +++ b/ext/elasticsearch/persistence/src/test/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchAnySearchDAOTest.java @@ -190,7 +190,7 @@ public void searchRequest4groupOwner() throws IOException { index(ElasticsearchUtils.getAnyIndex(AuthContextUtils.getDomain(), AnyTypeKind.USER)). searchType(SearchType.QueryThenFetch). query(searchDAO.getQuery(mock(Realm.class), true, - adminRealms, SearchCond.getLeaf(anyCond), AnyTypeKind.USER)). + adminRealms, SearchCond.of(anyCond), AnyTypeKind.USER)). from(1). size(10). build(); @@ -250,14 +250,13 @@ public void issueSYNCOPE1725() throws IOException { utils.when(() -> ElasticsearchUtils.getAnyIndex( SyncopeConstants.MASTER_DOMAIN, AnyTypeKind.USER)).thenReturn("master_user"); - Query query = searchDAO.getQuery( - SearchCond.getAnd( - List.of(SearchCond.getLeaf(cond1), - SearchCond.getLeaf(cond2), - SearchCond.getLeaf(cond3), - SearchCond.getLeaf(cond4), - SearchCond.getLeaf(cond5), - SearchCond.getLeaf(cond6))), + Query query = searchDAO.getQuery(SearchCond.and( + SearchCond.of(cond1), + SearchCond.of(cond2), + SearchCond.of(cond3), + SearchCond.of(cond4), + SearchCond.of(cond5), + SearchCond.of(cond6)), AnyTypeKind.USER); assertEquals(Query.Kind.Bool, query._kind()); assertEquals(6, ((BoolQuery) query._get()).must().size()); @@ -278,14 +277,13 @@ public void issueSYNCOPE1725() throws IOException { build()).build()). usingRecursiveComparison().isEqualTo(query); - query = searchDAO.getQuery( - SearchCond.getOr( - List.of(SearchCond.getLeaf(cond1), - SearchCond.getLeaf(cond2), - SearchCond.getLeaf(cond3), - SearchCond.getLeaf(cond4), - SearchCond.getLeaf(cond5), - SearchCond.getLeaf(cond6))), + query = searchDAO.getQuery(SearchCond.or( + SearchCond.of(cond1), + SearchCond.of(cond2), + SearchCond.of(cond3), + SearchCond.of(cond4), + SearchCond.of(cond5), + SearchCond.of(cond6)), AnyTypeKind.USER); assertEquals(Query.Kind.DisMax, query._kind()); assertEquals(6, ((DisMaxQuery) query._get()).queries().size()); @@ -306,16 +304,15 @@ public void issueSYNCOPE1725() throws IOException { build()).build()). usingRecursiveComparison().isEqualTo(query); - query = searchDAO.getQuery( - SearchCond.getAnd(List.of( - SearchCond.getOr(List.of( - SearchCond.getLeaf(cond1), - SearchCond.getLeaf(cond2), - SearchCond.getLeaf(cond3))), - SearchCond.getOr(List.of( - SearchCond.getLeaf(cond4), - SearchCond.getLeaf(cond5), - SearchCond.getLeaf(cond6))))), + query = searchDAO.getQuery(SearchCond.and( + SearchCond.or( + SearchCond.of(cond1), + SearchCond.of(cond2), + SearchCond.of(cond3)), + SearchCond.or( + SearchCond.of(cond4), + SearchCond.of(cond5), + SearchCond.of(cond6))), AnyTypeKind.USER); assertEquals(Query.Kind.Bool, query._kind()); assertEquals(2, ((BoolQuery) query._get()).must().size()); @@ -346,16 +343,15 @@ public void issueSYNCOPE1725() throws IOException { build()).build()). usingRecursiveComparison().isEqualTo(query); - query = searchDAO.getQuery( - SearchCond.getOr(List.of( - SearchCond.getAnd(List.of( - SearchCond.getLeaf(cond1), - SearchCond.getLeaf(cond2), - SearchCond.getLeaf(cond3))), - SearchCond.getAnd(List.of( - SearchCond.getLeaf(cond4), - SearchCond.getLeaf(cond5), - SearchCond.getLeaf(cond6))))), + query = searchDAO.getQuery(SearchCond.or( + SearchCond.and( + SearchCond.of(cond1), + SearchCond.of(cond2), + SearchCond.of(cond3)), + SearchCond.and( + SearchCond.of(cond4), + SearchCond.of(cond5), + SearchCond.of(cond6))), AnyTypeKind.USER); assertEquals(Query.Kind.DisMax, query._kind()); assertEquals(2, ((DisMaxQuery) query._get()).queries().size()); diff --git a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAO.java b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAO.java index 37b93ea42a..600dd0dc48 100644 --- a/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAO.java +++ b/ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAO.java @@ -300,74 +300,74 @@ protected Query getQuery(final SearchCond cond, final AnyTypeKind kind) { switch (cond.getType()) { case LEAF: case NOT_LEAF: - query = cond.getLeaf(AnyTypeCond.class). + query = cond.asLeaf(AnyTypeCond.class). filter(leaf -> AnyTypeKind.ANY_OBJECT == kind). map(this::getQuery). orElse(null); if (query == null) { - query = cond.getLeaf(RelationshipTypeCond.class). + query = cond.asLeaf(RelationshipTypeCond.class). filter(leaf -> AnyTypeKind.GROUP != kind). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(RelationshipCond.class). + query = cond.asLeaf(RelationshipCond.class). filter(leaf -> AnyTypeKind.GROUP != kind). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(MembershipCond.class). + query = cond.asLeaf(MembershipCond.class). filter(leaf -> AnyTypeKind.GROUP != kind). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(MemberCond.class). + query = cond.asLeaf(MemberCond.class). filter(leaf -> AnyTypeKind.GROUP == kind). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(RoleCond.class). + query = cond.asLeaf(RoleCond.class). filter(leaf -> AnyTypeKind.USER == kind). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(PrivilegeCond.class). + query = cond.asLeaf(PrivilegeCond.class). filter(leaf -> AnyTypeKind.USER == kind). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(DynRealmCond.class). + query = cond.asLeaf(DynRealmCond.class). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(AuxClassCond.class). + query = cond.asLeaf(AuxClassCond.class). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(ResourceCond.class). + query = cond.asLeaf(ResourceCond.class). map(this::getQuery). orElse(null); } if (query == null) { - query = cond.getLeaf(AnyCond.class).map(ac -> getQuery(ac, kind)). - or(() -> cond.getLeaf(AttrCond.class).map(ac -> getQuery(ac, kind))). + query = cond.asLeaf(AnyCond.class).map(ac -> getQuery(ac, kind)). + or(() -> cond.asLeaf(AttrCond.class).map(ac -> getQuery(ac, kind))). orElse(null); } diff --git a/ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAOTest.java b/ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAOTest.java index 2afeb424ea..802d3a1764 100644 --- a/ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAOTest.java +++ b/ext/opensearch/persistence/src/test/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchAnySearchDAOTest.java @@ -191,7 +191,7 @@ public void searchRequest4groupOwner() throws IOException { index(OpenSearchUtils.getAnyIndex(AuthContextUtils.getDomain(), AnyTypeKind.USER)). searchType(SearchType.QueryThenFetch). query(searchDAO.getQuery(mock(Realm.class), true, - adminRealms, SearchCond.getLeaf(anyCond), AnyTypeKind.USER)). + adminRealms, SearchCond.of(anyCond), AnyTypeKind.USER)). from(1). size(10). build(); @@ -251,14 +251,13 @@ public void issueSYNCOPE1725() throws IOException { utils.when(() -> OpenSearchUtils.getAnyIndex( SyncopeConstants.MASTER_DOMAIN, AnyTypeKind.USER)).thenReturn("master_user"); - Query query = searchDAO.getQuery( - SearchCond.getAnd( - List.of(SearchCond.getLeaf(cond1), - SearchCond.getLeaf(cond2), - SearchCond.getLeaf(cond3), - SearchCond.getLeaf(cond4), - SearchCond.getLeaf(cond5), - SearchCond.getLeaf(cond6))), + Query query = searchDAO.getQuery(SearchCond.and( + SearchCond.of(cond1), + SearchCond.of(cond2), + SearchCond.of(cond3), + SearchCond.of(cond4), + SearchCond.of(cond5), + SearchCond.of(cond6)), AnyTypeKind.USER); assertEquals(Query.Kind.Bool, query._kind()); assertEquals(6, ((BoolQuery) query._get()).must().size()); @@ -279,14 +278,13 @@ public void issueSYNCOPE1725() throws IOException { build()).build()). usingRecursiveComparison().isEqualTo(query); - query = searchDAO.getQuery( - SearchCond.getOr( - List.of(SearchCond.getLeaf(cond1), - SearchCond.getLeaf(cond2), - SearchCond.getLeaf(cond3), - SearchCond.getLeaf(cond4), - SearchCond.getLeaf(cond5), - SearchCond.getLeaf(cond6))), + query = searchDAO.getQuery(SearchCond.or( + SearchCond.of(cond1), + SearchCond.of(cond2), + SearchCond.of(cond3), + SearchCond.of(cond4), + SearchCond.of(cond5), + SearchCond.of(cond6)), AnyTypeKind.USER); assertEquals(Query.Kind.DisMax, query._kind()); assertEquals(6, ((DisMaxQuery) query._get()).queries().size()); @@ -307,16 +305,15 @@ public void issueSYNCOPE1725() throws IOException { build()).build()). usingRecursiveComparison().isEqualTo(query); - query = searchDAO.getQuery( - SearchCond.getAnd(List.of( - SearchCond.getOr(List.of( - SearchCond.getLeaf(cond1), - SearchCond.getLeaf(cond2), - SearchCond.getLeaf(cond3))), - SearchCond.getOr(List.of( - SearchCond.getLeaf(cond4), - SearchCond.getLeaf(cond5), - SearchCond.getLeaf(cond6))))), + query = searchDAO.getQuery(SearchCond.and( + SearchCond.or( + SearchCond.of(cond1), + SearchCond.of(cond2), + SearchCond.of(cond3)), + SearchCond.or( + SearchCond.of(cond4), + SearchCond.of(cond5), + SearchCond.of(cond6))), AnyTypeKind.USER); assertEquals(Query.Kind.Bool, query._kind()); assertEquals(2, ((BoolQuery) query._get()).must().size()); @@ -353,16 +350,15 @@ public void issueSYNCOPE1725() throws IOException { build()).build()). usingRecursiveComparison().isEqualTo(query); - query = searchDAO.getQuery( - SearchCond.getOr(List.of( - SearchCond.getAnd(List.of( - SearchCond.getLeaf(cond1), - SearchCond.getLeaf(cond2), - SearchCond.getLeaf(cond3))), - SearchCond.getAnd(List.of( - SearchCond.getLeaf(cond4), - SearchCond.getLeaf(cond5), - SearchCond.getLeaf(cond6))))), + query = searchDAO.getQuery(SearchCond.or( + SearchCond.and( + SearchCond.of(cond1), + SearchCond.of(cond2), + SearchCond.of(cond3)), + SearchCond.and( + SearchCond.of(cond4), + SearchCond.of(cond5), + SearchCond.of(cond6))), AnyTypeKind.USER); assertEquals(Query.Kind.DisMax, query._kind()); assertEquals(2, ((DisMaxQuery) query._get()).queries().size()); diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java index 3cc60b644e..c4fff76aa0 100644 --- a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java +++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMDataBinder.java @@ -1011,7 +1011,7 @@ public SCIMGroup toSCIMGroup( MembershipCond membCond = new MembershipCond(); membCond.setGroup(groupTO.getKey()); - SearchCond searchCond = SearchCond.getLeaf(membCond); + SearchCond searchCond = SearchCond.of(membCond); if (output(attributes, excludedAttributes, "members")) { long count = userLogic.search( diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java index fba72c8bef..933b461ace 100644 --- a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java +++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/scim/SearchCondVisitor.java @@ -92,8 +92,8 @@ private static SearchCond setOperator(final AttrCond attrCond, final String oper } return "ne".equals(operator) - ? SearchCond.getNotLeaf(attrCond) - : SearchCond.getLeaf(attrCond); + ? SearchCond.negate(attrCond) + : SearchCond.of(attrCond); } private static > SearchCond complex( @@ -106,7 +106,7 @@ private static > SearchCond complex( AttrCond attrCond = new AttrCond(); attrCond.setSchema(item.get().getValue()); attrCond.setType(AttrCond.Type.ISNOTNULL); - return SearchCond.getLeaf(attrCond); + return SearchCond.of(attrCond); } } else if (MULTIVALUE.contains(left) || left.endsWith(".value")) { List orConds = items.stream(). @@ -118,7 +118,7 @@ private static > SearchCond complex( return setOperator(cond, operator); }).collect(Collectors.toList()); if (!orConds.isEmpty()) { - return SearchCond.getOr(orConds); + return SearchCond.or(orConds); } } @@ -135,7 +135,7 @@ private static SearchCond addresses( AttrCond attrCond = new AttrCond(); attrCond.setSchema(item.get().getFormatted()); attrCond.setType(AttrCond.Type.ISNOTNULL); - return SearchCond.getLeaf(attrCond); + return SearchCond.of(attrCond); } } else if (MULTIVALUE.contains(left) || left.endsWith(".value")) { List orConds = items.stream(). @@ -147,7 +147,7 @@ private static SearchCond addresses( return setOperator(cond, operator); }).collect(Collectors.toList()); if (!orConds.isEmpty()) { - return SearchCond.getOr(orConds); + return SearchCond.or(orConds); } } @@ -326,7 +326,7 @@ public SearchCond visitATTR_OPER_EXPR(final SCIMFilterParser.ATTR_OPER_EXPRConte public SearchCond visitATTR_PR(final SCIMFilterParser.ATTR_PRContext ctx) { AttrCond cond = createAttrCond(ctx.ATTRNAME().getText()); cond.setType(AttrCond.Type.ISNOTNULL); - return SearchCond.getLeaf(cond); + return SearchCond.of(cond); } @Override @@ -337,7 +337,7 @@ public SearchCond visitLPAREN_EXPR_RPAREN(final SCIMFilterParser.LPAREN_EXPR_RPA @Override public SearchCond visitNOT_EXPR(final SCIMFilterParser.NOT_EXPRContext ctx) { SearchCond cond = visit(ctx.expression()); - Optional anyCond = cond.getLeaf(AnyCond.class); + Optional anyCond = cond.asLeaf(AnyCond.class); if (anyCond.isPresent()) { if (anyCond.get().getType() == AttrCond.Type.ISNULL) { anyCond.get().setType(AttrCond.Type.ISNOTNULL); @@ -345,7 +345,7 @@ public SearchCond visitNOT_EXPR(final SCIMFilterParser.NOT_EXPRContext ctx) { anyCond.get().setType(AttrCond.Type.ISNULL); } } else { - Optional attrCond = cond.getLeaf(AttrCond.class); + Optional attrCond = cond.asLeaf(AttrCond.class); if (attrCond.isPresent()) { if (attrCond.get().getType() == AnyCond.Type.ISNULL) { attrCond.get().setType(AnyCond.Type.ISNOTNULL); @@ -353,7 +353,7 @@ public SearchCond visitNOT_EXPR(final SCIMFilterParser.NOT_EXPRContext ctx) { attrCond.get().setType(AnyCond.Type.ISNULL); } } else { - cond = SearchCond.getNotLeaf(cond); + cond = SearchCond.negate(cond); } } @@ -362,11 +362,11 @@ public SearchCond visitNOT_EXPR(final SCIMFilterParser.NOT_EXPRContext ctx) { @Override public SearchCond visitEXPR_AND_EXPR(final SCIMFilterParser.EXPR_AND_EXPRContext ctx) { - return SearchCond.getAnd(visit(ctx.expression(0)), visit(ctx.expression(1))); + return SearchCond.and(visit(ctx.expression(0)), visit(ctx.expression(1))); } @Override public SearchCond visitEXPR_OR_EXPR(final SCIMFilterParser.EXPR_OR_EXPRContext ctx) { - return SearchCond.getOr(visit(ctx.expression(0)), visit(ctx.expression(1))); + return SearchCond.or(visit(ctx.expression(0)), visit(ctx.expression(1))); } } diff --git a/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java b/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java index 5e62a41252..6787f0cfd1 100644 --- a/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java +++ b/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/scim/SCIMFilterTest.java @@ -79,18 +79,18 @@ public static void setup() { public void eq() { SearchCond cond = SearchCondConverter.convert(VISITOR, "userName eq \"bjensen\""); assertNotNull(cond); - assertEquals("username", cond.getLeaf(AnyCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.IEQ, cond.getLeaf(AnyCond.class).orElseThrow().getType()); - assertEquals("bjensen", cond.getLeaf(AnyCond.class).orElseThrow().getExpression()); + assertEquals("username", cond.asLeaf(AnyCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.IEQ, cond.asLeaf(AnyCond.class).orElseThrow().getType()); + assertEquals("bjensen", cond.asLeaf(AnyCond.class).orElseThrow().getExpression()); } @Test public void sw() { SearchCond cond = SearchCondConverter.convert(VISITOR, "userName sw \"J\""); assertNotNull(cond); - assertEquals("username", cond.getLeaf(AnyCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.ILIKE, cond.getLeaf(AnyCond.class).orElseThrow().getType()); - assertEquals("J%", cond.getLeaf(AnyCond.class).orElseThrow().getExpression()); + assertEquals("username", cond.asLeaf(AnyCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.ILIKE, cond.asLeaf(AnyCond.class).orElseThrow().getType()); + assertEquals("J%", cond.asLeaf(AnyCond.class).orElseThrow().getExpression()); SearchCond fqn = SearchCondConverter.convert( VISITOR, "urn:ietf:params:scim:schemas:core:2.0:User:userName sw \"J\""); @@ -101,27 +101,27 @@ public void sw() { public void pr() { SearchCond cond = SearchCondConverter.convert(VISITOR, "title pr"); assertNotNull(cond); - assertEquals("title", cond.getLeaf(AttrCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.ISNOTNULL, cond.getLeaf(AttrCond.class).orElseThrow().getType()); - assertNull(cond.getLeaf(AttrCond.class).orElseThrow().getExpression()); + assertEquals("title", cond.asLeaf(AttrCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.ISNOTNULL, cond.asLeaf(AttrCond.class).orElseThrow().getType()); + assertNull(cond.asLeaf(AttrCond.class).orElseThrow().getExpression()); } @Test public void gt() { SearchCond cond = SearchCondConverter.convert(VISITOR, "meta.lastModified gt \"2011-05-13T04:42:34Z\""); assertNotNull(cond); - assertEquals("lastChangeDate", cond.getLeaf(AnyCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.GT, cond.getLeaf(AnyCond.class).orElseThrow().getType()); - assertEquals("2011-05-13T04:42:34Z", cond.getLeaf(AnyCond.class).orElseThrow().getExpression()); + assertEquals("lastChangeDate", cond.asLeaf(AnyCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.GT, cond.asLeaf(AnyCond.class).orElseThrow().getType()); + assertEquals("2011-05-13T04:42:34Z", cond.asLeaf(AnyCond.class).orElseThrow().getExpression()); } @Test public void not() { SearchCond cond = SearchCondConverter.convert(VISITOR, "not (title pr)"); assertNotNull(cond); - assertEquals("title", cond.getLeaf(AttrCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.ISNULL, cond.getLeaf(AttrCond.class).orElseThrow().getType()); - assertNull(cond.getLeaf(AttrCond.class).orElseThrow().getExpression()); + assertEquals("title", cond.asLeaf(AttrCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.ISNULL, cond.asLeaf(AttrCond.class).orElseThrow().getType()); + assertNull(cond.asLeaf(AttrCond.class).orElseThrow().getExpression()); } @Test @@ -132,15 +132,15 @@ public void and() { SearchCond left = cond.getLeft(); assertNotNull(left); - assertEquals("title", left.getLeaf(AttrCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.ISNOTNULL, left.getLeaf(AttrCond.class).orElseThrow().getType()); - assertNull(left.getLeaf(AttrCond.class).orElseThrow().getExpression()); + assertEquals("title", left.asLeaf(AttrCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.ISNOTNULL, left.asLeaf(AttrCond.class).orElseThrow().getType()); + assertNull(left.asLeaf(AttrCond.class).orElseThrow().getExpression()); SearchCond right = cond.getRight(); assertNotNull(right); - assertEquals("username", right.getLeaf(AnyCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.ILIKE, right.getLeaf(AnyCond.class).orElseThrow().getType()); - assertEquals("J%", right.getLeaf(AnyCond.class).orElseThrow().getExpression()); + assertEquals("username", right.asLeaf(AnyCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.ILIKE, right.asLeaf(AnyCond.class).orElseThrow().getType()); + assertEquals("J%", right.asLeaf(AnyCond.class).orElseThrow().getExpression()); } @Test @@ -151,15 +151,15 @@ public void or() { SearchCond left = cond.getLeft(); assertNotNull(left); - assertEquals("title", left.getLeaf(AttrCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.ISNOTNULL, left.getLeaf(AttrCond.class).orElseThrow().getType()); - assertNull(left.getLeaf(AttrCond.class).orElseThrow().getExpression()); + assertEquals("title", left.asLeaf(AttrCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.ISNOTNULL, left.asLeaf(AttrCond.class).orElseThrow().getType()); + assertNull(left.asLeaf(AttrCond.class).orElseThrow().getExpression()); SearchCond right = cond.getRight(); assertNotNull(right); - assertEquals("cn", right.getLeaf(AttrCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.IEQ, right.getLeaf(AttrCond.class).orElseThrow().getType()); - assertEquals("Other", right.getLeaf(AttrCond.class).orElseThrow().getExpression()); + assertEquals("cn", right.asLeaf(AttrCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.IEQ, right.asLeaf(AttrCond.class).orElseThrow().getType()); + assertEquals("Other", right.asLeaf(AttrCond.class).orElseThrow().getExpression()); } @Test @@ -171,14 +171,14 @@ public void type() { SearchCond left = cond.getLeft(); assertNotNull(left); - assertEquals("userType", left.getLeaf(AttrCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.IEQ, left.getLeaf(AttrCond.class).orElseThrow().getType()); - assertEquals("Employee", left.getLeaf(AttrCond.class).orElseThrow().getExpression()); + assertEquals("userType", left.asLeaf(AttrCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.IEQ, left.asLeaf(AttrCond.class).orElseThrow().getType()); + assertEquals("Employee", left.asLeaf(AttrCond.class).orElseThrow().getExpression()); SearchCond right = cond.getRight(); assertNotNull(right); - assertEquals("email", right.getLeaf(AttrCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.ISNOTNULL, right.getLeaf(AttrCond.class).orElseThrow().getType()); + assertEquals("email", right.asLeaf(AttrCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.ISNOTNULL, right.asLeaf(AttrCond.class).orElseThrow().getType()); } @Test @@ -187,7 +187,7 @@ public void name() { assertNotNull(cond); assertEquals(SearchCond.Type.LEAF, cond.getType()); - AttrCond leaf = cond.getLeaf(AttrCond.class).orElseThrow(); + AttrCond leaf = cond.asLeaf(AttrCond.class).orElseThrow(); assertNotNull(leaf); assertEquals("surname", leaf.getSchema()); assertEquals(AttrCond.Type.ILIKE, leaf.getType()); @@ -207,15 +207,15 @@ public void emails() { SearchCond left1 = left.getLeft(); assertNotNull(left1); - assertEquals("email", left1.getLeaf(AttrCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.ILIKE, left1.getLeaf(AttrCond.class).orElseThrow().getType()); - assertEquals("%example.com%", left1.getLeaf(AttrCond.class).orElseThrow().getExpression()); + assertEquals("email", left1.asLeaf(AttrCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.ILIKE, left1.asLeaf(AttrCond.class).orElseThrow().getType()); + assertEquals("%example.com%", left1.asLeaf(AttrCond.class).orElseThrow().getExpression()); SearchCond left2 = left.getRight(); assertNotNull(left2); - assertEquals("gmail", left2.getLeaf(AttrCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.ILIKE, left2.getLeaf(AttrCond.class).orElseThrow().getType()); - assertEquals("%example.com%", left2.getLeaf(AttrCond.class).orElseThrow().getExpression()); + assertEquals("gmail", left2.asLeaf(AttrCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.ILIKE, left2.asLeaf(AttrCond.class).orElseThrow().getType()); + assertEquals("%example.com%", left2.asLeaf(AttrCond.class).orElseThrow().getExpression()); SearchCond right = cond.getRight(); assertNotNull(right); @@ -223,15 +223,15 @@ public void emails() { SearchCond right1 = right.getLeft(); assertNotNull(right1); - assertEquals("email", right1.getLeaf(AttrCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.ILIKE, right1.getLeaf(AttrCond.class).orElseThrow().getType()); - assertEquals("%example.org%", right1.getLeaf(AttrCond.class).orElseThrow().getExpression()); + assertEquals("email", right1.asLeaf(AttrCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.ILIKE, right1.asLeaf(AttrCond.class).orElseThrow().getType()); + assertEquals("%example.org%", right1.asLeaf(AttrCond.class).orElseThrow().getExpression()); SearchCond right2 = right.getRight(); assertNotNull(right2); - assertEquals("gmail", right2.getLeaf(AttrCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.ILIKE, right2.getLeaf(AttrCond.class).orElseThrow().getType()); - assertEquals("%example.org%", right2.getLeaf(AttrCond.class).orElseThrow().getExpression()); + assertEquals("gmail", right2.asLeaf(AttrCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.ILIKE, right2.asLeaf(AttrCond.class).orElseThrow().getType()); + assertEquals("%example.org%", right2.asLeaf(AttrCond.class).orElseThrow().getExpression()); } @Test @@ -239,9 +239,9 @@ public void enterpriseUserAttr() { SearchCond cond = SearchCondConverter.convert(VISITOR, "urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:organization eq \"The ASF\""); assertNotNull(cond); - assertEquals("org", cond.getLeaf(AttrCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.IEQ, cond.getLeaf(AttrCond.class).orElseThrow().getType()); - assertEquals("The ASF", cond.getLeaf(AttrCond.class).orElseThrow().getExpression()); + assertEquals("org", cond.asLeaf(AttrCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.IEQ, cond.asLeaf(AttrCond.class).orElseThrow().getType()); + assertEquals("The ASF", cond.asLeaf(AttrCond.class).orElseThrow().getExpression()); } @Test @@ -249,8 +249,8 @@ public void extensionUserAttr() { SearchCond cond = SearchCondConverter.convert(VISITOR, "urn:ietf:params:scim:schemas:extension:syncope:2.0:User:realm sw \"/\""); assertNotNull(cond); - assertEquals("realm", cond.getLeaf(AttrCond.class).orElseThrow().getSchema()); - assertEquals(AttrCond.Type.ILIKE, cond.getLeaf(AttrCond.class).orElseThrow().getType()); - assertEquals("/%", cond.getLeaf(AttrCond.class).orElseThrow().getExpression()); + assertEquals("realm", cond.asLeaf(AttrCond.class).orElseThrow().getSchema()); + assertEquals(AttrCond.Type.ILIKE, cond.asLeaf(AttrCond.class).orElseThrow().getType()); + assertEquals("/%", cond.asLeaf(AttrCond.class).orElseThrow().getExpression()); } } diff --git a/ext/scimv2/logic/src/test/resources/simplelogger.properties b/ext/scimv2/logic/src/test/resources/simplelogger.properties index 973e0096ff..030d4d091c 100644 --- a/ext/scimv2/logic/src/test/resources/simplelogger.properties +++ b/ext/scimv2/logic/src/test/resources/simplelogger.properties @@ -17,5 +17,6 @@ # See http://www.slf4j.org/api/org/slf4j/impl/SimpleLogger.html # Possible values: "trace", "debug", "info", "warn", or "error" +org.slf4j.simpleLogger.showDateTime=true +org.slf4j.simpleLogger.dateTimeFormat=YYYY-mm-dd HH:mm:ss.SSS org.slf4j.simpleLogger.defaultLogLevel=debug - diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java index c8e746c331..6ce4e0534e 100644 --- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java +++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/service/SCIMGroupServiceImpl.java @@ -124,7 +124,7 @@ private Set members(final String group) { MembershipCond membCond = new MembershipCond(); membCond.setGroup(group); - SearchCond searchCond = SearchCond.getLeaf(membCond); + SearchCond searchCond = SearchCond.of(membCond); long count = userLogic.search( searchCond, PageRequest.of(0, 1), SyncopeConstants.ROOT_REALM, true, false).getTotalElements(); for (int page = 0; page <= (count / AnyDAO.DEFAULT_PAGE_SIZE); page++) { diff --git a/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/PrintersValueProvider.java b/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/PrintersValueProvider.java index d297c44f43..d61ad75b04 100644 --- a/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/PrintersValueProvider.java +++ b/fit/core-reference/src/main/java-all/org/apache/syncope/fit/core/reference/flowable/PrintersValueProvider.java @@ -40,7 +40,7 @@ public class PrintersValueProvider implements DropdownValueProvider { static { AnyTypeCond anyTypeCond = new AnyTypeCond(); anyTypeCond.setAnyTypeKey("PRINTER"); - PRINTER_COND = SearchCond.getLeaf(anyTypeCond); + PRINTER_COND = SearchCond.of(anyTypeCond); Sort.Order orderByNameAsc = new Sort.Order(Sort.Direction.ASC, "name"); ORDER_BY = List.of(orderByNameAsc); diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CustomJWTSSOProvider.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CustomJWTSSOProvider.java index 03ab3e354d..6531e893b5 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CustomJWTSSOProvider.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/CustomJWTSSOProvider.java @@ -101,7 +101,7 @@ public Pair> resolve(final JWTClaimsSet jwtCl userIdCond.setType(AttrCond.Type.EQ); userIdCond.setExpression(jwtClaims.getSubject()); - List matching = anySearchDAO.search(SearchCond.getLeaf(userIdCond), AnyTypeKind.USER); + List matching = anySearchDAO.search(SearchCond.of(userIdCond), AnyTypeKind.USER); if (matching.size() == 1) { User user = matching.get(0); Set authorities = authDataAccessor.getAuthorities(user.getUsername(), null); diff --git a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSampleInboundCorrelationRule.java b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSampleInboundCorrelationRule.java index 832af71be1..4e01c5310b 100644 --- a/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSampleInboundCorrelationRule.java +++ b/fit/core-reference/src/main/java/org/apache/syncope/fit/core/reference/LinkedAccountSampleInboundCorrelationRule.java @@ -55,7 +55,7 @@ public SearchCond getSearchCond(final LiveSyncDelta syncDelta, final Provision p cond.setSchema(""); } - return SearchCond.getLeaf(cond); + return SearchCond.of(cond); } @Transactional(readOnly = true) diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java index ec9247e9d6..b91250c28a 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/SearchITCase.java @@ -984,46 +984,14 @@ public void issueSYNCOPE1727() { @Test public void issueSYNCOPE1779() { // 1. create user with underscore - UserTO userWithUnderscore = createUser(UserITCase.getSample("syncope1779_test@syncope.apache.org")).getEntity(); + UserTO userWith = createUser(UserITCase.getSample("syncope1779_test@syncope.apache.org")).getEntity(); // 2 create second user without underscore - createUser(UserITCase.getSample("syncope1779test@syncope.apache.org")).getEntity(); - - // 3. search for user - if (IS_EXT_SEARCH_ENABLED) { - try { - Thread.sleep(2000); - } catch (InterruptedException ex) { - // ignore - } - } - - // Search by username - PagedResult users = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). - fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("syncope1779_*"). - and().is("firstname").equalTo("syncope1779_*"). - and().is("userId").equalTo("syncope1779_*").query()). - build()); - assertEquals(1, users.getResult().size()); - assertEquals(userWithUnderscore.getKey(), users.getResult().get(0).getKey()); - // Search also by attribute - users = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). - fiql(SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("syncope1779_*").query()). - build()); - assertEquals(1, users.getResult().size()); - assertEquals(userWithUnderscore.getKey(), users.getResult().get(0).getKey()); - // search for both - users = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). - fiql(SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("syncope1779*").query()). - build()); - assertEquals(2, users.getResult().size()); - - users.getResult().forEach(u -> USER_SERVICE.delete(u.getKey())); - - // 4. create any object with underscore + UserTO userWithout = createUser(UserITCase.getSample("syncope1779test@syncope.apache.org")).getEntity(); + // 3. create printer with underscore AnyObjectTO printer = createAnyObject( new AnyObjectCR.Builder(SyncopeConstants.ROOT_REALM, PRINTER, "_syncope1779").build()).getEntity(); - // 5. search for printer + // 4. search if (IS_EXT_SEARCH_ENABLED) { try { Thread.sleep(2000); @@ -1032,14 +1000,38 @@ public void issueSYNCOPE1779() { } } - PagedResult printers = ANY_OBJECT_SERVICE.search( - new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). - fiql("$type==PRINTER;name==_syncope1779"). - build()); - assertEquals(1, printers.getResult().size()); - assertEquals(printer.getKey(), printers.getResult().get(0).getKey()); + try { + // Search by username + PagedResult users = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). + fiql(SyncopeClient.getUserSearchConditionBuilder().is("username").equalTo("syncope1779_*"). + and().is("firstname").equalTo("syncope1779_*"). + and().is("userId").equalTo("syncope1779_*").query()). + build()); + assertEquals(1, users.getResult().size()); + assertEquals(userWith.getKey(), users.getResult().get(0).getKey()); + // Search also by attribute + users = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). + fiql(SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("syncope1779_*").query()). + build()); + assertEquals(1, users.getResult().size()); + assertEquals(userWith.getKey(), users.getResult().get(0).getKey()); + // search for both + users = USER_SERVICE.search(new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). + fiql(SyncopeClient.getUserSearchConditionBuilder().is("email").equalTo("syncope1779*").query()). + build()); + assertEquals(2, users.getResult().size()); - printers.getResult().forEach(u -> ANY_OBJECT_SERVICE.delete(u.getKey())); + // search for printer + PagedResult printers = ANY_OBJECT_SERVICE.search( + new AnyQuery.Builder().realm(SyncopeConstants.ROOT_REALM). + fiql("$type==PRINTER;name==_syncope1779").build()); + assertEquals(1, printers.getResult().size()); + assertEquals(printer.getKey(), printers.getResult().get(0).getKey()); + } finally { + USER_SERVICE.delete(userWith.getKey()); + USER_SERVICE.delete(userWithout.getKey()); + ANY_OBJECT_SERVICE.delete(printer.getKey()); + } } @Test diff --git a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java index 466e417577..9cd1a95d63 100644 --- a/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java +++ b/fit/core-reference/src/test/java/org/apache/syncope/fit/core/UserIssuesITCase.java @@ -1850,12 +1850,11 @@ void issueSYNCOPE1818() { void issueSYNCOPE1853() { GroupTO cGroupForPropagation = createGroup( new GroupCR.Builder(SyncopeConstants.ROOT_REALM, "cGroupForPropagation") - .resource(RESOURCE_NAME_LDAP) - .build()).getEntity(); + .resource(RESOURCE_NAME_LDAP).build()).getEntity(); GroupTO dGroupForPropagation = createGroup( new GroupCR.Builder(SyncopeConstants.ROOT_REALM, "dGroupForPropagation") - .resource(RESOURCE_NAME_LDAP) - .build()).getEntity(); + .resource(RESOURCE_NAME_LDAP).build()).getEntity(); + // 1. assign both groups cGroupForPropagation and dGroupForPropagation with resource-csv to bellini updateUser(new UserUR.Builder("c9b2dec2-00a7-4855-97c0-d854842b4b24").memberships( new MembershipUR.Builder(cGroupForPropagation.getKey()).build(), @@ -1863,38 +1862,40 @@ void issueSYNCOPE1853() { // 2. assign cGroupForPropagation also to vivaldi updateUser(new UserUR.Builder("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee").membership( new MembershipUR.Builder(dGroupForPropagation.getKey()).build()).build()); + // 3. propagation tasks cleanup - TASK_SERVICE.search( - new TaskQuery.Builder(TaskType.PROPAGATION) - .anyTypeKind(AnyTypeKind.USER) - .resource(RESOURCE_NAME_LDAP) - .entityKey("c9b2dec2-00a7-4855-97c0-d854842b4b24") - .build()).getResult() + TASK_SERVICE.search(new TaskQuery.Builder(TaskType.PROPAGATION) + .anyTypeKind(AnyTypeKind.USER) + .resource(RESOURCE_NAME_LDAP) + .entityKey("c9b2dec2-00a7-4855-97c0-d854842b4b24") + .build()).getResult() .forEach(pt -> TASK_SERVICE.delete(TaskType.PROPAGATION, pt.getKey())); - TASK_SERVICE.search( - new TaskQuery.Builder(TaskType.PROPAGATION) - .anyTypeKind(AnyTypeKind.USER) - .resource(RESOURCE_NAME_LDAP) - .entityKey("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee") - .build()).getResult() + TASK_SERVICE.search(new TaskQuery.Builder(TaskType.PROPAGATION) + .anyTypeKind(AnyTypeKind.USER) + .resource(RESOURCE_NAME_LDAP) + .entityKey("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee") + .build()).getResult() .forEach(pt -> TASK_SERVICE.delete(TaskType.PROPAGATION, pt.getKey())); + // 4. delete group cGroupForPropagation: no deprovision should be fired on bellini, since there is already // bGroupForPropagation, deprovision instead must be fired for vivaldi GROUP_SERVICE.delete(cGroupForPropagation.getKey()); - await().during(5, TimeUnit.SECONDS).atMost(10, TimeUnit.SECONDS).until(() -> TASK_SERVICE.search( - new TaskQuery.Builder(TaskType.PROPAGATION) - .anyTypeKind(AnyTypeKind.USER) - .resource(RESOURCE_NAME_LDAP) - .entityKey("c9b2dec2-00a7-4855-97c0-d854842b4b24").build()) - .getResult().stream().map(PropagationTaskTO.class::cast) - .collect(Collectors.toList()).stream().noneMatch(pt -> ResourceOperation.DELETE == pt.getOperation())); + await().during(5, TimeUnit.SECONDS).atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).until( + () -> TASK_SERVICE.search(new TaskQuery.Builder(TaskType.PROPAGATION) + .anyTypeKind(AnyTypeKind.USER) + .resource(RESOURCE_NAME_LDAP) + .entityKey("c9b2dec2-00a7-4855-97c0-d854842b4b24").build()) + .getResult().stream().map(PropagationTaskTO.class::cast) + .collect(Collectors.toList()).stream().noneMatch(pt -> ResourceOperation.DELETE == pt. + getOperation())); GROUP_SERVICE.delete(dGroupForPropagation.getKey()); - await().atMost(10, TimeUnit.SECONDS).until(() -> TASK_SERVICE.search( - new TaskQuery.Builder(TaskType.PROPAGATION) - .anyTypeKind(AnyTypeKind.USER) - .resource(RESOURCE_NAME_LDAP) - .entityKey("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee").build()) - .getResult().stream().map(PropagationTaskTO.class::cast) - .collect(Collectors.toList()).stream().anyMatch(pt -> ResourceOperation.DELETE == pt.getOperation())); + await().atMost(MAX_WAIT_SECONDS, TimeUnit.SECONDS).until( + () -> TASK_SERVICE.search(new TaskQuery.Builder(TaskType.PROPAGATION) + .anyTypeKind(AnyTypeKind.USER) + .resource(RESOURCE_NAME_LDAP) + .entityKey("b3cbc78d-32e6-4bd4-92e0-bbe07566a2ee").build()) + .getResult().stream().map(PropagationTaskTO.class::cast) + .collect(Collectors.toList()).stream().anyMatch(pt -> ResourceOperation.DELETE == pt. + getOperation())); } } diff --git a/fit/core-reference/src/test/resources/TestPullRule.groovy b/fit/core-reference/src/test/resources/TestPullRule.groovy index f77e425ec7..7160f792da 100644 --- a/fit/core-reference/src/test/resources/TestPullRule.groovy +++ b/fit/core-reference/src/test/resources/TestPullRule.groovy @@ -36,6 +36,6 @@ class TestPullRule implements InboundCorrelationRule { cond.setType(AttrCond.Type.EQ); cond.setExpression(syncDelta.getObject().getName().getNameValue()); - return SearchCond.getLeaf(cond); + return SearchCond.of(cond); } } diff --git a/pom.xml b/pom.xml index 6092f99a9a..6c175650c0 100644 --- a/pom.xml +++ b/pom.xml @@ -414,7 +414,7 @@ under the License. 1.80 10.0.1 - 3.3.7 + 3.3.8 4.1.6 4.0.1 @@ -500,14 +500,14 @@ under the License. 10.1.33 35.0.0.Final - 6.2024.12 + 6.2025.1 4.1.2 17-alpine 9.0 11 23-slim-faststart - 5.26.0 + 5.26.1 42.7.5 9.2.0