github-advanced-security[bot] commented on code in PR #1300:
URL: https://github.com/apache/syncope/pull/1300#discussion_r2841160847
##########
ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchRealmSearchDAO.java:
##########
@@ -105,7 +132,7 @@
orElse(null);
return realmDAO.findById(result).map(Realm.class::cast);
} catch (Exception e) {
- LOG.error("While searching OpenSearch for Realm path {} with
request {}", fullPath, request, e);
+ LOG.error("While searching Elasticsearch for Realm path {} with
request {}", fullPath, request, e);
Review Comment:
## Log Injection
This log entry depends on a [user-provided value](1).
This log entry depends on a [user-provided value](2).
This log entry depends on a [user-provided value](3).
This log entry depends on a [user-provided value](4).
This log entry depends on a [user-provided value](5).
This log entry depends on a [user-provided value](6).
This log entry depends on a [user-provided value](7).
This log entry depends on a [user-provided value](8).
This log entry depends on a [user-provided value](9).
This log entry depends on a [user-provided value](10).
This log entry depends on a [user-provided value](11).
This log entry depends on a [user-provided value](12).
This log entry depends on a [user-provided value](13).
This log entry depends on a [user-provided value](14).
This log entry depends on a [user-provided value](15).
This log entry depends on a [user-provided value](16).
This log entry depends on a [user-provided value](17).
This log entry depends on a [user-provided value](18).
This log entry depends on a [user-provided value](19).
This log entry depends on a [user-provided value](20).
This log entry depends on a [user-provided value](21).
This log entry depends on a [user-provided value](22).
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2563)
##########
core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractSearchDAO.java:
##########
@@ -0,0 +1,288 @@
+/*
+ * 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 jakarta.validation.ValidationException;
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Supplier;
+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.SyncopeClientException;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import
org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+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.dao.search.SearchCond;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractSearchDAO {
+
+ public record CheckResult<C extends AttrCond>(PlainSchema schema,
PlainAttrValue value, C cond) {
+
+ }
+
+ protected static final Logger LOG =
LoggerFactory.getLogger(AbstractSearchDAO.class);
+
+ protected static final String ALWAYS_FALSE_CLAUSE = "1=2";
+
+ public static String key(final AttrSchemaType schemaType) {
+ return switch (schemaType) {
+ case Boolean ->
+ "booleanValue";
+
+ case Date ->
+ "dateValue";
+
+ case Double ->
+ "doubleValue";
+
+ case Long ->
+ "longValue";
+
+ case Binary ->
+ "binaryValue";
+
+ default ->
+ "stringValue";
+ };
+ }
+
+ protected static final Comparator<String> 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<String> split(final String attrValue, final
List<String> literals) {
+ final List<String> attrValues = new ArrayList<>();
+
+ if (literals.isEmpty()) {
+ attrValues.add(attrValue);
+ } else {
+ for (String token :
attrValue.split(Pattern.quote(literals.getFirst()))) {
+ if (!token.isEmpty()) {
+ attrValues.addAll(split(token, literals.subList(1,
literals.size())));
+ }
+ }
+ }
+
+ return attrValues;
+ }
+
+ protected static Supplier<SyncopeClientException>
syncopeClientException(final String message) {
+ return () -> {
+ SyncopeClientException sce =
SyncopeClientException.build(ClientExceptionType.InvalidSearchParameters);
+ sce.getElements().add(message);
+ return sce;
+ };
+ }
+
+ protected final PlainSchemaDAO plainSchemaDAO;
+
+ protected final EntityFactory entityFactory;
+
+ protected final PlainAttrValidationManager validator;
+
+ protected AbstractSearchDAO(
+ final PlainSchemaDAO plainSchemaDAO,
+ final EntityFactory entityFactory,
+ final PlainAttrValidationManager validator) {
+
+ this.plainSchemaDAO = plainSchemaDAO;
+ this.entityFactory = entityFactory;
+ this.validator = validator;
+ }
+
+ protected List<SearchCond> buildDerAttrValueConditions(
+ final String expression,
+ final String value,
+ final boolean ignoreCaseMatch) {
+
+ Parser parser = new Parser(expression);
+
+ // Schema keys
+ List<String> identifiers = new ArrayList<>();
+
+ // Literals
+ List<String> 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<String> 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<SearchCond> conditions = new ArrayList<>();
+
+ // Contains used identifiers in order to avoid replications
+ Set<String> used = new HashSet<>();
+
+ // Create several clauses: one for eanch identifiers
+ for (int i = 0; i < identifiers.size() &&
!used.contains(identifiers.get(i)); i++) {
+ used.add(identifiers.get(i));
+
+ AttrCond cond = plainSchemaDAO.findById(identifiers.get(i)).
+ map(schema -> new AttrCond()).
Review Comment:
## Useless parameter
The parameter 'schema' is never used.
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2565)
##########
core/persistence-common/src/main/java/org/apache/syncope/core/persistence/common/dao/AbstractSearchDAO.java:
##########
@@ -0,0 +1,288 @@
+/*
+ * 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 jakarta.validation.ValidationException;
+import jakarta.validation.constraints.Max;
+import jakarta.validation.constraints.Min;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Supplier;
+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.SyncopeClientException;
+import org.apache.syncope.common.lib.types.AttrSchemaType;
+import org.apache.syncope.common.lib.types.ClientExceptionType;
+import
org.apache.syncope.core.persistence.api.attrvalue.PlainAttrValidationManager;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+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.dao.search.SearchCond;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractSearchDAO {
+
+ public record CheckResult<C extends AttrCond>(PlainSchema schema,
PlainAttrValue value, C cond) {
+
+ }
+
+ protected static final Logger LOG =
LoggerFactory.getLogger(AbstractSearchDAO.class);
+
+ protected static final String ALWAYS_FALSE_CLAUSE = "1=2";
+
+ public static String key(final AttrSchemaType schemaType) {
+ return switch (schemaType) {
+ case Boolean ->
+ "booleanValue";
+
+ case Date ->
+ "dateValue";
+
+ case Double ->
+ "doubleValue";
+
+ case Long ->
+ "longValue";
+
+ case Binary ->
+ "binaryValue";
+
+ default ->
+ "stringValue";
+ };
+ }
+
+ protected static final Comparator<String> 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<String> split(final String attrValue, final
List<String> literals) {
+ final List<String> attrValues = new ArrayList<>();
+
+ if (literals.isEmpty()) {
+ attrValues.add(attrValue);
+ } else {
+ for (String token :
attrValue.split(Pattern.quote(literals.getFirst()))) {
+ if (!token.isEmpty()) {
+ attrValues.addAll(split(token, literals.subList(1,
literals.size())));
+ }
+ }
+ }
+
+ return attrValues;
+ }
+
+ protected static Supplier<SyncopeClientException>
syncopeClientException(final String message) {
+ return () -> {
+ SyncopeClientException sce =
SyncopeClientException.build(ClientExceptionType.InvalidSearchParameters);
+ sce.getElements().add(message);
+ return sce;
+ };
+ }
+
+ protected final PlainSchemaDAO plainSchemaDAO;
+
+ protected final EntityFactory entityFactory;
+
+ protected final PlainAttrValidationManager validator;
+
+ protected AbstractSearchDAO(
+ final PlainSchemaDAO plainSchemaDAO,
+ final EntityFactory entityFactory,
+ final PlainAttrValidationManager validator) {
+
+ this.plainSchemaDAO = plainSchemaDAO;
+ this.entityFactory = entityFactory;
+ this.validator = validator;
+ }
+
+ protected List<SearchCond> buildDerAttrValueConditions(
+ final String expression,
+ final String value,
+ final boolean ignoreCaseMatch) {
+
+ Parser parser = new Parser(expression);
+
+ // Schema keys
+ List<String> identifiers = new ArrayList<>();
+
+ // Literals
+ List<String> 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<String> 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<SearchCond> conditions = new ArrayList<>();
+
+ // Contains used identifiers in order to avoid replications
+ Set<String> used = new HashSet<>();
+
+ // Create several clauses: one for eanch identifiers
+ for (int i = 0; i < identifiers.size() &&
!used.contains(identifiers.get(i)); i++) {
+ used.add(identifiers.get(i));
+
+ AttrCond cond = plainSchemaDAO.findById(identifiers.get(i)).
+ map(schema -> new AttrCond()).
+ orElseGet(() -> new AnyCond());
+ cond.setType(ignoreCaseMatch ? AttrCond.Type.IEQ :
AttrCond.Type.EQ);
+ cond.setSchema(identifiers.get(i));
+ cond.setExpression(attrValues.get(i));
+ conditions.add(SearchCond.of(cond));
+ }
+
+ return conditions;
+ }
+
+ protected CheckResult<AttrCond> check(final AttrCond cond) {
+ PlainSchema schema = plainSchemaDAO.findById(cond.getSchema()).
+ orElseThrow(() -> new IllegalArgumentException("Invalid schema
" + cond.getSchema()));
+
+ PlainAttrValue attrValue = new PlainAttrValue();
+
+ if (AttrSchemaType.Encrypted == schema.getType()) {
+ throw new IllegalArgumentException("Cannot search by encrypted
schema " + cond.getSchema());
+ }
+
+ try {
+ if (cond.getType() != AttrCond.Type.LIKE
+ && cond.getType() != AttrCond.Type.ILIKE
+ && cond.getType() != AttrCond.Type.ISNULL
+ && cond.getType() != AttrCond.Type.ISNOTNULL) {
+
+ validator.validate(schema, cond.getExpression(), attrValue);
+ }
+ } catch (ValidationException e) {
+ throw new IllegalArgumentException("Could not validate expression
" + cond.getExpression());
+ }
+
+ return new CheckResult<>(schema, attrValue, cond);
+ }
+
+ protected CheckResult<AnyCond> check(final AnyCond cond, final Field
field, final Set<String> relationshipsFields) {
+ AnyCond computed = new AnyCond(cond.getType());
+ computed.setSchema(cond.getSchema());
+ computed.setExpression(cond.getExpression());
+
+ // Keeps track of difference between entity's getKey() and @Id fields
+ if ("key".equals(computed.getSchema())) {
+ computed.setSchema("id");
+ }
+
+ PlainSchema schema = entityFactory.newEntity(PlainSchema.class);
+ schema.setKey(field.getName());
+ for (AttrSchemaType attrSchemaType : AttrSchemaType.values()) {
+ if (field.getType().isAssignableFrom(attrSchemaType.getType())) {
+ 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;
+ boolean foundBooleanMax = false;
+ if (Integer.class.equals(field.getType())) {
+ for (Annotation annotation : field.getAnnotations()) {
+ if (Min.class.equals(annotation.annotationType())) {
+ foundBooleanMin = ((Min) annotation).value() == 0;
+ } else if (Max.class.equals(annotation.annotationType())) {
+ foundBooleanMax = ((Max) annotation).value() == 1;
+ }
+ }
+ }
+ if (foundBooleanMin && foundBooleanMax) {
+ schema.setType(AttrSchemaType.Boolean);
+ }
+
+ // Deal with fields representing relationships to other entities
+ if (relationshipsFields.contains(computed.getSchema())) {
+ computed.setSchema(computed.getSchema() + "_id");
+ schema.setType(AttrSchemaType.String);
+ }
+
+ PlainAttrValue attrValue = new PlainAttrValue();
+ if (computed.getType() != AttrCond.Type.LIKE
+ && computed.getType() != AttrCond.Type.ILIKE
+ && computed.getType() != AttrCond.Type.ISNULL
+ && computed.getType() != AttrCond.Type.ISNOTNULL) {
+
+ try {
+ validator.validate(schema, computed.getExpression(),
attrValue);
+ } catch (ValidationException e) {
+ LOG.error("Could not validate expression {}",
computed.getExpression(), e);
Review Comment:
## Log Injection
This log entry depends on a [user-provided value](1).
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2564)
##########
ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java:
##########
@@ -240,8 +245,6 @@
return builder;
}
- protected void customizeDocument(
- final Map<String, Object> builder,
- final AuditEvent auditEvent) {
+ protected void customizeDocument(final Map<String, Object> builder, final
AuditEvent auditEvent) {
Review Comment:
## Useless parameter
The parameter 'builder' is never used.
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2571)
##########
ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchRealmSearchDAO.java:
##########
@@ -159,28 +189,199 @@
});
Query prefix = new
Query.Builder().disMax(QueryBuilders.disMax().queries(basesQueries).build()).build();
- if (keyword == null) {
- return prefix;
+ BoolQuery.Builder boolBuilder = QueryBuilders.bool().filter(prefix);
+ if (searchCond != null) {
+ boolBuilder.filter(getQuery(searchCond));
}
+ return new Query.Builder().bool(boolBuilder.build()).build();
+ }
- return new Query.Builder().bool(QueryBuilders.bool().filter(
- prefix,
- new Query.Builder().wildcard(QueryBuilders.wildcard().
- field("name").value(keyword.replace('%',
'*').replace("\\_", "_")).
- caseInsensitive(true).build()).build()).build()).
- build();
+ protected Query getQuery(final SearchCond cond) {
+ Query query = null;
+
+ switch (cond.getType()) {
+ case LEAF, NOT_LEAF -> {
+ query =
cond.asLeaf(AnyCond.class).map(this::getQuery).orElse(null);
+ if (query == null) {
+ query =
cond.asLeaf(AttrCond.class).map(this::getQuery).orElse(null);
+ }
+ if (query == null) {
+ query = getQueryForCustomConds(cond);
+ }
+ if (query == null) {
+ throw new IllegalArgumentException("Cannot construct
QueryBuilder");
+ }
+ if (cond.getType() == SearchCond.Type.NOT_LEAF) {
+ query = new
Query.Builder().bool(QueryBuilders.bool().mustNot(query).build()).build();
+ }
+ }
+ case AND -> {
+ List<Query> andCompound = new ArrayList<>();
+ Query andLeft = getQuery(cond.getLeft());
+ if (andLeft.isBool() && !andLeft.bool().filter().isEmpty()) {
+ andCompound.addAll(andLeft.bool().filter());
+ } else {
+ andCompound.add(andLeft);
+ }
+ Query andRight = getQuery(cond.getRight());
+ if (andRight.isBool() && !andRight.bool().filter().isEmpty()) {
+ andCompound.addAll(andRight.bool().filter());
+ } else {
+ andCompound.add(andRight);
+ }
+ query = new
Query.Builder().bool(QueryBuilders.bool().filter(andCompound).build()).build();
+ }
+ case OR -> {
+ List<Query> orCompound = new ArrayList<>();
+ Query orLeft = getQuery(cond.getLeft());
+ if (orLeft.isDisMax()) {
+ orCompound.addAll(orLeft.disMax().queries());
+ } else {
+ orCompound.add(orLeft);
+ }
+ Query orRight = getQuery(cond.getRight());
+ if (orRight.isDisMax()) {
+ orCompound.addAll(orRight.disMax().queries());
+ } else {
+ orCompound.add(orRight);
+ }
+ query = new
Query.Builder().disMax(QueryBuilders.disMax().queries(orCompound).build()).build();
+ }
+ default -> {
+ }
+ }
+
+ return query;
}
@Override
- public long countDescendants(final String base, final String keyword) {
- return countDescendants(Set.of(base), keyword);
+ protected CheckResult<AnyCond> check(final AnyCond cond, final Field
field, final Set<String> relationshipsFields) {
+ CheckResult<AnyCond> checked = super.check(cond, field,
relationshipsFields);
+
+ // Manage difference between external id attribute and internal _id
+ if ("id".equals(checked.cond().getSchema())) {
+ checked.cond().setSchema("_id");
+ }
+ if ("id".equals(checked.schema().getKey())) {
+ checked.schema().setKey("_id");
+ }
+
+ return checked;
+ }
+
+ protected Query fillAttrQuery(
+ final PlainSchema schema,
+ final PlainAttrValue attrValue,
+ final AttrCond cond) {
+
+ Object value = schema.getType() == AttrSchemaType.Date &&
attrValue.getDateValue() != null
+ ? FormatUtils.format(attrValue.getDateValue())
+ : attrValue.getValue();
+
+ Query query = null;
+ switch (cond.getType()) {
+ case ISNOTNULL:
+ query = new
Query.Builder().exists(QueryBuilders.exists().field(schema.getKey()).build()).build();
+ break;
+
+ case ISNULL:
+ query = new Query.Builder().bool(QueryBuilders.bool().mustNot(
+ new
Query.Builder().exists(QueryBuilders.exists().field(schema.getKey()).build())
+ .build()).build()).build();
+ break;
+
+ case ILIKE:
+ query = new Query.Builder().wildcard(QueryBuilders.wildcard().
+
field(schema.getKey()).value(cond.getExpression().replace('%',
'*').replace("\\_", "_")).
+ caseInsensitive(true).build()).build();
+ break;
+
+ case LIKE:
+ query = new Query.Builder().wildcard(QueryBuilders.wildcard().
+
field(schema.getKey()).value(cond.getExpression().replace('%',
'*').replace("\\_", "_")).
+ caseInsensitive(false).build()).build();
+ break;
+
+ case IEQ:
+ query = new Query.Builder().term(QueryBuilders.term().
+
field(schema.getKey()).value(FieldValue.of(cond.getExpression())).caseInsensitive(true).
+ build()).build();
+ break;
+
+ case EQ:
+ FieldValue fieldValue = switch (value) {
+ case Double aDouble ->
+ FieldValue.of(aDouble);
+ case Long aLong ->
+ FieldValue.of(aLong);
+ case Boolean aBoolean ->
+ FieldValue.of(aBoolean);
+ default ->
+ FieldValue.of(value.toString());
+ };
+ query = new Query.Builder().term(QueryBuilders.term().
+
field(schema.getKey()).value(fieldValue).caseInsensitive(false).build()).
+ build();
+ break;
+
+ case GE:
+ query = new Query.Builder().range(RangeQuery.of(r ->
r.untyped(n -> n.
+ field(schema.getKey()).
+ gte(JsonData.of(value))))).
+ build();
+ break;
+
+ case GT:
+ query = new Query.Builder().range(RangeQuery.of(r ->
r.untyped(n -> n.
+ field(schema.getKey()).
+ gt(JsonData.of(value))))).
+ build();
+ break;
+
+ case LE:
+ query = new Query.Builder().range(RangeQuery.of(r ->
r.untyped(n -> n.
+ field(schema.getKey()).
+ lte(JsonData.of(value))))).
+ build();
+ break;
+
+ case LT:
+ query = new Query.Builder().range(RangeQuery.of(r ->
r.untyped(n -> n.
+ field(schema.getKey()).
+ lt(JsonData.of(value))))).
+ build();
+ break;
+
+ default:
+ break;
+ }
+
+ return query;
+ }
+
+ protected Query getQuery(final AttrCond cond) {
+ CheckResult<AttrCond> checked = check(cond);
+ return fillAttrQuery(checked.schema(), checked.value(), cond);
+ }
+
+ protected Query getQuery(final AnyCond cond) {
Review Comment:
## Confusing overloading of methods
Method ElasticsearchRealmSearchDAO.getQuery(..) could be confused with
overloaded method [getQuery](1), since dispatch depends on static types.
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2575)
##########
ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/OpenSearchUtils.java:
##########
@@ -240,8 +245,6 @@
return builder;
}
- protected void customizeDocument(
- final Map<String, Object> builder,
- final AuditEvent auditEvent) {
+ protected void customizeDocument(final Map<String, Object> builder, final
AuditEvent auditEvent) {
Review Comment:
## Useless parameter
The parameter 'builder' is never used.
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2573)
##########
ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchRealmSearchDAO.java:
##########
@@ -161,53 +188,252 @@
});
Query prefix = new
Query.Builder().disMax(QueryBuilders.disMax().queries(basesQueries).build()).build();
- if (keyword == null) {
- return prefix;
+ BoolQuery.Builder boolBuilder = QueryBuilders.bool().filter(prefix);
+ if (searchCond != null) {
+ boolBuilder.filter(getQuery(searchCond));
}
+ return new Query.Builder().bool(boolBuilder.build()).build();
+ }
- return new Query.Builder().bool(QueryBuilders.bool().filter(
- prefix,
- new Query.Builder().wildcard(QueryBuilders.wildcard().
- field("name").value(keyword.replace('%',
'*').replace("\\_", "_")).
- caseInsensitive(true).build()).build()).build()).
- build();
+ protected Query getQuery(final SearchCond cond) {
+ Query query = null;
+
+ switch (cond.getType()) {
+ case LEAF, NOT_LEAF -> {
+ query =
cond.asLeaf(AnyCond.class).map(this::getQuery).orElse(null);
+ if (query == null) {
+ query =
cond.asLeaf(AttrCond.class).map(this::getQuery).orElse(null);
+ }
+ if (query == null) {
+ query = getQueryForCustomConds(cond);
+ }
+ if (query == null) {
+ throw new IllegalArgumentException("Cannot construct
QueryBuilder");
+ }
+ if (cond.getType() == SearchCond.Type.NOT_LEAF) {
+ query = new
Query.Builder().bool(QueryBuilders.bool().mustNot(query).build()).build();
+ }
+ }
+ case AND -> {
+ List<Query> andCompound = new ArrayList<>();
+ Query andLeft = getQuery(cond.getLeft());
+ if (andLeft.isBool() && !andLeft.bool().filter().isEmpty()) {
+ andCompound.addAll(andLeft.bool().filter());
+ } else {
+ andCompound.add(andLeft);
+ }
+ Query andRight = getQuery(cond.getRight());
+ if (andRight.isBool() && !andRight.bool().filter().isEmpty()) {
+ andCompound.addAll(andRight.bool().filter());
+ } else {
+ andCompound.add(andRight);
+ }
+ query = new
Query.Builder().bool(QueryBuilders.bool().filter(andCompound).build()).build();
+ }
+ case OR -> {
+ List<Query> orCompound = new ArrayList<>();
+ Query orLeft = getQuery(cond.getLeft());
+ if (orLeft.isDisMax()) {
+ orCompound.addAll(orLeft.disMax().queries());
+ } else {
+ orCompound.add(orLeft);
+ }
+ Query orRight = getQuery(cond.getRight());
+ if (orRight.isDisMax()) {
+ orCompound.addAll(orRight.disMax().queries());
+ } else {
+ orCompound.add(orRight);
+ }
+ query = new
Query.Builder().disMax(QueryBuilders.disMax().queries(orCompound).build()).build();
+ }
+ default -> {
+ }
+ }
+
+ return query;
}
@Override
- public long countDescendants(final String base, final String keyword) {
- return countDescendants(Set.of(base), keyword);
+ protected CheckResult<AnyCond> check(final AnyCond cond, final Field
field, final Set<String> relationshipsFields) {
+ CheckResult<AnyCond> checked = super.check(cond, field,
relationshipsFields);
+
+ // Manage difference between external id attribute and internal _id
+ if ("id".equals(checked.cond().getSchema())) {
+ checked.cond().setSchema("_id");
+ }
+ if ("id".equals(checked.schema().getKey())) {
+ checked.schema().setKey("_id");
+ }
+
+ return checked;
+ }
+
+ protected Query fillAttrQuery(
+ final PlainSchema schema,
+ final PlainAttrValue attrValue,
+ final AttrCond cond) {
+
+ Object value = schema.getType() == AttrSchemaType.Date &&
attrValue.getDateValue() != null
+ ? FormatUtils.format(attrValue.getDateValue())
+ : attrValue.getValue();
+
+ Query query = null;
+ switch (cond.getType()) {
+ case ISNOTNULL:
+ query = new
Query.Builder().exists(QueryBuilders.exists().field(schema.getKey()).build()).build();
+ break;
+
+ case ISNULL:
+ query = new Query.Builder().bool(QueryBuilders.bool().mustNot(
+ new
Query.Builder().exists(QueryBuilders.exists().field(schema.getKey()).build())
+ .build()).build()).build();
+ break;
+
+ case ILIKE:
+ query = new Query.Builder().wildcard(QueryBuilders.wildcard().
+
field(schema.getKey()).value(cond.getExpression().replace('%',
'*').replace("\\_", "_")).
+ caseInsensitive(true).build()).build();
+ break;
+
+ case LIKE:
+ query = new Query.Builder().wildcard(QueryBuilders.wildcard().
+
field(schema.getKey()).value(cond.getExpression().replace('%',
'*').replace("\\_", "_")).
+ caseInsensitive(false).build()).build();
+ break;
+
+ case IEQ:
+ query = new Query.Builder().term(QueryBuilders.term().
+
field(schema.getKey()).value(FieldValue.of(cond.getExpression())).caseInsensitive(true).
+ build()).build();
+ break;
+
+ case EQ:
+ FieldValue fieldValue = switch (value) {
+ case Double aDouble ->
+ FieldValue.of(aDouble);
+ case Long aLong ->
+ FieldValue.of(aLong);
+ case Boolean aBoolean ->
+ FieldValue.of(aBoolean);
+ default ->
+ FieldValue.of(value.toString());
+ };
+ query = new Query.Builder().term(QueryBuilders.term().
+
field(schema.getKey()).value(fieldValue).caseInsensitive(false).build()).
+ build();
+ break;
+
+ case GE:
+ query = new Query.Builder().range(QueryBuilders.range().
+
field(schema.getKey()).gte(JsonData.of(value)).build()).
+ build();
+ break;
+
+ case GT:
+ query = new Query.Builder().range(QueryBuilders.range().
+ field(schema.getKey()).gt(JsonData.of(value)).build()).
+ build();
+ break;
+
+ case LE:
+ query = new Query.Builder().range(QueryBuilders.range().
+
field(schema.getKey()).lte(JsonData.of(value)).build()).
+ build();
+ break;
+
+ case LT:
+ query = new Query.Builder().range(QueryBuilders.range().
+ field(schema.getKey()).lt(JsonData.of(value)).build()).
+ build();
+ break;
+
+ default:
+ break;
+ }
+
+ return query;
+ }
+
+ protected Query getQuery(final AttrCond cond) {
+ CheckResult<AttrCond> checked = check(cond);
+ return fillAttrQuery(checked.schema(), checked.value(), cond);
+ }
+
+ protected Query getQuery(final AnyCond cond) {
Review Comment:
## Confusing overloading of methods
Method OpenSearchRealmSearchDAO.getQuery(..) could be confused with
overloaded method [getQuery](1), since dispatch depends on static types.
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2576)
##########
ext/elasticsearch/client-elasticsearch/src/main/java/org/apache/syncope/ext/elasticsearch/client/ElasticsearchUtils.java:
##########
@@ -240,8 +245,6 @@
return builder;
}
- protected void customizeDocument(
- final Map<String, Object> builder,
- final AuditEvent auditEvent) {
+ protected void customizeDocument(final Map<String, Object> builder, final
AuditEvent auditEvent) {
Review Comment:
## Useless parameter
The parameter 'auditEvent' is never used.
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2572)
##########
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPARealmSearchDAO.java:
##########
@@ -0,0 +1,537 @@
+/*
+ * 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.NoResultException;
+import jakarta.persistence.Query;
+import jakarta.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
+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.MalformedPathException;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmDAO;
+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.dao.search.SearchCond;
+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.apache.syncope.core.persistence.api.utils.RealmUtils;
+import org.apache.syncope.core.persistence.common.dao.AbstractRealmSearchDAO;
+import org.apache.syncope.core.persistence.jpa.entity.JPARealm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.transaction.annotation.Transactional;
+
+public abstract class AbstractJPARealmSearchDAO extends AbstractRealmSearchDAO
{
+
+ protected record QueryInfo(RealmSearchNode node, Set<String> plainSchemas)
{
+
+ }
+
+ protected record AttrCondQuery(Boolean addPlainSchemas, RealmSearchNode
node) {
+
+ }
+
+ protected static int setParameter(final List<Object> parameters, final
Object parameter) {
+ parameters.add(parameter);
+ return parameters.size();
+ }
+
+ protected static void fillWithParameters(final Query query, final
List<Object> parameters) {
+ for (int i = 0; i < parameters.size(); i++) {
+ if (parameters.get(i) instanceof Boolean aBoolean) {
+ query.setParameter(i + 1, aBoolean ? 1 : 0);
+ } else {
+ query.setParameter(i + 1, parameters.get(i));
+ }
+ }
+ }
+
+ protected final EntityManager entityManager;
+
+ protected final RealmUtils realmUtils;
+
+ protected AbstractJPARealmSearchDAO(
+ final EntityManager entityManager,
+ final PlainSchemaDAO plainSchemaDAO,
+ final EntityFactory entityFactory,
+ final PlainAttrValidationManager validator,
+ final RealmUtils realmUtils) {
+
+ super(plainSchemaDAO, entityFactory, validator);
+
+ this.entityManager = entityManager;
+ this.realmUtils = realmUtils;
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public Optional<Realm> findByFullPath(final String fullPath) {
+ if (StringUtils.isBlank(fullPath)
+ || (!SyncopeConstants.ROOT_REALM.equals(fullPath)
+ && !RealmDAO.PATH_PATTERN.matcher(fullPath).matches())) {
+
+ throw new MalformedPathException(fullPath);
+ }
+
+ TypedQuery<Realm> query = entityManager.createQuery(
+ "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE
e.fullPath=:fullPath", Realm.class);
+ query.setParameter("fullPath", fullPath);
+
+ Realm result = null;
+ try {
+ result = query.getSingleResult();
+ } catch (NoResultException e) {
+ LOG.debug("Realm with fullPath {} not found", fullPath, e);
+ }
+
+ return Optional.ofNullable(result);
+ }
+
+ @Override
+ public List<Realm> findByName(final String name) {
+ TypedQuery<Realm> query = entityManager.createQuery(
+ "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE
e.name=:name", Realm.class);
+ query.setParameter("name", name);
+
+ return query.getResultList();
+ }
+
+ @Override
+ public List<Realm> findChildren(final Realm realm) {
+ TypedQuery<Realm> query = entityManager.createQuery(
+ "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE
e.parent=:realm", Realm.class);
+ query.setParameter("realm", realm);
+
+ return query.getResultList();
+ }
+
+ @Override
+ public List<Realm> findDescendants(final String base, final String prefix)
{
+ List<Object> parameters = new ArrayList<>();
+
+ StringBuilder queryString = new StringBuilder("SELECT e FROM ").
+ append(JPARealm.class.getSimpleName()).
+ append(" e WHERE
(e.fullPath=?").append(setParameter(parameters, base)).
+ append(" OR e.fullPath LIKE ?").append(setParameter(
+ parameters,
+ SyncopeConstants.ROOT_REALM.equals(base) ? "/%" : base +
"/%")).
+ append(") ");
+ if (prefix != null) {
+ queryString.append("AND
(e.fullPath=?").append(setParameter(parameters, prefix)).
+ append(" OR e.fullPath LIKE ?").append(setParameter(
+ parameters,
+ SyncopeConstants.ROOT_REALM.equals(prefix) ? "/%" : prefix
+ "/%")).
+ append(") ");
+ }
+ queryString.append("ORDER BY e.fullPath");
+
+ TypedQuery<Realm> query =
entityManager.createQuery(queryString.toString(), Realm.class);
+
+ fillWithParameters(query, parameters);
+
+ return query.getResultList();
+ }
+
+ // ------------------------------------------ //
+ protected Optional<RealmSearchNode> getQueryForCustomConds(
+ final SearchCond cond,
+ final boolean not,
+ final List<Object> parameters) {
+
+ return Optional.empty();
+ }
+
+ protected Optional<QueryInfo> getQuery(final SearchCond cond, final
List<Object> parameters) {
+ if (cond == null) {
+ return Optional.empty();
+ }
+
+ boolean not = cond.getType() == SearchCond.Type.NOT_LEAF;
+
+ Optional<RealmSearchNode> node = Optional.empty();
+ Set<String> plainSchemas = new HashSet<>();
+
+ switch (cond.getType()) {
+ case LEAF:
+ case NOT_LEAF:
+ node = cond.asLeaf(AnyCond.class).
+ map(anyCond -> getQuery(anyCond, not, parameters)).
+ or(() -> cond.asLeaf(AttrCond.class).
+ map(attrCond -> {
+ CheckResult<AttrCond> checked = check(attrCond);
+ AttrCondQuery query = getQuery(attrCond, not,
checked, parameters);
+ if (query.addPlainSchemas()) {
+ plainSchemas.add(checked.schema().getKey());
+ }
+ return query.node();
+ }));
+
+ if (node.isEmpty()) {
+ node = getQueryForCustomConds(cond, not, parameters);
+ }
+ break;
+
+ case AND:
+ RealmSearchNode andNode = new
RealmSearchNode(RealmSearchNode.Type.AND);
+
+ getQuery(cond.getLeft(), parameters).ifPresent(left -> {
+ andNode.add(left.node());
+ plainSchemas.addAll(left.plainSchemas());
+ });
+
+ getQuery(cond.getRight(), parameters).ifPresent(right -> {
+ andNode.add(right.node());
+ plainSchemas.addAll(right.plainSchemas());
+ });
+
+ if (!andNode.getChildren().isEmpty()) {
+ node = Optional.of(andNode);
+ }
+ break;
+
+ case OR:
+ RealmSearchNode orNode = new
RealmSearchNode(RealmSearchNode.Type.OR);
+
+ getQuery(cond.getLeft(), parameters).ifPresent(left -> {
+ orNode.add(left.node());
+ plainSchemas.addAll(left.plainSchemas());
+ });
+
+ getQuery(cond.getRight(), parameters).ifPresent(right -> {
+ orNode.add(right.node());
+ plainSchemas.addAll(right.plainSchemas());
+ });
+
+ if (!orNode.getChildren().isEmpty()) {
+ node = Optional.of(orNode);
+ }
+ break;
+
+ default:
+ }
+
+ return node.map(n -> new QueryInfo(n, plainSchemas));
+ }
+
+ protected abstract AttrCondQuery getQuery(
+ AttrCond cond,
+ boolean not,
+ CheckResult<AttrCond> checked,
+ List<Object> parameters);
+
+ protected RealmSearchNode getQuery(final AnyCond cond, final boolean not,
final List<Object> parameters) {
+ CheckResult<AnyCond> checked = check(
+ cond,
+ realmUtils.getField(cond.getSchema()).
+ orElseThrow(() -> new
IllegalArgumentException("Invalid schema " + cond.getSchema())),
+ RELATIONSHIP_FIELDS);
+
+ return switch (checked.cond().getType()) {
+ case ISNULL ->
+ new RealmSearchNode.Leaf("r." + checked.cond().getSchema() +
(not ? " IS NOT NULL" : " IS NULL"));
+
+ case ISNOTNULL ->
+ new RealmSearchNode.Leaf("r." + checked.cond().getSchema() +
(not ? " IS NULL" : " IS NOT NULL"));
+
+ default ->
+ fillAttrQuery(
+ "r." + checked.cond().getSchema(),
+ checked.value(),
+ checked.schema(),
+ checked.cond(),
+ not,
+ parameters);
+ };
+ }
+
+ protected RealmSearchNode.Leaf fillAttrQuery(
+ final String column,
+ final PlainAttrValue attrValue,
+ final PlainSchema schema,
+ final AttrCond cond,
+ final boolean not,
+ final List<Object> parameters) {
+
+ boolean ignoreCase = AttrCond.Type.ILIKE == cond.getType() ||
AttrCond.Type.IEQ == cond.getType();
+
+ String left = column;
+ if (ignoreCase && (schema.getType() == AttrSchemaType.String ||
schema.getType() == AttrSchemaType.Enum)) {
+ left = "LOWER(" + left + ')';
+ }
+
+ StringBuilder clause = new StringBuilder(left);
+ switch (cond.getType()) {
+
+ 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 (this instanceof OracleJPARealmSearchDAO) {
+ clause.append(" ESCAPE '\\'");
+ }
+ } else {
+ LOG.error("LIKE is only compatible with string or enum
schemas");
+ return new RealmSearchNode.Leaf(ALWAYS_FALSE_CLAUSE);
+ }
+ break;
+
+ case IEQ:
+ case EQ:
+ default:
+ clause.append(not ? "<>" : "=");
+ if (ignoreCase
+ && (schema.getType() == AttrSchemaType.String ||
schema.getType() == AttrSchemaType.Enum)) {
+ clause.append("LOWER(?").append(setParameter(parameters,
attrValue.getValue())).append(')');
+ } else {
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ }
+ break;
+
+ case GE:
+ clause.append(not ? "<" : ">=");
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ break;
+
+ case GT:
+ clause.append(not ? "<=" : ">");
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ break;
+
+ case LE:
+ clause.append(not ? ">" : "<=");
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ break;
+
+ case LT:
+ clause.append(not ? ">=" : "<");
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ break;
+ }
+
+ return new RealmSearchNode.Leaf(
+ cond instanceof AnyCond
+ ? clause.toString()
+ : "r.schema_id='" + schema.getKey() + "' AND " +
clause);
+ }
+
+ protected void visitNode(final RealmSearchNode node, final List<String>
where) {
+ node.asLeaf().ifPresentOrElse(
+ leaf -> where.add(leaf.getClause()),
+ () -> {
+ List<String> nodeWhere = new ArrayList<>();
+ node.getChildren().forEach(child -> visitNode(child,
nodeWhere));
+ String op = " " + node.getType().name() + " ";
+ where.add(nodeWhere.stream().
+ map(w -> w.contains(" AND ") || w.contains(" OR ")
? "(" + w + ")" : w).
+ collect(Collectors.joining(op)));
+ });
+ }
+
+ protected String buildFrom(final Set<String> plainSchemas, final
OrderBySupport obs) {
+ return JPARealm.TABLE + " r";
+ }
+
+ protected String buildWhere(final Set<String> bases, final QueryInfo
queryInfo, final List<Object> parameters) {
+ String fullPaths = bases.stream().
+ map(base -> "r.fullPath=?" + setParameter(parameters, base)
+ + " OR r.fullPath LIKE ?" + setParameter(
+ parameters, SyncopeConstants.ROOT_REALM.equals(base) ?
"/%" : base + "/%")).
+ collect(Collectors.joining(" OR "));
+
+ RealmSearchNode root;
+ if (queryInfo.node().getType() == RealmSearchNode.Type.AND) {
+ root = queryInfo.node();
+ } else {
+ root = new RealmSearchNode(RealmSearchNode.Type.AND);
+ root.add(queryInfo.node());
+ }
+
+ List<String> where = new ArrayList<>();
+ visitNode(root, where);
+
+ return "(" + fullPaths + ')'
+ + " AND (" + where.stream().
+ map(w -> w.contains(" AND ") || w.contains(" OR ") ?
"(" + w + ")" : w).
+ collect(Collectors.joining(' ' + root.getType().name()
+ ' '))
+ + ')';
+ }
+
+ @Override
+ protected long doCount(final Set<String> bases, final SearchCond cond) {
+ List<Object> parameters = new ArrayList<>();
+
+ QueryInfo queryInfo = getQuery(cond, parameters).orElse(null);
+ if (queryInfo == null) {
+ LOG.error("Invalid search condition: {}", cond);
+ return 0;
+ }
+
+ String queryString = new StringBuilder("SELECT COUNT(DISTINCT r.id)").
+ append(" FROM ").append(buildFrom(queryInfo.plainSchemas(),
null)).
+ append(" WHERE ").append(buildWhere(bases, queryInfo,
parameters)).
+ toString();
+
+ LOG.debug("Query: {}, parameters: {}", queryString, parameters);
+
+ Query query = entityManager.createNativeQuery(queryString);
+ fillWithParameters(query, parameters);
+
+ return ((Number) query.getSingleResult()).longValue();
+ }
+
+ protected abstract void parseOrderByForPlainSchema(
+ OrderBySupport obs,
+ OrderBySupport.Item item,
+ Sort.Order clause,
+ PlainSchema schema,
+ String fieldName);
+
+ protected void parseOrderByForField(
+ final OrderBySupport.Item item,
+ final String fieldName,
+ final Sort.Order clause) {
+
+ item.select = "r." + fieldName;
+ item.where = StringUtils.EMPTY;
+ item.orderBy = "r." + fieldName + ' ' + clause.getDirection().name();
+ }
+
+ protected void parseOrderByForCustom(
+ final Sort.Order clause,
+ final OrderBySupport.Item item,
Review Comment:
## Useless parameter
The parameter 'item' is never used.
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2568)
##########
ext/opensearch/client-opensearch/src/main/java/org/apache/syncope/ext/opensearch/client/OpenSearchUtils.java:
##########
@@ -240,8 +245,6 @@
return builder;
}
- protected void customizeDocument(
- final Map<String, Object> builder,
- final AuditEvent auditEvent) {
+ protected void customizeDocument(final Map<String, Object> builder, final
AuditEvent auditEvent) {
Review Comment:
## Useless parameter
The parameter 'auditEvent' is never used.
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2574)
##########
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPARealmSearchDAO.java:
##########
@@ -0,0 +1,537 @@
+/*
+ * 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.NoResultException;
+import jakarta.persistence.Query;
+import jakarta.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
+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.MalformedPathException;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmDAO;
+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.dao.search.SearchCond;
+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.apache.syncope.core.persistence.api.utils.RealmUtils;
+import org.apache.syncope.core.persistence.common.dao.AbstractRealmSearchDAO;
+import org.apache.syncope.core.persistence.jpa.entity.JPARealm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.transaction.annotation.Transactional;
+
+public abstract class AbstractJPARealmSearchDAO extends AbstractRealmSearchDAO
{
+
+ protected record QueryInfo(RealmSearchNode node, Set<String> plainSchemas)
{
+
+ }
+
+ protected record AttrCondQuery(Boolean addPlainSchemas, RealmSearchNode
node) {
+
+ }
+
+ protected static int setParameter(final List<Object> parameters, final
Object parameter) {
+ parameters.add(parameter);
+ return parameters.size();
+ }
+
+ protected static void fillWithParameters(final Query query, final
List<Object> parameters) {
+ for (int i = 0; i < parameters.size(); i++) {
+ if (parameters.get(i) instanceof Boolean aBoolean) {
+ query.setParameter(i + 1, aBoolean ? 1 : 0);
+ } else {
+ query.setParameter(i + 1, parameters.get(i));
+ }
+ }
+ }
+
+ protected final EntityManager entityManager;
+
+ protected final RealmUtils realmUtils;
+
+ protected AbstractJPARealmSearchDAO(
+ final EntityManager entityManager,
+ final PlainSchemaDAO plainSchemaDAO,
+ final EntityFactory entityFactory,
+ final PlainAttrValidationManager validator,
+ final RealmUtils realmUtils) {
+
+ super(plainSchemaDAO, entityFactory, validator);
+
+ this.entityManager = entityManager;
+ this.realmUtils = realmUtils;
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public Optional<Realm> findByFullPath(final String fullPath) {
+ if (StringUtils.isBlank(fullPath)
+ || (!SyncopeConstants.ROOT_REALM.equals(fullPath)
+ && !RealmDAO.PATH_PATTERN.matcher(fullPath).matches())) {
+
+ throw new MalformedPathException(fullPath);
+ }
+
+ TypedQuery<Realm> query = entityManager.createQuery(
+ "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE
e.fullPath=:fullPath", Realm.class);
+ query.setParameter("fullPath", fullPath);
+
+ Realm result = null;
+ try {
+ result = query.getSingleResult();
+ } catch (NoResultException e) {
+ LOG.debug("Realm with fullPath {} not found", fullPath, e);
+ }
+
+ return Optional.ofNullable(result);
+ }
+
+ @Override
+ public List<Realm> findByName(final String name) {
+ TypedQuery<Realm> query = entityManager.createQuery(
+ "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE
e.name=:name", Realm.class);
+ query.setParameter("name", name);
+
+ return query.getResultList();
+ }
+
+ @Override
+ public List<Realm> findChildren(final Realm realm) {
+ TypedQuery<Realm> query = entityManager.createQuery(
+ "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE
e.parent=:realm", Realm.class);
+ query.setParameter("realm", realm);
+
+ return query.getResultList();
+ }
+
+ @Override
+ public List<Realm> findDescendants(final String base, final String prefix)
{
+ List<Object> parameters = new ArrayList<>();
+
+ StringBuilder queryString = new StringBuilder("SELECT e FROM ").
+ append(JPARealm.class.getSimpleName()).
+ append(" e WHERE
(e.fullPath=?").append(setParameter(parameters, base)).
+ append(" OR e.fullPath LIKE ?").append(setParameter(
+ parameters,
+ SyncopeConstants.ROOT_REALM.equals(base) ? "/%" : base +
"/%")).
+ append(") ");
+ if (prefix != null) {
+ queryString.append("AND
(e.fullPath=?").append(setParameter(parameters, prefix)).
+ append(" OR e.fullPath LIKE ?").append(setParameter(
+ parameters,
+ SyncopeConstants.ROOT_REALM.equals(prefix) ? "/%" : prefix
+ "/%")).
+ append(") ");
+ }
+ queryString.append("ORDER BY e.fullPath");
+
+ TypedQuery<Realm> query =
entityManager.createQuery(queryString.toString(), Realm.class);
+
+ fillWithParameters(query, parameters);
+
+ return query.getResultList();
+ }
+
+ // ------------------------------------------ //
+ protected Optional<RealmSearchNode> getQueryForCustomConds(
+ final SearchCond cond,
+ final boolean not,
+ final List<Object> parameters) {
+
+ return Optional.empty();
+ }
+
+ protected Optional<QueryInfo> getQuery(final SearchCond cond, final
List<Object> parameters) {
+ if (cond == null) {
+ return Optional.empty();
+ }
+
+ boolean not = cond.getType() == SearchCond.Type.NOT_LEAF;
+
+ Optional<RealmSearchNode> node = Optional.empty();
+ Set<String> plainSchemas = new HashSet<>();
+
+ switch (cond.getType()) {
+ case LEAF:
+ case NOT_LEAF:
+ node = cond.asLeaf(AnyCond.class).
+ map(anyCond -> getQuery(anyCond, not, parameters)).
+ or(() -> cond.asLeaf(AttrCond.class).
+ map(attrCond -> {
+ CheckResult<AttrCond> checked = check(attrCond);
+ AttrCondQuery query = getQuery(attrCond, not,
checked, parameters);
+ if (query.addPlainSchemas()) {
+ plainSchemas.add(checked.schema().getKey());
+ }
+ return query.node();
+ }));
+
+ if (node.isEmpty()) {
+ node = getQueryForCustomConds(cond, not, parameters);
+ }
+ break;
+
+ case AND:
+ RealmSearchNode andNode = new
RealmSearchNode(RealmSearchNode.Type.AND);
+
+ getQuery(cond.getLeft(), parameters).ifPresent(left -> {
+ andNode.add(left.node());
+ plainSchemas.addAll(left.plainSchemas());
+ });
+
+ getQuery(cond.getRight(), parameters).ifPresent(right -> {
+ andNode.add(right.node());
+ plainSchemas.addAll(right.plainSchemas());
+ });
+
+ if (!andNode.getChildren().isEmpty()) {
+ node = Optional.of(andNode);
+ }
+ break;
+
+ case OR:
+ RealmSearchNode orNode = new
RealmSearchNode(RealmSearchNode.Type.OR);
+
+ getQuery(cond.getLeft(), parameters).ifPresent(left -> {
+ orNode.add(left.node());
+ plainSchemas.addAll(left.plainSchemas());
+ });
+
+ getQuery(cond.getRight(), parameters).ifPresent(right -> {
+ orNode.add(right.node());
+ plainSchemas.addAll(right.plainSchemas());
+ });
+
+ if (!orNode.getChildren().isEmpty()) {
+ node = Optional.of(orNode);
+ }
+ break;
+
+ default:
+ }
+
+ return node.map(n -> new QueryInfo(n, plainSchemas));
+ }
+
+ protected abstract AttrCondQuery getQuery(
+ AttrCond cond,
+ boolean not,
+ CheckResult<AttrCond> checked,
+ List<Object> parameters);
+
+ protected RealmSearchNode getQuery(final AnyCond cond, final boolean not,
final List<Object> parameters) {
+ CheckResult<AnyCond> checked = check(
+ cond,
+ realmUtils.getField(cond.getSchema()).
+ orElseThrow(() -> new
IllegalArgumentException("Invalid schema " + cond.getSchema())),
+ RELATIONSHIP_FIELDS);
+
+ return switch (checked.cond().getType()) {
+ case ISNULL ->
+ new RealmSearchNode.Leaf("r." + checked.cond().getSchema() +
(not ? " IS NOT NULL" : " IS NULL"));
+
+ case ISNOTNULL ->
+ new RealmSearchNode.Leaf("r." + checked.cond().getSchema() +
(not ? " IS NULL" : " IS NOT NULL"));
+
+ default ->
+ fillAttrQuery(
+ "r." + checked.cond().getSchema(),
+ checked.value(),
+ checked.schema(),
+ checked.cond(),
+ not,
+ parameters);
+ };
+ }
+
+ protected RealmSearchNode.Leaf fillAttrQuery(
+ final String column,
+ final PlainAttrValue attrValue,
+ final PlainSchema schema,
+ final AttrCond cond,
+ final boolean not,
+ final List<Object> parameters) {
+
+ boolean ignoreCase = AttrCond.Type.ILIKE == cond.getType() ||
AttrCond.Type.IEQ == cond.getType();
+
+ String left = column;
+ if (ignoreCase && (schema.getType() == AttrSchemaType.String ||
schema.getType() == AttrSchemaType.Enum)) {
+ left = "LOWER(" + left + ')';
+ }
+
+ StringBuilder clause = new StringBuilder(left);
+ switch (cond.getType()) {
+
+ 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 (this instanceof OracleJPARealmSearchDAO) {
+ clause.append(" ESCAPE '\\'");
+ }
+ } else {
+ LOG.error("LIKE is only compatible with string or enum
schemas");
+ return new RealmSearchNode.Leaf(ALWAYS_FALSE_CLAUSE);
+ }
+ break;
+
+ case IEQ:
+ case EQ:
+ default:
+ clause.append(not ? "<>" : "=");
+ if (ignoreCase
+ && (schema.getType() == AttrSchemaType.String ||
schema.getType() == AttrSchemaType.Enum)) {
+ clause.append("LOWER(?").append(setParameter(parameters,
attrValue.getValue())).append(')');
+ } else {
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ }
+ break;
+
+ case GE:
+ clause.append(not ? "<" : ">=");
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ break;
+
+ case GT:
+ clause.append(not ? "<=" : ">");
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ break;
+
+ case LE:
+ clause.append(not ? ">" : "<=");
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ break;
+
+ case LT:
+ clause.append(not ? ">=" : "<");
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ break;
+ }
+
+ return new RealmSearchNode.Leaf(
+ cond instanceof AnyCond
+ ? clause.toString()
+ : "r.schema_id='" + schema.getKey() + "' AND " +
clause);
+ }
+
+ protected void visitNode(final RealmSearchNode node, final List<String>
where) {
+ node.asLeaf().ifPresentOrElse(
+ leaf -> where.add(leaf.getClause()),
+ () -> {
+ List<String> nodeWhere = new ArrayList<>();
+ node.getChildren().forEach(child -> visitNode(child,
nodeWhere));
+ String op = " " + node.getType().name() + " ";
+ where.add(nodeWhere.stream().
+ map(w -> w.contains(" AND ") || w.contains(" OR ")
? "(" + w + ")" : w).
+ collect(Collectors.joining(op)));
+ });
+ }
+
+ protected String buildFrom(final Set<String> plainSchemas, final
OrderBySupport obs) {
+ return JPARealm.TABLE + " r";
+ }
+
+ protected String buildWhere(final Set<String> bases, final QueryInfo
queryInfo, final List<Object> parameters) {
+ String fullPaths = bases.stream().
+ map(base -> "r.fullPath=?" + setParameter(parameters, base)
+ + " OR r.fullPath LIKE ?" + setParameter(
+ parameters, SyncopeConstants.ROOT_REALM.equals(base) ?
"/%" : base + "/%")).
+ collect(Collectors.joining(" OR "));
+
+ RealmSearchNode root;
+ if (queryInfo.node().getType() == RealmSearchNode.Type.AND) {
+ root = queryInfo.node();
+ } else {
+ root = new RealmSearchNode(RealmSearchNode.Type.AND);
+ root.add(queryInfo.node());
+ }
+
+ List<String> where = new ArrayList<>();
+ visitNode(root, where);
+
+ return "(" + fullPaths + ')'
+ + " AND (" + where.stream().
+ map(w -> w.contains(" AND ") || w.contains(" OR ") ?
"(" + w + ")" : w).
+ collect(Collectors.joining(' ' + root.getType().name()
+ ' '))
+ + ')';
+ }
+
+ @Override
+ protected long doCount(final Set<String> bases, final SearchCond cond) {
+ List<Object> parameters = new ArrayList<>();
+
+ QueryInfo queryInfo = getQuery(cond, parameters).orElse(null);
+ if (queryInfo == null) {
+ LOG.error("Invalid search condition: {}", cond);
+ return 0;
+ }
+
+ String queryString = new StringBuilder("SELECT COUNT(DISTINCT r.id)").
+ append(" FROM ").append(buildFrom(queryInfo.plainSchemas(),
null)).
+ append(" WHERE ").append(buildWhere(bases, queryInfo,
parameters)).
+ toString();
+
+ LOG.debug("Query: {}, parameters: {}", queryString, parameters);
+
+ Query query = entityManager.createNativeQuery(queryString);
+ fillWithParameters(query, parameters);
+
+ return ((Number) query.getSingleResult()).longValue();
+ }
+
+ protected abstract void parseOrderByForPlainSchema(
+ OrderBySupport obs,
+ OrderBySupport.Item item,
+ Sort.Order clause,
+ PlainSchema schema,
+ String fieldName);
+
+ protected void parseOrderByForField(
+ final OrderBySupport.Item item,
+ final String fieldName,
+ final Sort.Order clause) {
+
+ item.select = "r." + fieldName;
+ item.where = StringUtils.EMPTY;
+ item.orderBy = "r." + fieldName + ' ' + clause.getDirection().name();
+ }
+
+ protected void parseOrderByForCustom(
+ final Sort.Order clause,
Review Comment:
## Useless parameter
The parameter 'clause' is never used.
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2567)
##########
ext/elasticsearch/persistence/src/main/java/org/apache/syncope/core/persistence/elasticsearch/dao/ElasticsearchRealmSearchDAO.java:
##########
@@ -159,28 +189,199 @@
});
Query prefix = new
Query.Builder().disMax(QueryBuilders.disMax().queries(basesQueries).build()).build();
- if (keyword == null) {
- return prefix;
+ BoolQuery.Builder boolBuilder = QueryBuilders.bool().filter(prefix);
+ if (searchCond != null) {
+ boolBuilder.filter(getQuery(searchCond));
}
+ return new Query.Builder().bool(boolBuilder.build()).build();
+ }
- return new Query.Builder().bool(QueryBuilders.bool().filter(
- prefix,
- new Query.Builder().wildcard(QueryBuilders.wildcard().
- field("name").value(keyword.replace('%',
'*').replace("\\_", "_")).
- caseInsensitive(true).build()).build()).build()).
- build();
+ protected Query getQuery(final SearchCond cond) {
+ Query query = null;
+
+ switch (cond.getType()) {
+ case LEAF, NOT_LEAF -> {
+ query =
cond.asLeaf(AnyCond.class).map(this::getQuery).orElse(null);
+ if (query == null) {
+ query =
cond.asLeaf(AttrCond.class).map(this::getQuery).orElse(null);
+ }
+ if (query == null) {
+ query = getQueryForCustomConds(cond);
+ }
+ if (query == null) {
+ throw new IllegalArgumentException("Cannot construct
QueryBuilder");
+ }
+ if (cond.getType() == SearchCond.Type.NOT_LEAF) {
+ query = new
Query.Builder().bool(QueryBuilders.bool().mustNot(query).build()).build();
+ }
+ }
+ case AND -> {
+ List<Query> andCompound = new ArrayList<>();
+ Query andLeft = getQuery(cond.getLeft());
+ if (andLeft.isBool() && !andLeft.bool().filter().isEmpty()) {
+ andCompound.addAll(andLeft.bool().filter());
+ } else {
+ andCompound.add(andLeft);
+ }
+ Query andRight = getQuery(cond.getRight());
+ if (andRight.isBool() && !andRight.bool().filter().isEmpty()) {
+ andCompound.addAll(andRight.bool().filter());
+ } else {
+ andCompound.add(andRight);
+ }
+ query = new
Query.Builder().bool(QueryBuilders.bool().filter(andCompound).build()).build();
+ }
+ case OR -> {
+ List<Query> orCompound = new ArrayList<>();
+ Query orLeft = getQuery(cond.getLeft());
+ if (orLeft.isDisMax()) {
+ orCompound.addAll(orLeft.disMax().queries());
+ } else {
+ orCompound.add(orLeft);
+ }
+ Query orRight = getQuery(cond.getRight());
+ if (orRight.isDisMax()) {
+ orCompound.addAll(orRight.disMax().queries());
+ } else {
+ orCompound.add(orRight);
+ }
+ query = new
Query.Builder().disMax(QueryBuilders.disMax().queries(orCompound).build()).build();
+ }
+ default -> {
+ }
+ }
+
+ return query;
}
@Override
- public long countDescendants(final String base, final String keyword) {
- return countDescendants(Set.of(base), keyword);
+ protected CheckResult<AnyCond> check(final AnyCond cond, final Field
field, final Set<String> relationshipsFields) {
+ CheckResult<AnyCond> checked = super.check(cond, field,
relationshipsFields);
+
+ // Manage difference between external id attribute and internal _id
+ if ("id".equals(checked.cond().getSchema())) {
+ checked.cond().setSchema("_id");
+ }
+ if ("id".equals(checked.schema().getKey())) {
+ checked.schema().setKey("_id");
+ }
+
+ return checked;
+ }
+
+ protected Query fillAttrQuery(
+ final PlainSchema schema,
+ final PlainAttrValue attrValue,
+ final AttrCond cond) {
+
+ Object value = schema.getType() == AttrSchemaType.Date &&
attrValue.getDateValue() != null
+ ? FormatUtils.format(attrValue.getDateValue())
+ : attrValue.getValue();
+
+ Query query = null;
+ switch (cond.getType()) {
+ case ISNOTNULL:
+ query = new
Query.Builder().exists(QueryBuilders.exists().field(schema.getKey()).build()).build();
+ break;
+
+ case ISNULL:
+ query = new Query.Builder().bool(QueryBuilders.bool().mustNot(
+ new
Query.Builder().exists(QueryBuilders.exists().field(schema.getKey()).build())
+ .build()).build()).build();
+ break;
+
+ case ILIKE:
+ query = new Query.Builder().wildcard(QueryBuilders.wildcard().
+
field(schema.getKey()).value(cond.getExpression().replace('%',
'*').replace("\\_", "_")).
+ caseInsensitive(true).build()).build();
+ break;
+
+ case LIKE:
+ query = new Query.Builder().wildcard(QueryBuilders.wildcard().
+
field(schema.getKey()).value(cond.getExpression().replace('%',
'*').replace("\\_", "_")).
+ caseInsensitive(false).build()).build();
+ break;
+
+ case IEQ:
+ query = new Query.Builder().term(QueryBuilders.term().
+
field(schema.getKey()).value(FieldValue.of(cond.getExpression())).caseInsensitive(true).
+ build()).build();
+ break;
+
+ case EQ:
+ FieldValue fieldValue = switch (value) {
+ case Double aDouble ->
+ FieldValue.of(aDouble);
+ case Long aLong ->
+ FieldValue.of(aLong);
+ case Boolean aBoolean ->
+ FieldValue.of(aBoolean);
+ default ->
+ FieldValue.of(value.toString());
+ };
+ query = new Query.Builder().term(QueryBuilders.term().
+
field(schema.getKey()).value(fieldValue).caseInsensitive(false).build()).
+ build();
+ break;
+
+ case GE:
+ query = new Query.Builder().range(RangeQuery.of(r ->
r.untyped(n -> n.
+ field(schema.getKey()).
+ gte(JsonData.of(value))))).
+ build();
+ break;
+
+ case GT:
+ query = new Query.Builder().range(RangeQuery.of(r ->
r.untyped(n -> n.
+ field(schema.getKey()).
+ gt(JsonData.of(value))))).
+ build();
+ break;
+
+ case LE:
+ query = new Query.Builder().range(RangeQuery.of(r ->
r.untyped(n -> n.
+ field(schema.getKey()).
+ lte(JsonData.of(value))))).
+ build();
+ break;
+
+ case LT:
+ query = new Query.Builder().range(RangeQuery.of(r ->
r.untyped(n -> n.
+ field(schema.getKey()).
+ lt(JsonData.of(value))))).
+ build();
+ break;
+
+ default:
+ break;
+ }
+
+ return query;
+ }
+
+ protected Query getQuery(final AttrCond cond) {
+ CheckResult<AttrCond> checked = check(cond);
+ return fillAttrQuery(checked.schema(), checked.value(), cond);
+ }
+
+ protected Query getQuery(final AnyCond cond) {
+ CheckResult<AnyCond> checked = check(
+ cond,
+ realmUtils.getField(cond.getSchema()).
+ orElseThrow(() -> new
IllegalArgumentException("Invalid schema " + cond.getSchema())),
+ RELATIONSHIP_FIELDS);
+ return fillAttrQuery(checked.schema(), checked.value(),
checked.cond());
+ }
+
+ protected Query getQueryForCustomConds(final SearchCond cond) {
Review Comment:
## Useless parameter
The parameter 'cond' is never used.
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2566)
##########
core/persistence-jpa/src/main/java/org/apache/syncope/core/persistence/jpa/dao/AbstractJPARealmSearchDAO.java:
##########
@@ -0,0 +1,537 @@
+/*
+ * 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.NoResultException;
+import jakarta.persistence.Query;
+import jakarta.persistence.TypedQuery;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.common.lib.SyncopeConstants;
+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.MalformedPathException;
+import org.apache.syncope.core.persistence.api.dao.PlainSchemaDAO;
+import org.apache.syncope.core.persistence.api.dao.RealmDAO;
+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.dao.search.SearchCond;
+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.apache.syncope.core.persistence.api.utils.RealmUtils;
+import org.apache.syncope.core.persistence.common.dao.AbstractRealmSearchDAO;
+import org.apache.syncope.core.persistence.jpa.entity.JPARealm;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.transaction.annotation.Transactional;
+
+public abstract class AbstractJPARealmSearchDAO extends AbstractRealmSearchDAO
{
+
+ protected record QueryInfo(RealmSearchNode node, Set<String> plainSchemas)
{
+
+ }
+
+ protected record AttrCondQuery(Boolean addPlainSchemas, RealmSearchNode
node) {
+
+ }
+
+ protected static int setParameter(final List<Object> parameters, final
Object parameter) {
+ parameters.add(parameter);
+ return parameters.size();
+ }
+
+ protected static void fillWithParameters(final Query query, final
List<Object> parameters) {
+ for (int i = 0; i < parameters.size(); i++) {
+ if (parameters.get(i) instanceof Boolean aBoolean) {
+ query.setParameter(i + 1, aBoolean ? 1 : 0);
+ } else {
+ query.setParameter(i + 1, parameters.get(i));
+ }
+ }
+ }
+
+ protected final EntityManager entityManager;
+
+ protected final RealmUtils realmUtils;
+
+ protected AbstractJPARealmSearchDAO(
+ final EntityManager entityManager,
+ final PlainSchemaDAO plainSchemaDAO,
+ final EntityFactory entityFactory,
+ final PlainAttrValidationManager validator,
+ final RealmUtils realmUtils) {
+
+ super(plainSchemaDAO, entityFactory, validator);
+
+ this.entityManager = entityManager;
+ this.realmUtils = realmUtils;
+ }
+
+ @Transactional(readOnly = true)
+ @Override
+ public Optional<Realm> findByFullPath(final String fullPath) {
+ if (StringUtils.isBlank(fullPath)
+ || (!SyncopeConstants.ROOT_REALM.equals(fullPath)
+ && !RealmDAO.PATH_PATTERN.matcher(fullPath).matches())) {
+
+ throw new MalformedPathException(fullPath);
+ }
+
+ TypedQuery<Realm> query = entityManager.createQuery(
+ "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE
e.fullPath=:fullPath", Realm.class);
+ query.setParameter("fullPath", fullPath);
+
+ Realm result = null;
+ try {
+ result = query.getSingleResult();
+ } catch (NoResultException e) {
+ LOG.debug("Realm with fullPath {} not found", fullPath, e);
+ }
+
+ return Optional.ofNullable(result);
+ }
+
+ @Override
+ public List<Realm> findByName(final String name) {
+ TypedQuery<Realm> query = entityManager.createQuery(
+ "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE
e.name=:name", Realm.class);
+ query.setParameter("name", name);
+
+ return query.getResultList();
+ }
+
+ @Override
+ public List<Realm> findChildren(final Realm realm) {
+ TypedQuery<Realm> query = entityManager.createQuery(
+ "SELECT e FROM " + JPARealm.class.getSimpleName() + " e WHERE
e.parent=:realm", Realm.class);
+ query.setParameter("realm", realm);
+
+ return query.getResultList();
+ }
+
+ @Override
+ public List<Realm> findDescendants(final String base, final String prefix)
{
+ List<Object> parameters = new ArrayList<>();
+
+ StringBuilder queryString = new StringBuilder("SELECT e FROM ").
+ append(JPARealm.class.getSimpleName()).
+ append(" e WHERE
(e.fullPath=?").append(setParameter(parameters, base)).
+ append(" OR e.fullPath LIKE ?").append(setParameter(
+ parameters,
+ SyncopeConstants.ROOT_REALM.equals(base) ? "/%" : base +
"/%")).
+ append(") ");
+ if (prefix != null) {
+ queryString.append("AND
(e.fullPath=?").append(setParameter(parameters, prefix)).
+ append(" OR e.fullPath LIKE ?").append(setParameter(
+ parameters,
+ SyncopeConstants.ROOT_REALM.equals(prefix) ? "/%" : prefix
+ "/%")).
+ append(") ");
+ }
+ queryString.append("ORDER BY e.fullPath");
+
+ TypedQuery<Realm> query =
entityManager.createQuery(queryString.toString(), Realm.class);
+
+ fillWithParameters(query, parameters);
+
+ return query.getResultList();
+ }
+
+ // ------------------------------------------ //
+ protected Optional<RealmSearchNode> getQueryForCustomConds(
+ final SearchCond cond,
+ final boolean not,
+ final List<Object> parameters) {
+
+ return Optional.empty();
+ }
+
+ protected Optional<QueryInfo> getQuery(final SearchCond cond, final
List<Object> parameters) {
+ if (cond == null) {
+ return Optional.empty();
+ }
+
+ boolean not = cond.getType() == SearchCond.Type.NOT_LEAF;
+
+ Optional<RealmSearchNode> node = Optional.empty();
+ Set<String> plainSchemas = new HashSet<>();
+
+ switch (cond.getType()) {
+ case LEAF:
+ case NOT_LEAF:
+ node = cond.asLeaf(AnyCond.class).
+ map(anyCond -> getQuery(anyCond, not, parameters)).
+ or(() -> cond.asLeaf(AttrCond.class).
+ map(attrCond -> {
+ CheckResult<AttrCond> checked = check(attrCond);
+ AttrCondQuery query = getQuery(attrCond, not,
checked, parameters);
+ if (query.addPlainSchemas()) {
+ plainSchemas.add(checked.schema().getKey());
+ }
+ return query.node();
+ }));
+
+ if (node.isEmpty()) {
+ node = getQueryForCustomConds(cond, not, parameters);
+ }
+ break;
+
+ case AND:
+ RealmSearchNode andNode = new
RealmSearchNode(RealmSearchNode.Type.AND);
+
+ getQuery(cond.getLeft(), parameters).ifPresent(left -> {
+ andNode.add(left.node());
+ plainSchemas.addAll(left.plainSchemas());
+ });
+
+ getQuery(cond.getRight(), parameters).ifPresent(right -> {
+ andNode.add(right.node());
+ plainSchemas.addAll(right.plainSchemas());
+ });
+
+ if (!andNode.getChildren().isEmpty()) {
+ node = Optional.of(andNode);
+ }
+ break;
+
+ case OR:
+ RealmSearchNode orNode = new
RealmSearchNode(RealmSearchNode.Type.OR);
+
+ getQuery(cond.getLeft(), parameters).ifPresent(left -> {
+ orNode.add(left.node());
+ plainSchemas.addAll(left.plainSchemas());
+ });
+
+ getQuery(cond.getRight(), parameters).ifPresent(right -> {
+ orNode.add(right.node());
+ plainSchemas.addAll(right.plainSchemas());
+ });
+
+ if (!orNode.getChildren().isEmpty()) {
+ node = Optional.of(orNode);
+ }
+ break;
+
+ default:
+ }
+
+ return node.map(n -> new QueryInfo(n, plainSchemas));
+ }
+
+ protected abstract AttrCondQuery getQuery(
+ AttrCond cond,
+ boolean not,
+ CheckResult<AttrCond> checked,
+ List<Object> parameters);
+
+ protected RealmSearchNode getQuery(final AnyCond cond, final boolean not,
final List<Object> parameters) {
+ CheckResult<AnyCond> checked = check(
+ cond,
+ realmUtils.getField(cond.getSchema()).
+ orElseThrow(() -> new
IllegalArgumentException("Invalid schema " + cond.getSchema())),
+ RELATIONSHIP_FIELDS);
+
+ return switch (checked.cond().getType()) {
+ case ISNULL ->
+ new RealmSearchNode.Leaf("r." + checked.cond().getSchema() +
(not ? " IS NOT NULL" : " IS NULL"));
+
+ case ISNOTNULL ->
+ new RealmSearchNode.Leaf("r." + checked.cond().getSchema() +
(not ? " IS NULL" : " IS NOT NULL"));
+
+ default ->
+ fillAttrQuery(
+ "r." + checked.cond().getSchema(),
+ checked.value(),
+ checked.schema(),
+ checked.cond(),
+ not,
+ parameters);
+ };
+ }
+
+ protected RealmSearchNode.Leaf fillAttrQuery(
+ final String column,
+ final PlainAttrValue attrValue,
+ final PlainSchema schema,
+ final AttrCond cond,
+ final boolean not,
+ final List<Object> parameters) {
+
+ boolean ignoreCase = AttrCond.Type.ILIKE == cond.getType() ||
AttrCond.Type.IEQ == cond.getType();
+
+ String left = column;
+ if (ignoreCase && (schema.getType() == AttrSchemaType.String ||
schema.getType() == AttrSchemaType.Enum)) {
+ left = "LOWER(" + left + ')';
+ }
+
+ StringBuilder clause = new StringBuilder(left);
+ switch (cond.getType()) {
+
+ 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 (this instanceof OracleJPARealmSearchDAO) {
+ clause.append(" ESCAPE '\\'");
+ }
+ } else {
+ LOG.error("LIKE is only compatible with string or enum
schemas");
+ return new RealmSearchNode.Leaf(ALWAYS_FALSE_CLAUSE);
+ }
+ break;
+
+ case IEQ:
+ case EQ:
+ default:
+ clause.append(not ? "<>" : "=");
+ if (ignoreCase
+ && (schema.getType() == AttrSchemaType.String ||
schema.getType() == AttrSchemaType.Enum)) {
+ clause.append("LOWER(?").append(setParameter(parameters,
attrValue.getValue())).append(')');
+ } else {
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ }
+ break;
+
+ case GE:
+ clause.append(not ? "<" : ">=");
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ break;
+
+ case GT:
+ clause.append(not ? "<=" : ">");
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ break;
+
+ case LE:
+ clause.append(not ? ">" : "<=");
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ break;
+
+ case LT:
+ clause.append(not ? ">=" : "<");
+ clause.append('?').append(setParameter(parameters,
attrValue.getValue()));
+ break;
+ }
+
+ return new RealmSearchNode.Leaf(
+ cond instanceof AnyCond
+ ? clause.toString()
+ : "r.schema_id='" + schema.getKey() + "' AND " +
clause);
+ }
+
+ protected void visitNode(final RealmSearchNode node, final List<String>
where) {
+ node.asLeaf().ifPresentOrElse(
+ leaf -> where.add(leaf.getClause()),
+ () -> {
+ List<String> nodeWhere = new ArrayList<>();
+ node.getChildren().forEach(child -> visitNode(child,
nodeWhere));
+ String op = " " + node.getType().name() + " ";
+ where.add(nodeWhere.stream().
+ map(w -> w.contains(" AND ") || w.contains(" OR ")
? "(" + w + ")" : w).
+ collect(Collectors.joining(op)));
+ });
+ }
+
+ protected String buildFrom(final Set<String> plainSchemas, final
OrderBySupport obs) {
+ return JPARealm.TABLE + " r";
+ }
+
+ protected String buildWhere(final Set<String> bases, final QueryInfo
queryInfo, final List<Object> parameters) {
+ String fullPaths = bases.stream().
+ map(base -> "r.fullPath=?" + setParameter(parameters, base)
+ + " OR r.fullPath LIKE ?" + setParameter(
+ parameters, SyncopeConstants.ROOT_REALM.equals(base) ?
"/%" : base + "/%")).
+ collect(Collectors.joining(" OR "));
+
+ RealmSearchNode root;
+ if (queryInfo.node().getType() == RealmSearchNode.Type.AND) {
+ root = queryInfo.node();
+ } else {
+ root = new RealmSearchNode(RealmSearchNode.Type.AND);
+ root.add(queryInfo.node());
+ }
+
+ List<String> where = new ArrayList<>();
+ visitNode(root, where);
+
+ return "(" + fullPaths + ')'
+ + " AND (" + where.stream().
+ map(w -> w.contains(" AND ") || w.contains(" OR ") ?
"(" + w + ")" : w).
+ collect(Collectors.joining(' ' + root.getType().name()
+ ' '))
+ + ')';
+ }
+
+ @Override
+ protected long doCount(final Set<String> bases, final SearchCond cond) {
+ List<Object> parameters = new ArrayList<>();
+
+ QueryInfo queryInfo = getQuery(cond, parameters).orElse(null);
+ if (queryInfo == null) {
+ LOG.error("Invalid search condition: {}", cond);
+ return 0;
+ }
+
+ String queryString = new StringBuilder("SELECT COUNT(DISTINCT r.id)").
+ append(" FROM ").append(buildFrom(queryInfo.plainSchemas(),
null)).
+ append(" WHERE ").append(buildWhere(bases, queryInfo,
parameters)).
+ toString();
+
+ LOG.debug("Query: {}, parameters: {}", queryString, parameters);
+
+ Query query = entityManager.createNativeQuery(queryString);
+ fillWithParameters(query, parameters);
+
+ return ((Number) query.getSingleResult()).longValue();
+ }
+
+ protected abstract void parseOrderByForPlainSchema(
+ OrderBySupport obs,
+ OrderBySupport.Item item,
+ Sort.Order clause,
+ PlainSchema schema,
+ String fieldName);
+
+ protected void parseOrderByForField(
+ final OrderBySupport.Item item,
+ final String fieldName,
+ final Sort.Order clause) {
+
+ item.select = "r." + fieldName;
+ item.where = StringUtils.EMPTY;
+ item.orderBy = "r." + fieldName + ' ' + clause.getDirection().name();
+ }
+
+ protected void parseOrderByForCustom(
+ final Sort.Order clause,
+ final OrderBySupport.Item item,
+ final OrderBySupport obs) {
Review Comment:
## Useless parameter
The parameter 'obs' is never used.
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2569)
##########
ext/opensearch/persistence/src/main/java/org/apache/syncope/core/persistence/opensearch/dao/OpenSearchRealmSearchDAO.java:
##########
@@ -161,53 +188,252 @@
});
Query prefix = new
Query.Builder().disMax(QueryBuilders.disMax().queries(basesQueries).build()).build();
- if (keyword == null) {
- return prefix;
+ BoolQuery.Builder boolBuilder = QueryBuilders.bool().filter(prefix);
+ if (searchCond != null) {
+ boolBuilder.filter(getQuery(searchCond));
}
+ return new Query.Builder().bool(boolBuilder.build()).build();
+ }
- return new Query.Builder().bool(QueryBuilders.bool().filter(
- prefix,
- new Query.Builder().wildcard(QueryBuilders.wildcard().
- field("name").value(keyword.replace('%',
'*').replace("\\_", "_")).
- caseInsensitive(true).build()).build()).build()).
- build();
+ protected Query getQuery(final SearchCond cond) {
+ Query query = null;
+
+ switch (cond.getType()) {
+ case LEAF, NOT_LEAF -> {
+ query =
cond.asLeaf(AnyCond.class).map(this::getQuery).orElse(null);
+ if (query == null) {
+ query =
cond.asLeaf(AttrCond.class).map(this::getQuery).orElse(null);
+ }
+ if (query == null) {
+ query = getQueryForCustomConds(cond);
+ }
+ if (query == null) {
+ throw new IllegalArgumentException("Cannot construct
QueryBuilder");
+ }
+ if (cond.getType() == SearchCond.Type.NOT_LEAF) {
+ query = new
Query.Builder().bool(QueryBuilders.bool().mustNot(query).build()).build();
+ }
+ }
+ case AND -> {
+ List<Query> andCompound = new ArrayList<>();
+ Query andLeft = getQuery(cond.getLeft());
+ if (andLeft.isBool() && !andLeft.bool().filter().isEmpty()) {
+ andCompound.addAll(andLeft.bool().filter());
+ } else {
+ andCompound.add(andLeft);
+ }
+ Query andRight = getQuery(cond.getRight());
+ if (andRight.isBool() && !andRight.bool().filter().isEmpty()) {
+ andCompound.addAll(andRight.bool().filter());
+ } else {
+ andCompound.add(andRight);
+ }
+ query = new
Query.Builder().bool(QueryBuilders.bool().filter(andCompound).build()).build();
+ }
+ case OR -> {
+ List<Query> orCompound = new ArrayList<>();
+ Query orLeft = getQuery(cond.getLeft());
+ if (orLeft.isDisMax()) {
+ orCompound.addAll(orLeft.disMax().queries());
+ } else {
+ orCompound.add(orLeft);
+ }
+ Query orRight = getQuery(cond.getRight());
+ if (orRight.isDisMax()) {
+ orCompound.addAll(orRight.disMax().queries());
+ } else {
+ orCompound.add(orRight);
+ }
+ query = new
Query.Builder().disMax(QueryBuilders.disMax().queries(orCompound).build()).build();
+ }
+ default -> {
+ }
+ }
+
+ return query;
}
@Override
- public long countDescendants(final String base, final String keyword) {
- return countDescendants(Set.of(base), keyword);
+ protected CheckResult<AnyCond> check(final AnyCond cond, final Field
field, final Set<String> relationshipsFields) {
+ CheckResult<AnyCond> checked = super.check(cond, field,
relationshipsFields);
+
+ // Manage difference between external id attribute and internal _id
+ if ("id".equals(checked.cond().getSchema())) {
+ checked.cond().setSchema("_id");
+ }
+ if ("id".equals(checked.schema().getKey())) {
+ checked.schema().setKey("_id");
+ }
+
+ return checked;
+ }
+
+ protected Query fillAttrQuery(
+ final PlainSchema schema,
+ final PlainAttrValue attrValue,
+ final AttrCond cond) {
+
+ Object value = schema.getType() == AttrSchemaType.Date &&
attrValue.getDateValue() != null
+ ? FormatUtils.format(attrValue.getDateValue())
+ : attrValue.getValue();
+
+ Query query = null;
+ switch (cond.getType()) {
+ case ISNOTNULL:
+ query = new
Query.Builder().exists(QueryBuilders.exists().field(schema.getKey()).build()).build();
+ break;
+
+ case ISNULL:
+ query = new Query.Builder().bool(QueryBuilders.bool().mustNot(
+ new
Query.Builder().exists(QueryBuilders.exists().field(schema.getKey()).build())
+ .build()).build()).build();
+ break;
+
+ case ILIKE:
+ query = new Query.Builder().wildcard(QueryBuilders.wildcard().
+
field(schema.getKey()).value(cond.getExpression().replace('%',
'*').replace("\\_", "_")).
+ caseInsensitive(true).build()).build();
+ break;
+
+ case LIKE:
+ query = new Query.Builder().wildcard(QueryBuilders.wildcard().
+
field(schema.getKey()).value(cond.getExpression().replace('%',
'*').replace("\\_", "_")).
+ caseInsensitive(false).build()).build();
+ break;
+
+ case IEQ:
+ query = new Query.Builder().term(QueryBuilders.term().
+
field(schema.getKey()).value(FieldValue.of(cond.getExpression())).caseInsensitive(true).
+ build()).build();
+ break;
+
+ case EQ:
+ FieldValue fieldValue = switch (value) {
+ case Double aDouble ->
+ FieldValue.of(aDouble);
+ case Long aLong ->
+ FieldValue.of(aLong);
+ case Boolean aBoolean ->
+ FieldValue.of(aBoolean);
+ default ->
+ FieldValue.of(value.toString());
+ };
+ query = new Query.Builder().term(QueryBuilders.term().
+
field(schema.getKey()).value(fieldValue).caseInsensitive(false).build()).
+ build();
+ break;
+
+ case GE:
+ query = new Query.Builder().range(QueryBuilders.range().
+
field(schema.getKey()).gte(JsonData.of(value)).build()).
+ build();
+ break;
+
+ case GT:
+ query = new Query.Builder().range(QueryBuilders.range().
+ field(schema.getKey()).gt(JsonData.of(value)).build()).
+ build();
+ break;
+
+ case LE:
+ query = new Query.Builder().range(QueryBuilders.range().
+
field(schema.getKey()).lte(JsonData.of(value)).build()).
+ build();
+ break;
+
+ case LT:
+ query = new Query.Builder().range(QueryBuilders.range().
+ field(schema.getKey()).lt(JsonData.of(value)).build()).
+ build();
+ break;
+
+ default:
+ break;
+ }
+
+ return query;
+ }
+
+ protected Query getQuery(final AttrCond cond) {
+ CheckResult<AttrCond> checked = check(cond);
+ return fillAttrQuery(checked.schema(), checked.value(), cond);
+ }
+
+ protected Query getQuery(final AnyCond cond) {
+ CheckResult<AnyCond> checked = check(
+ cond,
+ realmUtils.getField(cond.getSchema()).
+ orElseThrow(() -> new
IllegalArgumentException("Invalid schema " + cond.getSchema())),
+ RELATIONSHIP_FIELDS);
+ return fillAttrQuery(checked.schema(), checked.value(),
checked.cond());
+ }
+
+ protected Query getQueryForCustomConds(final SearchCond cond) {
Review Comment:
## Useless parameter
The parameter 'cond' is never used.
[Show more
details](https://github.com/apache/syncope/security/code-scanning/2570)
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]