You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@syncope.apache.org by il...@apache.org on 2025/11/17 11:36:36 UTC
(syncope) branch master updated: [SYNCOPE-1931] Allow for JEXL custom functions
This is an automated email from the ASF dual-hosted git repository.
ilgrosso pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/syncope.git
The following commit(s) were added to refs/heads/master by this push:
new 745d9b6f39 [SYNCOPE-1931] Allow for JEXL custom functions
745d9b6f39 is described below
commit 745d9b6f390657a61f4bd3b6047c138cde55976f
Author: Francesco Chicchiriccò <il...@apache.org>
AuthorDate: Mon Nov 17 12:10:18 2025 +0100
[SYNCOPE-1931] Allow for JEXL custom functions
---
.../syncope/core/logic/AbstractAnyLogic.java | 2 +-
.../apache/syncope/core/logic/AnyObjectLogic.java | 2 +-
.../org/apache/syncope/core/logic/GroupLogic.java | 2 +-
.../syncope/core/logic/IdRepoLogicContext.java | 2 +-
.../org/apache/syncope/core/logic/UserLogic.java | 2 +-
.../provisioning/api/jexl/EmptyClassLoader.java | 2 +-
.../{JexlUtils.java => JexlContextBuilder.java} | 134 ++--------
.../{EmptyClassLoader.java => JexlFunctions.java} | 15 +-
.../core/provisioning/api/jexl/JexlTools.java | 116 ++++++++
.../api/jexl/SyncopeJexlFunctions.java | 7 +-
.../core/provisioning/api/jexl}/TemplateUtils.java | 107 ++++----
.../core/provisioning/api/AbstractTest.java | 19 ++
.../core/provisioning/api/jexl/JexlUtilsTest.java | 72 +++--
.../provisioning/api/jexl/MailTemplateTest.java | 6 +-
.../core/provisioning/api/jexl/MappingTest.java | 19 +-
.../provisioning/java/DefaultDerAttrHandler.java | 43 +--
.../provisioning/java/DefaultMappingManager.java | 28 +-
.../provisioning/java/ProvisioningContext.java | 110 ++++++--
.../core/provisioning/java/data/AnyDataBinder.java | 9 +-
.../java/data/AnyObjectDataBinderImpl.java | 7 +-
.../java/data/AttrRepoDataBinderImpl.java | 12 +-
.../java/data/AttributableDataBinder.java | 10 +-
.../java/data/AuthModuleDataBinderImpl.java | 12 +-
.../java/data/GroupDataBinderImpl.java | 7 +-
.../java/data/JEXLItemTransformerImpl.java | 29 +-
.../java/data/RealmDataBinderImpl.java | 17 +-
.../java/data/ResourceDataBinderImpl.java | 18 +-
.../java/data/SchemaDataBinderImpl.java | 12 +-
.../provisioning/java/data/TaskDataBinderImpl.java | 12 +-
.../provisioning/java/data/UserDataBinderImpl.java | 7 +-
.../provisioning/java/job/MacroJobDelegate.java | 10 +-
.../notification/DefaultNotificationManager.java | 14 +-
.../propagation/DefaultPropagationManager.java | 10 +-
.../LDAPMembershipPropagationActions.java | 18 +-
.../java/pushpull/SinglePullJobDelegate.java | 7 +-
.../provisioning/java/utils/ConnObjectUtils.java | 1 +
.../syncope/core/logic/OIDCC4UILogicContext.java | 2 +-
.../syncope/core/logic/oidc/OIDCUserManager.java | 2 +-
.../java/data/OIDCC4UIProviderDataBinderImpl.java | 13 +-
.../java/data/OIDCC4UIProvisioningContext.java | 7 +-
.../syncope/core/logic/SAML2SP4UILogicContext.java | 2 +-
.../core/logic/saml2/SAML2SP4UIUserManager.java | 2 +-
.../java/SAML2SP4UIProvisioningContext.java | 7 +-
.../java/data/SAML2SP4UIIdPDataBinderImpl.java | 13 +-
.../apache/syncope/core/logic/SCIMDataBinder.java | 294 +++++++++++----------
.../syncope/core/logic/SCIMLogicContext.java | 6 +-
.../syncope/core/logic/SCIMDataBinderTest.java | 113 ++++----
.../ext/scimv2/cxf/SCIMv2RESTCXFContext.java | 50 +++-
.../scimv2/cxf/service/SCIMGroupServiceImpl.java | 10 +-
pom.xml | 4 +-
.../reference-guide/concepts/implementations.adoc | 2 +-
src/main/asciidoc/reference-guide/usage/core.adoc | 18 +-
52 files changed, 843 insertions(+), 602 deletions(-)
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
index 260df311e4..aca80f1b82 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AbstractAnyLogic.java
@@ -38,7 +38,7 @@ import org.apache.syncope.core.persistence.api.dao.RealmSearchDAO;
import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
import org.apache.syncope.core.persistence.api.entity.AnyType;
import org.apache.syncope.core.persistence.api.entity.Realm;
-import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.spring.implementation.ImplementationManager;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
index fe248c1709..7f9ec30d89 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/AnyObjectLogic.java
@@ -53,7 +53,7 @@ import org.apache.syncope.core.persistence.api.utils.RealmUtils;
import org.apache.syncope.core.provisioning.api.AnyObjectProvisioningManager;
import org.apache.syncope.core.provisioning.api.ProvisioningManager;
import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
-import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
index d59b1be994..1acd1039ff 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/GroupLogic.java
@@ -62,10 +62,10 @@ import org.apache.syncope.core.provisioning.api.GroupProvisioningManager;
import org.apache.syncope.core.provisioning.api.ProvisioningManager;
import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.provisioning.api.job.JobManager;
import org.apache.syncope.core.provisioning.java.job.GroupMemberProvisionTaskJobDelegate;
import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler;
-import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.spring.security.SecurityProperties;
import org.springframework.data.domain.Page;
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java
index 685174bbe6..4d20214dc1 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/IdRepoLogicContext.java
@@ -86,6 +86,7 @@ import org.apache.syncope.core.provisioning.api.data.SchemaDataBinder;
import org.apache.syncope.core.provisioning.api.data.SecurityQuestionDataBinder;
import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.provisioning.api.job.JobManager;
import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
@@ -93,7 +94,6 @@ import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
import org.apache.syncope.core.provisioning.api.rules.RuleProvider;
import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler;
-import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
import org.apache.syncope.core.spring.security.SecurityProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
index 02f9c35f44..87a9fecdac 100644
--- a/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
+++ b/core/idrepo/logic/src/main/java/org/apache/syncope/core/logic/UserLogic.java
@@ -72,9 +72,9 @@ import org.apache.syncope.core.persistence.api.utils.RealmUtils;
import org.apache.syncope.core.provisioning.api.ProvisioningManager;
import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.provisioning.api.rules.RuleProvider;
import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
-import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
import org.apache.syncope.core.spring.policy.AccountPolicyException;
import org.apache.syncope.core.spring.policy.PasswordPolicyException;
import org.apache.syncope.core.spring.security.AuthContextUtils;
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/EmptyClassLoader.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/EmptyClassLoader.java
index d6db5edc15..803f8a245c 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/EmptyClassLoader.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/EmptyClassLoader.java
@@ -21,7 +21,7 @@ package org.apache.syncope.core.provisioning.api.jexl;
/**
* A class loader that will throw {@link ClassNotFoundException} for every class name.
*/
-class EmptyClassLoader extends ClassLoader {
+public class EmptyClassLoader extends ClassLoader {
@Override
public Class<?> loadClass(final String name) throws ClassNotFoundException {
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlContextBuilder.java
similarity index 62%
rename from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java
rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlContextBuilder.java
index d11a0cc30c..269ec784e8 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtils.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlContextBuilder.java
@@ -21,7 +21,6 @@ package org.apache.syncope.core.provisioning.api.jexl;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
-import java.io.StringWriter;
import java.lang.reflect.Field;
import java.time.temporal.TemporalAccessor;
import java.util.Collection;
@@ -30,16 +29,9 @@ 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 org.apache.commons.jexl3.JexlBuilder;
import org.apache.commons.jexl3.JexlContext;
-import org.apache.commons.jexl3.JexlEngine;
-import org.apache.commons.jexl3.JexlException;
-import org.apache.commons.jexl3.JexlExpression;
-import org.apache.commons.jexl3.JxltEngine;
import org.apache.commons.jexl3.MapContext;
-import org.apache.commons.jexl3.introspection.JexlPermissions;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
@@ -58,101 +50,25 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;
-/**
- * JEXL <a href="http://commons.apache.org/jexl/reference/index.html">reference</a> is available.
- */
-@SuppressWarnings({ "squid:S3008", "squid:S3776", "squid:S1141" })
-public final class JexlUtils {
+public class JexlContextBuilder {
- private static final Logger LOG = LoggerFactory.getLogger(JexlUtils.class);
+ protected static final Logger LOG = LoggerFactory.getLogger(JexlContextBuilder.class);
private static final String[] IGNORE_FIELDS = { "password", "clearPassword", "serialVersionUID", "class" };
private static final Map<Class<?>, Set<Pair<PropertyDescriptor, Field>>> FIELD_CACHE =
Collections.synchronizedMap(new HashMap<>());
- private static JexlEngine JEXL_ENGINE;
-
- private static JxltEngine JXTL_ENGINE;
-
- private static JexlEngine getJexlEngine() {
- synchronized (LOG) {
- if (JEXL_ENGINE == null) {
- JEXL_ENGINE = new JexlBuilder().
- loader(new EmptyClassLoader()).
- permissions(JexlPermissions.RESTRICTED.compose("java.time.*", "org.apache.syncope.*")).
- namespaces(Map.of("syncope", new SyncopeJexlFunctions())).
- cache(512).
- silent(false).
- strict(false).
- create();
- }
- }
-
- return JEXL_ENGINE;
- }
-
- private static JxltEngine getJxltEngine() {
- synchronized (LOG) {
- if (JXTL_ENGINE == null) {
- JXTL_ENGINE = getJexlEngine().createJxltEngine(false);
- }
- }
-
- return JXTL_ENGINE;
- }
-
- public static boolean isExpressionValid(final String expression) {
- boolean result;
- try {
- getJexlEngine().createExpression(expression);
- result = true;
- } catch (JexlException e) {
- LOG.error("Invalid JEXL expression: {}", expression, e);
- result = false;
- }
+ protected final JexlContext jexlContext = new MapContext();
- return result;
+ public JexlContextBuilder with(final String name, final Object value) {
+ jexlContext.set(name, value);
+ return this;
}
- public static Object evaluateExpr(final String expression, final JexlContext jexlContext) {
- Object result = null;
-
- if (StringUtils.isNotBlank(expression) && jexlContext != null) {
- try {
- JexlExpression jexlExpression = getJexlEngine().createExpression(expression);
- result = jexlExpression.evaluate(jexlContext);
- } catch (Exception e) {
- LOG.error("Error while evaluating JEXL expression: {}", expression, e);
- }
- } else {
- LOG.debug("Expression not provided or invalid context");
- }
-
- return Optional.ofNullable(result).orElse(StringUtils.EMPTY);
- }
-
- public static String evaluateTemplate(final String template, final JexlContext jexlContext) {
- String result = null;
-
- if (StringUtils.isNotBlank(template) && jexlContext != null) {
- try {
- StringWriter writer = new StringWriter();
- getJxltEngine().createTemplate(template).evaluate(jexlContext, writer);
- result = writer.toString();
- } catch (Exception e) {
- LOG.error("Error while evaluating JEXL template: {}", template, e);
- }
- } else {
- LOG.debug("Template not provided or invalid context");
- }
-
- return Optional.ofNullable(result).orElse(template);
- }
-
- public static void addFieldsToContext(final Object object, final JexlContext jexlContext) {
+ public JexlContextBuilder fields(final Object object) {
if (object == null) {
- return;
+ return this;
}
Set<Pair<PropertyDescriptor, Field>> cached = FIELD_CACHE.get(object.getClass());
@@ -227,9 +143,11 @@ public final class JexlUtils {
} else if (object instanceof RealmTO realmTO) {
jexlContext.set("fullPath", realmTO.getFullPath());
}
+
+ return this;
}
- public static void addAttrsToContext(final Collection<Attr> attrs, final JexlContext jexlContext) {
+ public JexlContextBuilder attrs(final Collection<Attr> attrs) {
attrs.stream().filter(attr -> attr.getSchema() != null).forEach(attr -> {
Object value;
if (attr.getValues().isEmpty()) {
@@ -244,9 +162,11 @@ public final class JexlUtils {
jexlContext.set(attr.getSchema(), value);
});
+
+ return this;
}
- public static void addPlainAttrsToContext(final Collection<PlainAttr> attrs, final JexlContext jexlContext) {
+ public JexlContextBuilder plainAttrs(final Collection<PlainAttr> attrs) {
attrs.stream().filter(attr -> attr.getSchema() != null).forEach(attr -> {
List<String> attrValues = attr.getValuesAsStrings();
Object value;
@@ -262,13 +182,11 @@ public final class JexlUtils {
jexlContext.set(attr.getSchema(), value);
});
- }
- public static void addDerAttrsToContext(
- final Attributable attributable,
- final DerAttrHandler derAttrHandler,
- final JexlContext jexlContext) {
+ return this;
+ }
+ public JexlContextBuilder derAttrs(final Attributable attributable, final DerAttrHandler derAttrHandler) {
Map<DerSchema, String> derAttrs = attributable instanceof Realm realm
? derAttrHandler.getValues(realm)
: attributable instanceof Any any
@@ -276,23 +194,11 @@ public final class JexlUtils {
: Map.of();
derAttrs.forEach((schema, value) -> jexlContext.set(schema.getKey(), value));
- }
-
- public static boolean evaluateMandatoryCondition(
- final String mandatoryCondition,
- final Attributable attributable,
- final DerAttrHandler derAttrHandler) {
-
- JexlContext jexlContext = new MapContext();
- addPlainAttrsToContext(attributable.getPlainAttrs(), jexlContext);
- addDerAttrsToContext(attributable, derAttrHandler, jexlContext);
- return Boolean.parseBoolean(evaluateExpr(mandatoryCondition, jexlContext).toString());
+ return this;
}
- /**
- * Private default constructor, for static-only classes.
- */
- private JexlUtils() {
+ public JexlContext build() {
+ return jexlContext;
}
}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/EmptyClassLoader.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlFunctions.java
similarity index 61%
copy from core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/EmptyClassLoader.java
copy to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlFunctions.java
index d6db5edc15..56fd253091 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/EmptyClassLoader.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlFunctions.java
@@ -18,18 +18,7 @@
*/
package org.apache.syncope.core.provisioning.api.jexl;
-/**
- * A class loader that will throw {@link ClassNotFoundException} for every class name.
- */
-class EmptyClassLoader extends ClassLoader {
-
- @Override
- public Class<?> loadClass(final String name) throws ClassNotFoundException {
- throw new ClassNotFoundException("This classloader won't attempt to load " + name);
- }
+public interface JexlFunctions {
- @Override
- protected Class<?> loadClass(final String name, final boolean resolve) throws ClassNotFoundException {
- throw new ClassNotFoundException("This classloader won't attempt to load " + name);
- }
+ String getNamespace();
}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlTools.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlTools.java
new file mode 100644
index 0000000000..e34df5b5ba
--- /dev/null
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlTools.java
@@ -0,0 +1,116 @@
+/*
+ * 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.provisioning.api.jexl;
+
+import java.io.StringWriter;
+import java.util.Optional;
+import org.apache.commons.jexl3.JexlContext;
+import org.apache.commons.jexl3.JexlEngine;
+import org.apache.commons.jexl3.JexlException;
+import org.apache.commons.jexl3.JexlExpression;
+import org.apache.commons.jexl3.JxltEngine;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.syncope.core.persistence.api.entity.Attributable;
+import org.apache.syncope.core.provisioning.api.DerAttrHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * JEXL <a href="http://commons.apache.org/jexl/reference/index.html">reference</a> is available.
+ */
+@SuppressWarnings({ "squid:S3008", "squid:S3776", "squid:S1141" })
+public class JexlTools {
+
+ protected static final Logger LOG = LoggerFactory.getLogger(JexlTools.class);
+
+ protected final JexlEngine jexlEngine;
+
+ protected final JxltEngine jxltEngine;
+
+ public JexlTools(final JexlEngine jexlEngine) {
+ this.jexlEngine = jexlEngine;
+ this.jxltEngine = jexlEngine.createJxltEngine(false);
+ }
+
+ public JexlTools(final JexlEngine jexlEngine, final JxltEngine jxltEngine) {
+ this.jexlEngine = jexlEngine;
+ this.jxltEngine = jxltEngine;
+ }
+
+ public boolean isExpressionValid(final String expression) {
+ boolean result;
+ try {
+ jexlEngine.createExpression(expression);
+ result = true;
+ } catch (JexlException e) {
+ LOG.error("Invalid JEXL expression: {}", expression, e);
+ result = false;
+ }
+
+ return result;
+ }
+
+ public Object evaluateExpression(final String expression, final JexlContext jexlContext) {
+ Object result = null;
+
+ if (StringUtils.isNotBlank(expression) && jexlContext != null) {
+ try {
+ JexlExpression jexlExpression = jexlEngine.createExpression(expression);
+ result = jexlExpression.evaluate(jexlContext);
+ } catch (Exception e) {
+ LOG.error("Error while evaluating JEXL expression: {}", expression, e);
+ }
+ } else {
+ LOG.debug("Expression not provided or invalid context");
+ }
+
+ return Optional.ofNullable(result).orElse(StringUtils.EMPTY);
+ }
+
+ public String evaluateTemplate(final String template, final JexlContext jexlContext) {
+ String result = null;
+
+ if (StringUtils.isNotBlank(template) && jexlContext != null) {
+ try {
+ StringWriter writer = new StringWriter();
+ jxltEngine.createTemplate(template).evaluate(jexlContext, writer);
+ result = writer.toString();
+ } catch (Exception e) {
+ LOG.error("Error while evaluating JEXL template: {}", template, e);
+ }
+ } else {
+ LOG.debug("Template not provided or invalid context");
+ }
+
+ return Optional.ofNullable(result).orElse(template);
+ }
+
+ public boolean evaluateMandatoryCondition(
+ final String mandatoryCondition,
+ final Attributable attributable,
+ final DerAttrHandler derAttrHandler) {
+
+ JexlContext jexlContext = new JexlContextBuilder().
+ plainAttrs(attributable.getPlainAttrs()).
+ derAttrs(attributable, derAttrHandler).
+ build();
+
+ return Boolean.parseBoolean(evaluateExpression(mandatoryCondition, jexlContext).toString());
+ }
+}
diff --git a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java
index 2aa7f0ff8a..f13571681e 100644
--- a/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/SyncopeJexlFunctions.java
@@ -30,7 +30,12 @@ import org.identityconnectors.framework.common.objects.ConnectorObject;
/**
* Utility functions for usage with JEXL engine.
*/
-public class SyncopeJexlFunctions {
+public class SyncopeJexlFunctions implements JexlFunctions {
+
+ @Override
+ public String getNamespace() {
+ return "syncope";
+ }
/**
* Converts realm's full path into the equivalent DN.
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/TemplateUtils.java
similarity index 83%
rename from core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java
rename to core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/TemplateUtils.java
index 49712cd609..d49c06951f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/TemplateUtils.java
+++ b/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/TemplateUtils.java
@@ -16,12 +16,12 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.syncope.core.provisioning.java.utils;
+package org.apache.syncope.core.provisioning.api.jexl;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
-import org.apache.commons.jexl3.MapContext;
+import org.apache.commons.jexl3.JexlContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.EntityTOUtils;
@@ -37,37 +37,56 @@ import org.apache.syncope.common.lib.to.UserTO;
import org.apache.syncope.common.lib.types.ClientExceptionType;
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
public class TemplateUtils {
- public static void check(final Map<String, AnyTO> templates, final ClientExceptionType clientExceptionType) {
+ protected static void fillRelationships(final GroupableRelatableTO any, final GroupableRelatableTO template) {
+ template.getRelationships().stream().
+ filter(relationship -> any.getRelationship(
+ relationship.getOtherEndKey(), relationship.getOtherEndKey()).isEmpty()).
+ forEachOrdered(relationship -> any.getRelationships().add(relationship));
+ }
+
+ protected static void fillMemberships(final GroupableRelatableTO any, final GroupableRelatableTO template) {
+ template.getMemberships().stream().
+ filter(membership -> any.getMembership(membership.getGroupKey()).isEmpty()).
+ forEachOrdered(membership -> any.getMemberships().add(membership));
+ }
+
+ protected final UserDAO userDAO;
+
+ protected final GroupDAO groupDAO;
+
+ protected final JexlTools jexlTools;
+
+ public TemplateUtils(final UserDAO userDAO, final GroupDAO groupDAO, final JexlTools jexlTools) {
+ this.userDAO = userDAO;
+ this.groupDAO = groupDAO;
+ this.jexlTools = jexlTools;
+ }
+
+ public void check(final Map<String, AnyTO> templates, final ClientExceptionType clientExceptionType) {
SyncopeClientException sce = SyncopeClientException.build(clientExceptionType);
templates.values().forEach(value -> {
value.getPlainAttrs().stream().
- filter(attrTO -> !attrTO.getValues().isEmpty()
- && !JexlUtils.isExpressionValid(attrTO.getValues().getFirst())).
- forEachOrdered(attrTO -> sce.getElements().add("Invalid JEXL: " + attrTO.getValues().getFirst()));
+ filter(attr -> !attr.getValues().isEmpty()
+ && !jexlTools.isExpressionValid(attr.getValues().getFirst())).
+ forEach(attr -> sce.getElements().add("Invalid JEXL: " + attr.getValues().getFirst()));
switch (value) {
case UserTO template -> {
- if (StringUtils.isNotBlank(template.getUsername())
- && !JexlUtils.isExpressionValid(template.getUsername())) {
-
+ if (template.getUsername() != null && !jexlTools.isExpressionValid(template.getUsername())) {
sce.getElements().add("Invalid JEXL: " + template.getUsername());
}
- if (StringUtils.isNotBlank(template.getPassword())
- && !JexlUtils.isExpressionValid(template.getPassword())) {
-
+ if (template.getPassword() != null && !jexlTools.isExpressionValid(template.getPassword())) {
sce.getElements().add("Invalid JEXL: " + template.getPassword());
}
}
case GroupTO template -> {
- if (StringUtils.isNotBlank(template.getName())
- && !JexlUtils.isExpressionValid(template.getName())) {
-
+ if (template.getName() != null && !jexlTools.isExpressionValid(template.getName())) {
sce.getElements().add("Invalid JEXL: " + template.getName());
}
}
@@ -81,13 +100,13 @@ public class TemplateUtils {
}
}
- protected static Attr evaluateAttr(final Attr template, final MapContext jexlContext) {
+ protected Attr evaluateAttr(final Attr template, final JexlContext jexlContext) {
Attr result = new Attr();
result.setSchema(template.getSchema());
- if (template.getValues() != null && !template.getValues().isEmpty()) {
+ if (!CollectionUtils.isEmpty(template.getValues())) {
template.getValues().forEach(value -> {
- String evaluated = JexlUtils.evaluateExpr(value, jexlContext).toString();
+ String evaluated = jexlTools.evaluateExpression(value, jexlContext).toString();
if (StringUtils.isNotBlank(evaluated)) {
result.getValues().add(evaluated);
}
@@ -97,14 +116,15 @@ public class TemplateUtils {
return result;
}
- protected static void fill(final RealmMember realmMember, final RealmMember template) {
- MapContext jexlContext = new MapContext();
- JexlUtils.addFieldsToContext(realmMember, jexlContext);
- JexlUtils.addAttrsToContext(realmMember.getPlainAttrs(), jexlContext);
- JexlUtils.addAttrsToContext(realmMember.getDerAttrs(), jexlContext);
+ protected void fill(final RealmMember realmMember, final RealmMember template) {
+ JexlContext jexlContext = new JexlContextBuilder().
+ fields(realmMember).
+ attrs(realmMember.getPlainAttrs()).
+ attrs(realmMember.getDerAttrs()).
+ build();
if (template.getRealm() != null) {
- String evaluated = JexlUtils.evaluateExpr(template.getRealm(), jexlContext).toString();
+ String evaluated = jexlTools.evaluateExpression(template.getRealm(), jexlContext).toString();
if (StringUtils.isNotBlank(evaluated)) {
realmMember.setRealm(evaluated);
}
@@ -136,36 +156,15 @@ public class TemplateUtils {
realmMember.getAuxClasses().addAll(template.getAuxClasses());
}
- protected static void fillRelationships(final GroupableRelatableTO any, final GroupableRelatableTO template) {
- template.getRelationships().stream().
- filter(relationship -> any.getRelationship(
- relationship.getOtherEndKey(), relationship.getOtherEndKey()).isEmpty()).
- forEachOrdered(relationship -> any.getRelationships().add(relationship));
- }
-
- protected static void fillMemberships(final GroupableRelatableTO any, final GroupableRelatableTO template) {
- template.getMemberships().stream().
- filter(membership -> any.getMembership(membership.getGroupKey()).isEmpty()).
- forEachOrdered(membership -> any.getMemberships().add(membership));
- }
-
- protected final UserDAO userDAO;
-
- protected final GroupDAO groupDAO;
-
- public TemplateUtils(final UserDAO userDAO, final GroupDAO groupDAO) {
- this.userDAO = userDAO;
- this.groupDAO = groupDAO;
- }
-
@Transactional(readOnly = true)
public void apply(final RealmMember realmMember, final AnyTO template) {
fill(realmMember, template);
- MapContext jexlContext = new MapContext();
- JexlUtils.addFieldsToContext(realmMember, jexlContext);
- JexlUtils.addAttrsToContext(realmMember.getPlainAttrs(), jexlContext);
- JexlUtils.addAttrsToContext(realmMember.getDerAttrs(), jexlContext);
+ JexlContext jexlContext = new JexlContextBuilder().
+ fields(realmMember).
+ attrs(realmMember.getPlainAttrs()).
+ attrs(realmMember.getDerAttrs()).
+ build();
switch (template) {
case AnyObjectTO anyObjectTO -> {
@@ -175,7 +174,7 @@ public class TemplateUtils {
case UserTO userTO -> {
if (StringUtils.isNotBlank(userTO.getUsername())) {
- String evaluated = JexlUtils.evaluateExpr(userTO.getUsername(), jexlContext).toString();
+ String evaluated = jexlTools.evaluateExpression(userTO.getUsername(), jexlContext).toString();
if (StringUtils.isNotBlank(evaluated)) {
switch (realmMember) {
case UserTO urm ->
@@ -189,7 +188,7 @@ public class TemplateUtils {
}
if (StringUtils.isNotBlank(userTO.getPassword())) {
- String evaluated = JexlUtils.evaluateExpr(userTO.getPassword(), jexlContext).toString();
+ String evaluated = jexlTools.evaluateExpression(userTO.getPassword(), jexlContext).toString();
if (StringUtils.isNotBlank(evaluated)) {
switch (realmMember) {
case UserTO urm ->
@@ -242,7 +241,7 @@ public class TemplateUtils {
case GroupTO groupTO -> {
if (StringUtils.isNotBlank(groupTO.getName())) {
- String evaluated = JexlUtils.evaluateExpr(groupTO.getName(), jexlContext).toString();
+ String evaluated = jexlTools.evaluateExpression(groupTO.getName(), jexlContext).toString();
if (StringUtils.isNotBlank(evaluated)) {
switch (realmMember) {
case GroupTO grm ->
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AbstractTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AbstractTest.java
index deccfdd36a..f923b6d3f0 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AbstractTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/AbstractTest.java
@@ -18,6 +18,13 @@
*/
package org.apache.syncope.core.provisioning.api;
+import java.util.Map;
+import org.apache.commons.jexl3.JexlBuilder;
+import org.apache.commons.jexl3.JexlEngine;
+import org.apache.commons.jexl3.introspection.JexlPermissions;
+import org.apache.syncope.core.provisioning.api.jexl.EmptyClassLoader;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
+import org.apache.syncope.core.provisioning.api.jexl.SyncopeJexlFunctions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.MockitoAnnotations;
@@ -30,6 +37,18 @@ import org.mockito.quality.Strictness;
@SuppressWarnings("squid:S2187")
public class AbstractTest {
+ protected JexlTools jexlTools() {
+ JexlEngine jexlEngine = new JexlBuilder().
+ loader(new EmptyClassLoader()).
+ permissions(JexlPermissions.RESTRICTED.compose("java.time.*", "org.apache.syncope.*")).
+ namespaces(Map.of("syncope", new SyncopeJexlFunctions())).
+ cache(512).
+ silent(false).
+ strict(false).
+ create();
+ return new JexlTools(jexlEngine);
+ }
+
@BeforeEach
public void init() {
MockitoAnnotations.openMocks(this);
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java
index ab943900d9..5ccaad0c27 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/JexlUtilsTest.java
@@ -47,6 +47,7 @@ import org.apache.syncope.core.provisioning.api.AbstractTest;
import org.apache.syncope.core.provisioning.api.DerAttrHandler;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
+import org.springframework.test.util.ReflectionTestUtils;
public class JexlUtilsTest extends AbstractTest {
@@ -54,78 +55,87 @@ public class JexlUtilsTest extends AbstractTest {
private JexlContext context;
@Test
- public void isExpressionValid() {
+ void isExpressionValid() {
String expression = "6 * 12 + 5 / 2.6";
- assertTrue(JexlUtils.isExpressionValid(expression));
+ assertTrue(jexlTools().isExpressionValid(expression));
expression = "@inv4lid expression!";
- assertFalse(JexlUtils.isExpressionValid(expression));
+ assertFalse(jexlTools().isExpressionValid(expression));
}
@Test
- public void evaluateExpr() {
+ void evaluateExpr() {
String expression = null;
- assertEquals(StringUtils.EMPTY, JexlUtils.evaluateExpr(expression, context));
+ assertEquals(StringUtils.EMPTY, jexlTools().evaluateExpression(expression, context));
expression = "6 * 12 + 5 / 2.6";
double result = 73.92307692307692;
- assertEquals(result, JexlUtils.evaluateExpr(expression, context));
+ assertEquals(result, jexlTools().evaluateExpression(expression, context));
}
@Test
- public void addFieldsToContext(
+ void builderFields(
final @Mock Any any,
final @Mock AnyTO anyTO,
final @Mock Realm realm,
final @Mock RealmTO realmTO) {
- JexlUtils.addFieldsToContext(new Exception(), context);
+ JexlContextBuilder builder = new JexlContextBuilder();
+ ReflectionTestUtils.setField(builder, "jexlContext", context);
+
+ builder.fields(new Exception());
verify(context, times(2)).set(eq("cause"), any());
String testFullPath = "testFullPath";
when(any.getRealm()).thenReturn(realm);
when(realm.getFullPath()).thenReturn(testFullPath);
- JexlUtils.addFieldsToContext(any, context);
+ builder.fields(any);
verify(context).set("realm", testFullPath);
String testRealm = "testRealm";
when(anyTO.getRealm()).thenReturn(testRealm);
- JexlUtils.addFieldsToContext(anyTO, context);
+ builder.fields(anyTO);
verify(context, times(3)).set("realm", testRealm);
String fullPath = "test/full/path";
when(realm.getFullPath()).thenReturn(fullPath);
- JexlUtils.addFieldsToContext(realm, context);
+ builder.fields(realm);
verify(context, times(2)).set("fullPath", fullPath);
fullPath = "test/full/path2";
when(realmTO.getFullPath()).thenReturn(fullPath);
- JexlUtils.addFieldsToContext(realmTO, context);
+ builder.fields(realmTO);
verify(context, times(2)).set("fullPath", fullPath);
}
@Test
- public void addAttrTOsToContext() {
- String schemaName = "testSchema";
+ void builderAttrs() {
+ String schema = "testSchema";
String value = "testValue";
Collection<Attr> attrs = new ArrayList<>();
- Attr attr = new Attr.Builder(schemaName).build();
+ Attr attr = new Attr.Builder(schema).build();
attrs.add(attr);
- JexlUtils.addAttrsToContext(attrs, context);
- verify(context).set(schemaName, StringUtils.EMPTY);
+ JexlContextBuilder builder = new JexlContextBuilder();
+ ReflectionTestUtils.setField(builder, "jexlContext", context);
+
+ builder.attrs(attrs);
+ verify(context).set(schema, StringUtils.EMPTY);
- attr = new Attr.Builder(schemaName).value(value).build();
+ attr = new Attr.Builder(schema).value(value).build();
attrs.clear();
attrs.add(attr);
- JexlUtils.addAttrsToContext(attrs, context);
- verify(context).set(schemaName, value);
+ builder.attrs(attrs);
+ verify(context).set(schema, value);
}
@Test
- public void addPlainAttrsToContext(final @Mock Collection<PlainAttr> attrs) {
- JexlUtils.addPlainAttrsToContext(attrs, context);
+ void builderPlainAttrs(final @Mock Collection<PlainAttr> attrs) {
+ JexlContextBuilder builder = new JexlContextBuilder();
+ ReflectionTestUtils.setField(builder, "jexlContext", context);
+
+ builder.plainAttrs(attrs);
verify(context, times(0)).set(anyString(), any());
}
@@ -141,12 +151,16 @@ public class JexlUtilsTest extends AbstractTest {
derAttrs.put(derSchema, expression);
when(derAttrHandler.getValues(any(Any.class))).thenReturn(derAttrs);
- JexlUtils.addDerAttrsToContext(any, derAttrHandler, context);
+
+ JexlContextBuilder builder = new JexlContextBuilder();
+ ReflectionTestUtils.setField(builder, "jexlContext", context);
+
+ builder.derAttrs(any, derAttrHandler);
verify(context).set(derAttrs.get(derSchema), expression);
}
@Test
- public void evaluateMandatoryCondition(
+ void evaluateMandatoryCondition(
final @Mock DerAttrHandler derAttrHandler,
final @Mock Any any,
final @Mock DerSchema derSchema,
@@ -160,18 +174,18 @@ public class JexlUtilsTest extends AbstractTest {
when(any.getPlainAttrs()).thenReturn(new ArrayList<>());
when(derAttrHandler.getValues(any(Any.class))).thenReturn(derAttrs);
- assertTrue(JexlUtils.evaluateMandatoryCondition("true", any, derAttrHandler));
- assertFalse(JexlUtils.evaluateMandatoryCondition("false", any, derAttrHandler));
+ assertTrue(jexlTools().evaluateMandatoryCondition("true", any, derAttrHandler));
+ assertFalse(jexlTools().evaluateMandatoryCondition("false", any, derAttrHandler));
}
@Test
- public void evaluateTemplate() {
+ void evaluateTemplate() {
byte[] byteArray = "a value".getBytes();
- String result = JexlUtils.evaluateTemplate(
+ String result = jexlTools().evaluateTemplate(
"${syncope:base64Encode(value)}", new MapContext(Map.of("value", byteArray)));
assertEquals(Base64.getEncoder().encodeToString(byteArray), result);
- result = JexlUtils.evaluateTemplate(
+ result = jexlTools().evaluateTemplate(
"${syncope:fullPath2Dn(value, 'ou')}", new MapContext(Map.of("value", "/a/b/c")));
assertEquals("ou=c,ou=b,ou=a", result);
}
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MailTemplateTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MailTemplateTest.java
index b93bf591b2..9451bb2bdf 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MailTemplateTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MailTemplateTest.java
@@ -66,7 +66,7 @@ public class MailTemplateTest extends AbstractTest {
@Test
public void confirmPasswordReset() throws IOException {
- String htmlBody = JexlUtils.evaluateTemplate(CONFIRM_PASSWORD_RESET_TEMPLATE, new MapContext());
+ String htmlBody = jexlTools().evaluateTemplate(CONFIRM_PASSWORD_RESET_TEMPLATE, new MapContext());
assertNotNull(htmlBody);
}
@@ -84,7 +84,7 @@ public class MailTemplateTest extends AbstractTest {
input.add(token);
ctx.put("input", input);
- String textBody = JexlUtils.evaluateTemplate(REQUEST_PASSWORD_RESET_TEMPLATE, new MapContext(ctx));
+ String textBody = jexlTools().evaluateTemplate(REQUEST_PASSWORD_RESET_TEMPLATE, new MapContext(ctx));
assertNotNull(textBody);
assertTrue(textBody.contains("a password reset was requested for " + username + "."));
@@ -120,7 +120,7 @@ public class MailTemplateTest extends AbstractTest {
ctx.put("events", List.of("event1"));
- String htmlBody = JexlUtils.evaluateTemplate(OPTIN_TEMPLATE, new MapContext(ctx));
+ String htmlBody = jexlTools().evaluateTemplate(OPTIN_TEMPLATE, new MapContext(ctx));
assertNotNull(htmlBody);
diff --git a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MappingTest.java b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MappingTest.java
index 9badb8fa4b..fbb7129c1a 100644
--- a/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MappingTest.java
+++ b/core/provisioning-api/src/test/java/org/apache/syncope/core/provisioning/api/jexl/MappingTest.java
@@ -43,14 +43,13 @@ public class MappingTest extends AbstractTest {
when(user.getRealm()).thenReturn(realm);
assertNotNull(user);
- JexlContext jexlContext = new MapContext();
- JexlUtils.addFieldsToContext(user, jexlContext);
+ JexlContext jexlContext = new JexlContextBuilder().fields(user).build();
String connObjectLink = "'uid=' + username + ',ou=people,o=isp'";
- assertEquals("uid=rossini,ou=people,o=isp", JexlUtils.evaluateExpr(connObjectLink, jexlContext));
+ assertEquals("uid=rossini,ou=people,o=isp", jexlTools().evaluateExpression(connObjectLink, jexlContext));
connObjectLink = "'uid=' + username + realm.replaceAll('/', ',o=') + ',ou=people,o=isp'";
- assertEquals("uid=rossini,o=even,ou=people,o=isp", JexlUtils.evaluateExpr(connObjectLink, jexlContext));
+ assertEquals("uid=rossini,o=even,ou=people,o=isp", jexlTools().evaluateExpression(connObjectLink, jexlContext));
}
@Test
@@ -59,19 +58,17 @@ public class MappingTest extends AbstractTest {
when(realm.getFullPath()).thenReturn("/even/two");
assertNotNull(realm);
- JexlContext jexlContext = new MapContext();
- JexlUtils.addFieldsToContext(realm, jexlContext);
+ JexlContext jexlContext = new JexlContextBuilder().fields(realm).build();
String connObjectLink = "syncope:fullPath2Dn(fullPath, 'ou') + ',o=isp'";
- assertEquals("ou=two,ou=even,o=isp", JexlUtils.evaluateExpr(connObjectLink, jexlContext));
+ assertEquals("ou=two,ou=even,o=isp", jexlTools().evaluateExpression(connObjectLink, jexlContext));
when(realm.getFullPath()).thenReturn("/even");
assertNotNull(realm);
- jexlContext = new MapContext();
- JexlUtils.addFieldsToContext(realm, jexlContext);
+ jexlContext = new JexlContextBuilder().fields(realm).build();
- assertEquals("ou=even,o=isp", JexlUtils.evaluateExpr(connObjectLink, jexlContext));
+ assertEquals("ou=even,o=isp", jexlTools().evaluateExpression(connObjectLink, jexlContext));
}
@Test
@@ -82,6 +79,6 @@ public class MappingTest extends AbstractTest {
jexlContext.set("value", now);
String expression = "value.toInstant().toEpochMilli()";
- assertEquals(now.toInstant().toEpochMilli(), JexlUtils.evaluateExpr(expression, jexlContext));
+ assertEquals(now.toInstant().toEpochMilli(), jexlTools().evaluateExpression(expression, jexlContext));
}
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java
index 3741da47da..cad5cff4ff 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultDerAttrHandler.java
@@ -23,7 +23,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.commons.jexl3.JexlContext;
-import org.apache.commons.jexl3.MapContext;
import org.apache.syncope.core.persistence.api.entity.Any;
import org.apache.syncope.core.persistence.api.entity.AnyUtilsFactory;
import org.apache.syncope.core.persistence.api.entity.Attributable;
@@ -32,7 +31,8 @@ import org.apache.syncope.core.persistence.api.entity.Groupable;
import org.apache.syncope.core.persistence.api.entity.Membership;
import org.apache.syncope.core.persistence.api.entity.Realm;
import org.apache.syncope.core.provisioning.api.DerAttrHandler;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlContextBuilder;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
@@ -42,29 +42,33 @@ public class DefaultDerAttrHandler implements DerAttrHandler {
protected static final Logger LOG = LoggerFactory.getLogger(DerAttrHandler.class);
- protected static Map<DerSchema, String> getValues(
+ protected final AnyUtilsFactory anyUtilsFactory;
+
+ protected final JexlTools jexlTools;
+
+ public DefaultDerAttrHandler(final AnyUtilsFactory anyUtilsFactory, final JexlTools jexlTools) {
+ this.anyUtilsFactory = anyUtilsFactory;
+ this.jexlTools = jexlTools;
+ }
+
+ protected Map<DerSchema, String> getValues(
final Attributable attributable,
final Collection<? extends DerSchema> schemas) {
Map<DerSchema, String> result = new HashMap<>(schemas.size());
schemas.forEach(schema -> {
- JexlContext jexlContext = new MapContext();
- JexlUtils.addPlainAttrsToContext(attributable.getPlainAttrs(), jexlContext);
- JexlUtils.addFieldsToContext(attributable, jexlContext);
+ JexlContext jexlContext = new JexlContextBuilder().
+ plainAttrs(attributable.getPlainAttrs()).
+ fields(attributable).
+ build();
- result.put(schema, JexlUtils.evaluateExpr(schema.getExpression(), jexlContext).toString());
+ result.put(schema, jexlTools.evaluateExpression(schema.getExpression(), jexlContext).toString());
});
return result;
}
- protected final AnyUtilsFactory anyUtilsFactory;
-
- public DefaultDerAttrHandler(final AnyUtilsFactory anyUtilsFactory) {
- this.anyUtilsFactory = anyUtilsFactory;
- }
-
@Override
public String getValue(final Realm realm, final DerSchema schema) {
if (realm.getAnyTypeClass() == null || !realm.getAnyTypeClass().getDerSchemas().contains(schema)) {
@@ -115,17 +119,18 @@ public class DefaultDerAttrHandler implements DerAttrHandler {
anyUtilsFactory.getInstance(any).dao().findAllowedSchemas(any, DerSchema.class).getForSelf());
}
- protected static Map<DerSchema, String> getValues(
- final Groupable<?, ?, ?> any, final Membership<?> membership, final Set<DerSchema> schemas) {
+ protected Map<DerSchema, String> getValues(
+ final Groupable<?, ?, ?> groupable, final Membership<?> membership, final Set<DerSchema> schemas) {
Map<DerSchema, String> result = new HashMap<>(schemas.size());
schemas.forEach(schema -> {
- JexlContext jexlContext = new MapContext();
- JexlUtils.addPlainAttrsToContext(any.getPlainAttrs(membership), jexlContext);
- JexlUtils.addFieldsToContext(any, jexlContext);
+ JexlContext jexlContext = new JexlContextBuilder().
+ plainAttrs(groupable.getPlainAttrs(membership)).
+ fields(groupable).
+ build();
- result.put(schema, JexlUtils.evaluateExpr(schema.getExpression(), jexlContext).toString());
+ result.put(schema, jexlTools.evaluateExpression(schema.getExpression(), jexlContext).toString());
});
return result;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
index 0b947428ce..76b88c6baf 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/DefaultMappingManager.java
@@ -28,7 +28,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.commons.jexl3.JexlContext;
-import org.apache.commons.jexl3.MapContext;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.mutable.Mutable;
@@ -83,7 +82,8 @@ import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.MappingManager;
import org.apache.syncope.core.provisioning.api.PlainAttrGetter;
import org.apache.syncope.core.provisioning.api.data.ItemTransformer;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlContextBuilder;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
import org.identityconnectors.framework.common.FrameworkUtil;
@@ -189,6 +189,8 @@ public class DefaultMappingManager implements MappingManager {
protected final EncryptorManager encryptorManager;
+ protected final JexlTools jexlTools;
+
public DefaultMappingManager(
final AnyTypeDAO anyTypeDAO,
final UserDAO userDAO,
@@ -199,7 +201,8 @@ public class DefaultMappingManager implements MappingManager {
final ImplementationDAO implementationDAO,
final DerAttrHandler derAttrHandler,
final IntAttrNameParser intAttrNameParser,
- final EncryptorManager encryptorManager) {
+ final EncryptorManager encryptorManager,
+ final JexlTools jexlTools) {
this.anyTypeDAO = anyTypeDAO;
this.userDAO = userDAO;
@@ -211,6 +214,7 @@ public class DefaultMappingManager implements MappingManager {
this.derAttrHandler = derAttrHandler;
this.intAttrNameParser = intAttrNameParser;
this.encryptorManager = encryptorManager;
+ this.jexlTools = jexlTools;
}
protected List<Implementation> getTransformers(final Item item) {
@@ -242,11 +246,13 @@ public class DefaultMappingManager implements MappingManager {
orElse(null);
String evalConnObjectLink = null;
if (StringUtils.isNotBlank(connObjectLink)) {
- JexlContext jexlContext = new MapContext();
- JexlUtils.addFieldsToContext(any, jexlContext);
- JexlUtils.addPlainAttrsToContext(any.getPlainAttrs(), jexlContext);
- JexlUtils.addDerAttrsToContext(any, derAttrHandler, jexlContext);
- evalConnObjectLink = JexlUtils.evaluateExpr(connObjectLink, jexlContext).toString();
+ JexlContext jexlContext = new JexlContextBuilder().
+ fields(any).
+ plainAttrs(any.getPlainAttrs()).
+ derAttrs(any, derAttrHandler).
+ build();
+
+ evalConnObjectLink = jexlTools.evaluateExpression(connObjectLink, jexlContext).toString();
}
return getName(evalConnObjectLink, connObjectKey);
@@ -272,9 +278,9 @@ public class DefaultMappingManager implements MappingManager {
String connObjectLink = orgUnit.getConnObjectLink();
String evalConnObjectLink = null;
if (StringUtils.isNotBlank(connObjectLink)) {
- JexlContext jexlContext = new MapContext();
- JexlUtils.addFieldsToContext(realm, jexlContext);
- evalConnObjectLink = JexlUtils.evaluateExpr(connObjectLink, jexlContext).toString();
+ JexlContext jexlContext = new JexlContextBuilder().fields(realm).build();
+
+ evalConnObjectLink = jexlTools.evaluateExpression(connObjectLink, jexlContext).toString();
}
return getName(evalConnObjectLink, connObjectKey);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
index 3f2980226a..7653817aca 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/ProvisioningContext.java
@@ -20,6 +20,11 @@ package org.apache.syncope.core.provisioning.java;
import java.util.List;
import java.util.concurrent.Executor;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.apache.commons.jexl3.JexlBuilder;
+import org.apache.commons.jexl3.JexlEngine;
+import org.apache.commons.jexl3.introspection.JexlPermissions;
import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
import org.apache.syncope.core.persistence.api.DomainHolder;
import org.apache.syncope.core.persistence.api.EncryptorManager;
@@ -104,6 +109,11 @@ import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
import org.apache.syncope.core.provisioning.api.data.WAConfigDataBinder;
import org.apache.syncope.core.provisioning.api.data.wa.WAClientAppDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.EmptyClassLoader;
+import org.apache.syncope.core.provisioning.api.jexl.JexlFunctions;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
+import org.apache.syncope.core.provisioning.api.jexl.SyncopeJexlFunctions;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.provisioning.api.job.JobManager;
import org.apache.syncope.core.provisioning.api.notification.NotificationJobDelegate;
import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
@@ -155,7 +165,6 @@ import org.apache.syncope.core.provisioning.java.pushpull.InboundMatcher;
import org.apache.syncope.core.provisioning.java.pushpull.LiveSyncTaskSaver;
import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher;
import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
-import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
import org.apache.syncope.core.spring.security.DefaultCredentialChecker;
import org.apache.syncope.core.spring.security.PasswordGenerator;
import org.apache.syncope.core.spring.security.SecurityProperties;
@@ -183,6 +192,27 @@ import org.springframework.scheduling.concurrent.SimpleAsyncTaskScheduler;
@Configuration(proxyBeanMethods = false)
public class ProvisioningContext {
+ @ConditionalOnMissingBean
+ @Bean
+ public SyncopeJexlFunctions syncopeJexlFunctions() {
+ return new SyncopeJexlFunctions();
+ }
+
+ @ConditionalOnMissingBean
+ @Bean
+ public JexlTools jexlTools(final List<JexlFunctions> jexlFunctions) {
+ JexlEngine jexlEngine = new JexlBuilder().
+ loader(new EmptyClassLoader()).
+ permissions(JexlPermissions.RESTRICTED.compose("java.time.*", "org.apache.syncope.*")).
+ namespaces(jexlFunctions.stream().
+ collect(Collectors.toMap(JexlFunctions::getNamespace, Function.identity()))).
+ cache(512).
+ silent(false).
+ strict(false).
+ create();
+ return new JexlTools(jexlEngine);
+ }
+
@ConditionalOnMissingBean
@Bean
public AsyncConnectorFacade asyncConnectorFacade() {
@@ -361,8 +391,8 @@ public class ProvisioningContext {
@ConditionalOnMissingBean
@Bean
- public DerAttrHandler derAttrHandler(final AnyUtilsFactory anyUtilsFactory) {
- return new DefaultDerAttrHandler(anyUtilsFactory);
+ public DerAttrHandler derAttrHandler(final AnyUtilsFactory anyUtilsFactory, final JexlTools jexlTools) {
+ return new DefaultDerAttrHandler(anyUtilsFactory, jexlTools);
}
@ConditionalOnMissingBean
@@ -377,7 +407,8 @@ public class ProvisioningContext {
final ImplementationDAO implementationDAO,
final DerAttrHandler derAttrHandler,
final IntAttrNameParser intAttrNameParser,
- final EncryptorManager encryptorManager) {
+ final EncryptorManager encryptorManager,
+ final JexlTools jexlTools) {
return new DefaultMappingManager(
anyTypeDAO,
@@ -389,13 +420,14 @@ public class ProvisioningContext {
implementationDAO,
derAttrHandler,
intAttrNameParser,
- encryptorManager);
+ encryptorManager,
+ jexlTools);
}
@ConditionalOnMissingBean
@Bean
- public TemplateUtils templateUtils(final UserDAO userDAO, final GroupDAO groupDAO) {
- return new TemplateUtils(userDAO, groupDAO);
+ public TemplateUtils templateUtils(final UserDAO userDAO, final GroupDAO groupDAO, final JexlTools jexlTools) {
+ return new TemplateUtils(userDAO, groupDAO, jexlTools);
}
@ConditionalOnMissingBean
@@ -429,7 +461,8 @@ public class ProvisioningContext {
final ExternalResourceDAO resourceDAO,
final ConnObjectUtils connObjectUtils,
final MappingManager mappingManager,
- final DerAttrHandler derAttrHandler) {
+ final DerAttrHandler derAttrHandler,
+ final JexlTools jexlTools) {
return new DefaultPropagationManager(
resourceDAO,
@@ -437,7 +470,8 @@ public class ProvisioningContext {
connObjectUtils,
mappingManager,
derAttrHandler,
- anyUtilsFactory);
+ anyUtilsFactory,
+ jexlTools);
}
@ConditionalOnMissingBean
@@ -558,7 +592,8 @@ public class ProvisioningContext {
final AnyObjectDataBinder anyObjectDataBinder,
final ConfParamOps confParamOps,
final DerAttrHandler derAttrHandler,
- final IntAttrNameParser intAttrNameParser) {
+ final IntAttrNameParser intAttrNameParser,
+ final JexlTools jexlTools) {
return new DefaultNotificationManager(
derSchemaDAO,
@@ -576,7 +611,8 @@ public class ProvisioningContext {
confParamOps,
entityFactory,
intAttrNameParser,
- searchCondVisitor);
+ searchCondVisitor,
+ jexlTools);
}
/**
@@ -696,7 +732,8 @@ public class ProvisioningContext {
final MappingManager mappingManager,
final IntAttrNameParser intAttrNameParser,
final OutboundMatcher outboundMatcher,
- final PlainAttrValidationManager validator) {
+ final PlainAttrValidationManager validator,
+ final JexlTools jexlTools) {
return new AnyObjectDataBinderImpl(
anyTypeDAO,
@@ -714,7 +751,8 @@ public class ProvisioningContext {
mappingManager,
intAttrNameParser,
outboundMatcher,
- validator);
+ validator,
+ jexlTools);
}
@ConditionalOnMissingBean
@@ -755,14 +793,14 @@ public class ProvisioningContext {
@ConditionalOnMissingBean
@Bean
- public AuthModuleDataBinder authModuleDataBinder(final EntityFactory entityFactory) {
- return new AuthModuleDataBinderImpl(entityFactory);
+ public AuthModuleDataBinder authModuleDataBinder(final EntityFactory entityFactory, final JexlTools jexlTools) {
+ return new AuthModuleDataBinderImpl(entityFactory, jexlTools);
}
@ConditionalOnMissingBean
@Bean
- public AttrRepoDataBinder attrRepoDataBinder(final EntityFactory entityFactory) {
- return new AttrRepoDataBinderImpl(entityFactory);
+ public AttrRepoDataBinder attrRepoDataBinder(final EntityFactory entityFactory, final JexlTools jexlTools) {
+ return new AttrRepoDataBinderImpl(entityFactory, jexlTools);
}
@ConditionalOnMissingBean
@@ -848,7 +886,8 @@ public class ProvisioningContext {
final MappingManager mappingManager,
final IntAttrNameParser intAttrNameParser,
final OutboundMatcher outboundMatcher,
- final PlainAttrValidationManager validator) {
+ final PlainAttrValidationManager validator,
+ final JexlTools jexlTools) {
return new GroupDataBinderImpl(
anyTypeDAO,
@@ -867,7 +906,8 @@ public class ProvisioningContext {
intAttrNameParser,
outboundMatcher,
searchCondVisitor,
- validator);
+ validator,
+ jexlTools);
}
@ConditionalOnMissingBean
@@ -926,7 +966,9 @@ public class ProvisioningContext {
final DerAttrHandler derAttrHandler,
final PlainAttrValidationManager validator,
final MappingManager mappingManager,
- final IntAttrNameParser intAttrNameParser) {
+ final IntAttrNameParser intAttrNameParser,
+ final JexlTools jexlTools,
+ final TemplateUtils templateUtils) {
return new RealmDataBinderImpl(
anyTypeDAO,
@@ -940,7 +982,9 @@ public class ProvisioningContext {
derAttrHandler,
validator,
mappingManager,
- intAttrNameParser);
+ intAttrNameParser,
+ jexlTools,
+ templateUtils);
}
@ConditionalOnMissingBean
@@ -979,7 +1023,8 @@ public class ProvisioningContext {
final ImplementationDAO implementationDAO,
final PlainSchemaDAO plainSchemaDAO,
final IntAttrNameParser intAttrNameParser,
- final PropagationTaskExecutor propagationTaskExecutor) {
+ final PropagationTaskExecutor propagationTaskExecutor,
+ final JexlTools jexlTools) {
return new ResourceDataBinderImpl(
anyTypeDAO,
@@ -990,7 +1035,8 @@ public class ProvisioningContext {
plainSchemaDAO,
entityFactory,
intAttrNameParser,
- propagationTaskExecutor);
+ propagationTaskExecutor,
+ jexlTools);
}
@ConditionalOnMissingBean
@@ -1032,7 +1078,8 @@ public class ProvisioningContext {
final DerSchemaDAO derSchemaDAO,
final ExternalResourceDAO resourceDAO,
final AnyTypeDAO anyTypeDAO,
- final ImplementationDAO implementationDAO) {
+ final ImplementationDAO implementationDAO,
+ final JexlTools jexlTools) {
return new SchemaDataBinderImpl(
anyTypeClassDAO,
@@ -1042,7 +1089,8 @@ public class ProvisioningContext {
anyTypeDAO,
implementationDAO,
entityFactory,
- anyUtilsFactory);
+ anyUtilsFactory,
+ jexlTools);
}
@ConditionalOnMissingBean
@@ -1061,7 +1109,8 @@ public class ProvisioningContext {
final TaskExecDAO taskExecDAO,
final AnyTypeDAO anyTypeDAO,
final ImplementationDAO implementationDAO,
- final SyncopeTaskScheduler scheduler) {
+ final SyncopeTaskScheduler scheduler,
+ final TemplateUtils templateUtils) {
return new TaskDataBinderImpl(
realmSearchDAO,
@@ -1071,7 +1120,8 @@ public class ProvisioningContext {
implementationDAO,
entityFactory,
scheduler,
- taskUtilsFactory);
+ taskUtilsFactory,
+ templateUtils);
}
@ConditionalOnMissingBean
@@ -1098,7 +1148,8 @@ public class ProvisioningContext {
final SecurityQuestionDAO securityQuestionDAO,
final AccessTokenDAO accessTokenDAO,
final DelegationDAO delegationDAO,
- final ConfParamOps confParamOps) {
+ final ConfParamOps confParamOps,
+ final JexlTools jexlTools) {
return new UserDataBinderImpl(
anyTypeDAO,
@@ -1122,7 +1173,8 @@ public class ProvisioningContext {
accessTokenDAO,
delegationDAO,
confParamOps,
- securityProperties);
+ securityProperties,
+ jexlTools);
}
@ConditionalOnMissingBean
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyDataBinder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyDataBinder.java
index b803b511ba..e46db5fe01 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyDataBinder.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyDataBinder.java
@@ -83,7 +83,7 @@ import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.MappingManager;
import org.apache.syncope.core.provisioning.api.PlainAttrGetter;
import org.apache.syncope.core.provisioning.api.PropagationByResource;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher;
import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
@@ -186,9 +186,10 @@ abstract class AnyDataBinder extends AttributableDataBinder {
final MappingManager mappingManager,
final IntAttrNameParser intAttrNameParser,
final OutboundMatcher outboundMatcher,
- final PlainAttrValidationManager validator) {
+ final PlainAttrValidationManager validator,
+ final JexlTools jexlTools) {
- super(plainSchemaDAO, validator, derAttrHandler, mappingManager, intAttrNameParser);
+ super(plainSchemaDAO, validator, derAttrHandler, mappingManager, intAttrNameParser, jexlTools);
this.anyTypeDAO = anyTypeDAO;
this.realmSearchDAO = realmSearchDAO;
this.anyTypeClassDAO = anyTypeClassDAO;
@@ -273,7 +274,7 @@ abstract class AnyDataBinder extends AttributableDataBinder {
AccountGetter.DEFAULT,
PlainAttrGetter.DEFAULT);
if (intValues.values().isEmpty()
- && JexlUtils.evaluateMandatoryCondition(item.getMandatoryCondition(), any, derAttrHandler)) {
+ && jexlTools.evaluateMandatoryCondition(item.getMandatoryCondition(), any, derAttrHandler)) {
missingAttrNames.add(item.getIntAttrName());
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
index bec1013f37..ae09217b26 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AnyObjectDataBinderImpl.java
@@ -62,6 +62,7 @@ import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.MappingManager;
import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher;
import org.springframework.transaction.annotation.Transactional;
@@ -84,7 +85,8 @@ public class AnyObjectDataBinderImpl extends AnyDataBinder implements AnyObjectD
final MappingManager mappingManager,
final IntAttrNameParser intAttrNameParser,
final OutboundMatcher outboundMatcher,
- final PlainAttrValidationManager validator) {
+ final PlainAttrValidationManager validator,
+ final JexlTools jexlTools) {
super(anyTypeDAO,
realmSearchDAO,
@@ -101,7 +103,8 @@ public class AnyObjectDataBinderImpl extends AnyDataBinder implements AnyObjectD
mappingManager,
intAttrNameParser,
outboundMatcher,
- validator);
+ validator,
+ jexlTools);
}
@Transactional(readOnly = true)
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttrRepoDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttrRepoDataBinderImpl.java
index be8566aacb..da1a80feae 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttrRepoDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttrRepoDataBinderImpl.java
@@ -27,7 +27,7 @@ import org.apache.syncope.common.lib.types.MappingPurpose;
import org.apache.syncope.core.persistence.api.entity.EntityFactory;
import org.apache.syncope.core.persistence.api.entity.am.AttrRepo;
import org.apache.syncope.core.provisioning.api.data.AttrRepoDataBinder;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -37,8 +37,11 @@ public class AttrRepoDataBinderImpl implements AttrRepoDataBinder {
protected final EntityFactory entityFactory;
- public AttrRepoDataBinderImpl(final EntityFactory entityFactory) {
+ protected final JexlTools jexlTools;
+
+ public AttrRepoDataBinderImpl(final EntityFactory entityFactory, final JexlTools jexlTools) {
this.entityFactory = entityFactory;
+ this.jexlTools = jexlTools;
}
protected void populateItems(final AttrRepoTO attrRepoTO, final AttrRepo attrRepo) {
@@ -56,9 +59,8 @@ public class AttrRepoDataBinderImpl implements AttrRepoDataBinder {
requiredValuesMissing.getElements().add("intAttrName");
scce.addException(requiredValuesMissing);
} else {
- // no mandatory condition implies mandatory condition false
- if (!JexlUtils.isExpressionValid(itemTO.getMandatoryCondition() == null
- ? "false" : itemTO.getMandatoryCondition())) {
+ if (itemTO.getMandatoryCondition() != null
+ && !jexlTools.isExpressionValid(itemTO.getMandatoryCondition())) {
SyncopeClientException invalidMandatoryCondition =
SyncopeClientException.build(ClientExceptionType.InvalidValues);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttributableDataBinder.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttributableDataBinder.java
index 63527412a0..eac3acf877 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttributableDataBinder.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AttributableDataBinder.java
@@ -35,7 +35,7 @@ import org.apache.syncope.core.persistence.api.entity.PlainSchema;
import org.apache.syncope.core.provisioning.api.DerAttrHandler;
import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.MappingManager;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.spring.implementation.ImplementationManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -54,6 +54,8 @@ abstract class AttributableDataBinder {
protected final IntAttrNameParser intAttrNameParser;
+ protected final JexlTools jexlTools;
+
private final Map<String, DropdownValueProvider> dropdownValueProviders = new ConcurrentHashMap<>();
protected AttributableDataBinder(
@@ -61,13 +63,15 @@ abstract class AttributableDataBinder {
final PlainAttrValidationManager validator,
final DerAttrHandler derAttrHandler,
final MappingManager mappingManager,
- final IntAttrNameParser intAttrNameParser) {
+ final IntAttrNameParser intAttrNameParser,
+ final JexlTools jexlTools) {
this.plainSchemaDAO = plainSchemaDAO;
this.validator = validator;
this.derAttrHandler = derAttrHandler;
this.mappingManager = mappingManager;
this.intAttrNameParser = intAttrNameParser;
+ this.jexlTools = jexlTools;
}
protected Optional<PlainSchema> getPlainSchema(final String schemaName) {
@@ -95,7 +99,7 @@ abstract class AttributableDataBinder {
if (attr == null
&& !schema.isReadonly()
- && JexlUtils.evaluateMandatoryCondition(schema.getMandatoryCondition(), attributable, derAttrHandler)) {
+ && jexlTools.evaluateMandatoryCondition(schema.getMandatoryCondition(), attributable, derAttrHandler)) {
LOG.error("Mandatory schema {} not provided with values", schema.getKey());
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java
index 49a3ef8c2e..3a006249cd 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/AuthModuleDataBinderImpl.java
@@ -27,7 +27,7 @@ import org.apache.syncope.common.lib.types.MappingPurpose;
import org.apache.syncope.core.persistence.api.entity.EntityFactory;
import org.apache.syncope.core.persistence.api.entity.am.AuthModule;
import org.apache.syncope.core.provisioning.api.data.AuthModuleDataBinder;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -37,8 +37,11 @@ public class AuthModuleDataBinderImpl implements AuthModuleDataBinder {
protected final EntityFactory entityFactory;
- public AuthModuleDataBinderImpl(final EntityFactory entityFactory) {
+ protected final JexlTools jexlTools;
+
+ public AuthModuleDataBinderImpl(final EntityFactory entityFactory, final JexlTools jexlTools) {
this.entityFactory = entityFactory;
+ this.jexlTools = jexlTools;
}
protected void populateItems(final AuthModuleTO authModuleTO, final AuthModule authModule) {
@@ -56,9 +59,8 @@ public class AuthModuleDataBinderImpl implements AuthModuleDataBinder {
requiredValuesMissing.getElements().add("intAttrName");
scce.addException(requiredValuesMissing);
} else {
- // no mandatory condition implies mandatory condition false
- if (!JexlUtils.isExpressionValid(itemTO.getMandatoryCondition() == null
- ? "false" : itemTO.getMandatoryCondition())) {
+ if (itemTO.getMandatoryCondition() != null
+ && !jexlTools.isExpressionValid(itemTO.getMandatoryCondition())) {
SyncopeClientException invalidMandatoryCondition =
SyncopeClientException.build(ClientExceptionType.InvalidValues);
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 e8508c4e6e..b459ea7ef3 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
@@ -67,6 +67,7 @@ import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.MappingManager;
import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher;
import org.springframework.data.domain.Pageable;
import org.springframework.transaction.annotation.Transactional;
@@ -93,7 +94,8 @@ public class GroupDataBinderImpl extends AnyDataBinder implements GroupDataBinde
final IntAttrNameParser intAttrNameParser,
final OutboundMatcher outboundMatcher,
final SearchCondVisitor searchCondVisitor,
- final PlainAttrValidationManager validator) {
+ final PlainAttrValidationManager validator,
+ final JexlTools jexlTools) {
super(anyTypeDAO,
realmSearchDAO,
@@ -110,7 +112,8 @@ public class GroupDataBinderImpl extends AnyDataBinder implements GroupDataBinde
mappingManager,
intAttrNameParser,
outboundMatcher,
- validator);
+ validator,
+ jexlTools);
this.searchCondVisitor = searchCondVisitor;
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java
index 75964a410a..1679e3916c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/JEXLItemTransformerImpl.java
@@ -22,7 +22,6 @@ import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.jexl3.JexlContext;
-import org.apache.commons.jexl3.MapContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.mutable.Mutable;
import org.apache.commons.lang3.mutable.MutableObject;
@@ -35,11 +34,15 @@ import org.apache.syncope.core.persistence.api.entity.PlainAttrValue;
import org.apache.syncope.core.provisioning.api.DerAttrHandler;
import org.apache.syncope.core.provisioning.api.MappingManager;
import org.apache.syncope.core.provisioning.api.data.JEXLItemTransformer;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlContextBuilder;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.springframework.beans.factory.annotation.Autowired;
public class JEXLItemTransformerImpl implements JEXLItemTransformer {
+ @Autowired
+ private JexlTools jexlTools;
+
@Autowired
private DerAttrHandler derAttrHandler;
@@ -62,12 +65,13 @@ public class JEXLItemTransformerImpl implements JEXLItemTransformer {
final AttrSchemaType schemaType,
final PlainAttrValue value) {
- JexlContext jexlContext = new MapContext();
+ JexlContextBuilder builder = new JexlContextBuilder();
if (any != null) {
- JexlUtils.addFieldsToContext(any, jexlContext);
- JexlUtils.addPlainAttrsToContext(any.getPlainAttrs(), jexlContext);
- JexlUtils.addDerAttrsToContext(any, derAttrHandler, jexlContext);
+ builder.fields(any).
+ plainAttrs(any.getPlainAttrs()).
+ derAttrs(any, derAttrHandler);
}
+ JexlContext jexlContext = builder.build();
Object oValue;
switch (schemaType) {
@@ -100,7 +104,7 @@ public class JEXLItemTransformerImpl implements JEXLItemTransformer {
}
jexlContext.set("value", oValue);
- Object tValue = JexlUtils.evaluateExpr(propagationJEXL, jexlContext);
+ Object tValue = jexlTools.evaluateExpression(propagationJEXL, jexlContext);
value.setBinaryValue(null);
value.setBooleanValue(null);
@@ -172,15 +176,14 @@ public class JEXLItemTransformerImpl implements JEXLItemTransformer {
if (StringUtils.isNotBlank(pullJEXL) && values != null) {
List<Object> newValues = new ArrayList<>(values.size());
values.forEach(value -> {
- JexlContext jexlContext = new MapContext();
- jexlContext.set("value", value);
- JexlUtils.addFieldsToContext(entityTO, jexlContext);
+ JexlContextBuilder builder = new JexlContextBuilder().with("value", value).fields(entityTO);
if (entityTO instanceof AnyTO anyTO) {
- JexlUtils.addAttrsToContext(anyTO.getPlainAttrs(), jexlContext);
- JexlUtils.addAttrsToContext(anyTO.getDerAttrs(), jexlContext);
+ builder.attrs(anyTO.getPlainAttrs());
+ builder.attrs(anyTO.getDerAttrs());
}
+ JexlContext jexlContext = builder.build();
- newValues.add(JexlUtils.evaluateExpr(pullJEXL, jexlContext));
+ newValues.add(jexlTools.evaluateExpression(pullJEXL, jexlContext));
});
return newValues;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
index e12f3c4bfa..519f7feb6f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/RealmDataBinderImpl.java
@@ -57,9 +57,9 @@ import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.MappingManager;
import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.provisioning.api.data.RealmDataBinder;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
-import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
public class RealmDataBinderImpl extends AttributableDataBinder implements RealmDataBinder {
@@ -77,6 +77,8 @@ public class RealmDataBinderImpl extends AttributableDataBinder implements Realm
protected final EntityFactory entityFactory;
+ protected final TemplateUtils templateUtils;
+
public RealmDataBinderImpl(
final AnyTypeDAO anyTypeDAO,
final AnyTypeClassDAO anyTypeClassDAO,
@@ -89,9 +91,11 @@ public class RealmDataBinderImpl extends AttributableDataBinder implements Realm
final DerAttrHandler derAttrHandler,
final PlainAttrValidationManager validator,
final MappingManager mappingManager,
- final IntAttrNameParser intAttrNameParser) {
+ final IntAttrNameParser intAttrNameParser,
+ final JexlTools jexlTools,
+ final TemplateUtils templateUtils) {
- super(plainSchemaDAO, validator, derAttrHandler, mappingManager, intAttrNameParser);
+ super(plainSchemaDAO, validator, derAttrHandler, mappingManager, intAttrNameParser, jexlTools);
this.anyTypeDAO = anyTypeDAO;
this.anyTypeClassDAO = anyTypeClassDAO;
this.implementationDAO = implementationDAO;
@@ -99,6 +103,7 @@ public class RealmDataBinderImpl extends AttributableDataBinder implements Realm
this.policyDAO = policyDAO;
this.resourceDAO = resourceDAO;
this.entityFactory = entityFactory;
+ this.templateUtils = templateUtils;
}
protected void fill(
@@ -175,7 +180,7 @@ public class RealmDataBinderImpl extends AttributableDataBinder implements Realm
removeIf(implementation -> !realmTO.getActions().contains(implementation.getKey()));
// validate JEXL expressions from templates and proceed if fine
- TemplateUtils.check(realmTO.getTemplates(), ClientExceptionType.InvalidRealm);
+ templateUtils.check(realmTO.getTemplates(), ClientExceptionType.InvalidRealm);
realmTO.getTemplates().forEach((key, template) -> anyTypeDAO.findById(key).ifPresentOrElse(
type -> {
AnyTemplateRealm anyTemplate = realm.getTemplate(type).orElse(null);
@@ -225,7 +230,7 @@ public class RealmDataBinderImpl extends AttributableDataBinder implements Realm
schemaType,
realm);
if (intValues.values().isEmpty()
- && JexlUtils.evaluateMandatoryCondition(item.getMandatoryCondition(), realm, derAttrHandler)) {
+ && jexlTools.evaluateMandatoryCondition(item.getMandatoryCondition(), realm, derAttrHandler)) {
missingAttrNames.add(item.getIntAttrName());
}
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
index a83fb9fcdc..168195496c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/ResourceDataBinderImpl.java
@@ -60,7 +60,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.PushPolicy;
import org.apache.syncope.core.provisioning.api.IntAttrName;
import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.data.ResourceDataBinder;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskExecutor;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.slf4j.Logger;
@@ -88,6 +88,8 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
protected final PropagationTaskExecutor propagationTaskExecutor;
+ protected final JexlTools jexlTools;
+
public ResourceDataBinderImpl(
final AnyTypeDAO anyTypeDAO,
final ConnInstanceDAO connInstanceDAO,
@@ -97,7 +99,8 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
final PlainSchemaDAO plainSchemaDAO,
final EntityFactory entityFactory,
final IntAttrNameParser intAttrNameParser,
- final PropagationTaskExecutor propagationTaskExecutor) {
+ final PropagationTaskExecutor propagationTaskExecutor,
+ final JexlTools jexlTools) {
this.anyTypeDAO = anyTypeDAO;
this.connInstanceDAO = connInstanceDAO;
@@ -108,6 +111,7 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
this.entityFactory = entityFactory;
this.intAttrNameParser = intAttrNameParser;
this.propagationTaskExecutor = propagationTaskExecutor;
+ this.jexlTools = jexlTools;
}
@Override
@@ -265,9 +269,8 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
LOG.error("Only 'name' and 'fullpath' are supported for Realms");
invalidMapping.getElements().add("Only 'name' and 'fullpath' are supported for Realms");
} else {
- // no mandatory condition implies mandatory condition false
- if (!JexlUtils.isExpressionValid(itemTO.getMandatoryCondition() == null
- ? "false" : itemTO.getMandatoryCondition())) {
+ if (itemTO.getMandatoryCondition() != null
+ && !jexlTools.isExpressionValid(itemTO.getMandatoryCondition())) {
SyncopeClientException invalidMandatoryCondition = SyncopeClientException.build(
ClientExceptionType.InvalidValues);
@@ -415,9 +418,8 @@ public class ResourceDataBinderImpl implements ResourceDataBinder {
}
if (allowed) {
- // no mandatory condition implies mandatory condition false
- if (!JexlUtils.isExpressionValid(itemTO.getMandatoryCondition() == null
- ? "false" : itemTO.getMandatoryCondition())) {
+ if (itemTO.getMandatoryCondition() != null
+ && !jexlTools.isExpressionValid(itemTO.getMandatoryCondition())) {
SyncopeClientException invalidMandatoryCondition = SyncopeClientException.build(
ClientExceptionType.InvalidValues);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java
index b447d1dcda..0c8b5e687e 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SchemaDataBinderImpl.java
@@ -41,7 +41,7 @@ import org.apache.syncope.core.persistence.api.entity.EntityFactory;
import org.apache.syncope.core.persistence.api.entity.Implementation;
import org.apache.syncope.core.persistence.api.entity.PlainSchema;
import org.apache.syncope.core.provisioning.api.data.SchemaDataBinder;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -65,6 +65,8 @@ public class SchemaDataBinderImpl implements SchemaDataBinder {
protected final AnyUtilsFactory anyUtilsFactory;
+ protected final JexlTools jexlTools;
+
public SchemaDataBinderImpl(
final AnyTypeClassDAO anyTypeClassDAO,
final PlainSchemaDAO plainSchemaDAO,
@@ -73,7 +75,8 @@ public class SchemaDataBinderImpl implements SchemaDataBinder {
final AnyTypeDAO anyTypeDAO,
final ImplementationDAO implementationDAO,
final EntityFactory entityFactory,
- final AnyUtilsFactory anyUtilsFactory) {
+ final AnyUtilsFactory anyUtilsFactory,
+ final JexlTools jexlTools) {
this.anyTypeClassDAO = anyTypeClassDAO;
this.plainSchemaDAO = plainSchemaDAO;
@@ -83,11 +86,12 @@ public class SchemaDataBinderImpl implements SchemaDataBinder {
this.implementationDAO = implementationDAO;
this.entityFactory = entityFactory;
this.anyUtilsFactory = anyUtilsFactory;
+ this.jexlTools = jexlTools;
}
// --------------- PLAIN -----------------
protected PlainSchema fill(final PlainSchema schema, final PlainSchemaTO schemaTO) {
- if (!JexlUtils.isExpressionValid(schemaTO.getMandatoryCondition())) {
+ if (!jexlTools.isExpressionValid(schemaTO.getMandatoryCondition())) {
SyncopeClientException sce = SyncopeClientException.build(ClientExceptionType.InvalidValues);
sce.getElements().add(schemaTO.getMandatoryCondition());
throw sce;
@@ -225,7 +229,7 @@ public class SchemaDataBinderImpl implements SchemaDataBinder {
requiredValuesMissing.getElements().add("expression");
scce.addException(requiredValuesMissing);
- } else if (!JexlUtils.isExpressionValid(schemaTO.getExpression())) {
+ } else if (!jexlTools.isExpressionValid(schemaTO.getExpression())) {
SyncopeClientException e = SyncopeClientException.build(ClientExceptionType.InvalidValues);
e.getElements().add(schemaTO.getExpression());
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
index 441f999d12..48036dbf3c 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/TaskDataBinderImpl.java
@@ -76,6 +76,7 @@ import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
import org.apache.syncope.core.persistence.api.entity.task.TaskUtils;
import org.apache.syncope.core.persistence.api.entity.task.TaskUtilsFactory;
import org.apache.syncope.core.provisioning.api.data.TaskDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.provisioning.api.job.JobNamer;
import org.apache.syncope.core.provisioning.api.macro.MacroActions;
import org.apache.syncope.core.provisioning.java.job.MacroJobDelegate;
@@ -83,7 +84,6 @@ import org.apache.syncope.core.provisioning.java.job.SyncopeTaskScheduler;
import org.apache.syncope.core.provisioning.java.pushpull.LiveSyncJobDelegate;
import org.apache.syncope.core.provisioning.java.pushpull.PullJobDelegate;
import org.apache.syncope.core.provisioning.java.pushpull.PushJobDelegate;
-import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
import org.apache.syncope.core.spring.implementation.ImplementationManager;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.slf4j.Logger;
@@ -109,6 +109,8 @@ public class TaskDataBinderImpl extends AbstractExecutableDatabinder implements
protected final TaskUtilsFactory taskUtilsFactory;
+ protected final TemplateUtils templateUtils;
+
protected final Map<String, MacroActions> perContextMacroActions = new ConcurrentHashMap<>();
public TaskDataBinderImpl(
@@ -119,7 +121,8 @@ public class TaskDataBinderImpl extends AbstractExecutableDatabinder implements
final ImplementationDAO implementationDAO,
final EntityFactory entityFactory,
final SyncopeTaskScheduler scheduler,
- final TaskUtilsFactory taskUtilsFactory) {
+ final TaskUtilsFactory taskUtilsFactory,
+ final TemplateUtils templateUtils) {
this.realmSearchDAO = realmSearchDAO;
this.resourceDAO = resourceDAO;
@@ -129,6 +132,7 @@ public class TaskDataBinderImpl extends AbstractExecutableDatabinder implements
this.entityFactory = entityFactory;
this.scheduler = scheduler;
this.taskUtilsFactory = taskUtilsFactory;
+ this.templateUtils = templateUtils;
}
protected void fill(final ProvisioningTask<?> provisioningTask, final ProvisioningTaskTO provisioningTaskTO) {
@@ -207,7 +211,7 @@ public class TaskDataBinderImpl extends AbstractExecutableDatabinder implements
}
// validate JEXL expressions from templates and proceed if fine
- TemplateUtils.check(liveSyncTaskTO.getTemplates(), ClientExceptionType.InvalidLiveSyncTask);
+ templateUtils.check(liveSyncTaskTO.getTemplates(), ClientExceptionType.InvalidLiveSyncTask);
liveSyncTaskTO.getTemplates().forEach((type, template) -> anyTypeDAO.findById(type).ifPresentOrElse(
anyType -> {
AnyTemplateLiveSyncTask anyTemplate = liveSyncTask.getTemplate(anyType.getKey()).
@@ -255,7 +259,7 @@ public class TaskDataBinderImpl extends AbstractExecutableDatabinder implements
}
// validate JEXL expressions from templates and proceed if fine
- TemplateUtils.check(pullTaskTO.getTemplates(), ClientExceptionType.InvalidPullTask);
+ templateUtils.check(pullTaskTO.getTemplates(), ClientExceptionType.InvalidPullTask);
pullTaskTO.getTemplates().forEach((type, template) -> anyTypeDAO.findById(type).ifPresentOrElse(
anyType -> {
AnyTemplatePullTask anyTemplate = pullTask.getTemplate(anyType.getKey()).orElse(null);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
index 82bd53dbab..910335fb93 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/UserDataBinderImpl.java
@@ -85,6 +85,7 @@ import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.provisioning.api.UserWorkflowResult;
import org.apache.syncope.core.provisioning.api.UserWorkflowResult.PropagationInfo;
import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.provisioning.java.pushpull.OutboundMatcher;
import org.apache.syncope.core.spring.security.AuthContextUtils;
import org.apache.syncope.core.spring.security.SecurityProperties;
@@ -127,7 +128,8 @@ public class UserDataBinderImpl extends AnyDataBinder implements UserDataBinder
final AccessTokenDAO accessTokenDAO,
final DelegationDAO delegationDAO,
final ConfParamOps confParamOps,
- final SecurityProperties securityProperties) {
+ final SecurityProperties securityProperties,
+ final JexlTools jexlTools) {
super(anyTypeDAO,
realmSearchDAO,
@@ -144,7 +146,8 @@ public class UserDataBinderImpl extends AnyDataBinder implements UserDataBinder
mappingManager,
intAttrNameParser,
outboundMatcher,
- validator);
+ validator,
+ jexlTools);
this.roleDAO = roleDAO;
this.securityQuestionDAO = securityQuestionDAO;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java
index 628bde58a2..fb7201f0d3 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/job/MacroJobDelegate.java
@@ -52,7 +52,7 @@ import org.apache.syncope.core.persistence.api.entity.task.MacroTask;
import org.apache.syncope.core.persistence.api.entity.task.MacroTaskCommand;
import org.apache.syncope.core.persistence.api.entity.task.TaskExec;
import org.apache.syncope.core.persistence.api.utils.FormatUtils;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.provisioning.api.job.JobExecutionContext;
import org.apache.syncope.core.provisioning.api.job.JobExecutionException;
import org.apache.syncope.core.provisioning.api.macro.Command;
@@ -78,6 +78,9 @@ public class MacroJobDelegate extends AbstractSchedTaskJobDelegate<MacroTask> {
@Resource(name = "batchExecutor")
protected AsyncTaskExecutor taskExecutor;
+ @Autowired
+ protected JexlTools jexlTools;
+
protected final Map<String, MacroActions> perContextActions = new ConcurrentHashMap<>();
protected final Map<String, Command<?>> perContextCommands = new ConcurrentHashMap<>();
@@ -299,14 +302,13 @@ public class MacroJobDelegate extends AbstractSchedTaskJobDelegate<MacroTask> {
} else {
args = command.getArgs();
- jexlContext.ifPresent(jc -> ReflectionUtils.doWithFields(
- args.getClass(),
+ jexlContext.ifPresent(jc -> ReflectionUtils.doWithFields(args.getClass(),
field -> {
if (String.class.equals(field.getType())) {
field.setAccessible(true);
Object value = field.get(args);
if (value instanceof final String s) {
- field.set(args, JexlUtils.evaluateTemplate(s, jc));
+ field.set(args, jexlTools.evaluateTemplate(s, jc));
}
}
},
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
index 0ceaa46fe7..3fb10bea32 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/notification/DefaultNotificationManager.java
@@ -68,7 +68,7 @@ import org.apache.syncope.core.provisioning.api.data.AnyObjectDataBinder;
import org.apache.syncope.core.provisioning.api.data.GroupDataBinder;
import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
import org.apache.syncope.core.provisioning.api.event.AfterHandlingEvent;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.provisioning.api.notification.NotificationManager;
import org.apache.syncope.core.provisioning.api.notification.RecipientsProvider;
import org.apache.syncope.core.spring.implementation.ImplementationManager;
@@ -114,6 +114,8 @@ public class DefaultNotificationManager implements NotificationManager {
protected final SearchCondVisitor searchCondVisitor;
+ protected final JexlTools jexlTools;
+
protected Optional<RecipientsProvider> perContextRecipientsProvider = Optional.empty();
public DefaultNotificationManager(
@@ -132,7 +134,8 @@ public class DefaultNotificationManager implements NotificationManager {
final ConfParamOps confParamOps,
final EntityFactory entityFactory,
final IntAttrNameParser intAttrNameParser,
- final SearchCondVisitor searchCondVisitor) {
+ final SearchCondVisitor searchCondVisitor,
+ final JexlTools jexlTools) {
this.derSchemaDAO = derSchemaDAO;
this.notificationDAO = notificationDAO;
@@ -150,6 +153,7 @@ public class DefaultNotificationManager implements NotificationManager {
this.entityFactory = entityFactory;
this.intAttrNameParser = intAttrNameParser;
this.searchCondVisitor = searchCondVisitor;
+ this.jexlTools = jexlTools;
}
@Transactional(readOnly = true)
@@ -222,13 +226,13 @@ public class DefaultNotificationManager implements NotificationManager {
task.setTraceLevel(notification.getTraceLevel());
task.getRecipients().addAll(recipientEmails);
task.setSender(notification.getSender());
- task.setSubject(JexlUtils.evaluateTemplate(notification.getSubject(), ctx));
+ task.setSubject(jexlTools.evaluateTemplate(notification.getSubject(), ctx));
if (StringUtils.isNotBlank(notification.getTemplate().getTextTemplate())) {
- task.setTextBody(JexlUtils.evaluateTemplate(notification.getTemplate().getTextTemplate(), ctx));
+ task.setTextBody(jexlTools.evaluateTemplate(notification.getTemplate().getTextTemplate(), ctx));
}
if (StringUtils.isNotBlank(notification.getTemplate().getHTMLTemplate())) {
- task.setHtmlBody(JexlUtils.evaluateTemplate(notification.getTemplate().getHTMLTemplate(), ctx));
+ task.setHtmlBody(jexlTools.evaluateTemplate(notification.getTemplate().getHTMLTemplate(), ctx));
}
return task;
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationManager.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationManager.java
index 48af445ea6..8b0d5d9751 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationManager.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/DefaultPropagationManager.java
@@ -55,7 +55,7 @@ import org.apache.syncope.core.provisioning.api.DerAttrHandler;
import org.apache.syncope.core.provisioning.api.MappingManager;
import org.apache.syncope.core.provisioning.api.PropagationByResource;
import org.apache.syncope.core.provisioning.api.UserWorkflowResult;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.provisioning.api.propagation.PropagationManager;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
import org.apache.syncope.core.provisioning.java.utils.ConnObjectUtils;
@@ -94,13 +94,16 @@ public class DefaultPropagationManager implements PropagationManager {
protected final AnyUtilsFactory anyUtilsFactory;
+ protected final JexlTools jexlTools;
+
public DefaultPropagationManager(
final ExternalResourceDAO resourceDAO,
final EntityFactory entityFactory,
final ConnObjectUtils connObjectUtils,
final MappingManager mappingManager,
final DerAttrHandler derAttrHandler,
- final AnyUtilsFactory anyUtilsFactory) {
+ final AnyUtilsFactory anyUtilsFactory,
+ final JexlTools jexlTools) {
this.resourceDAO = resourceDAO;
this.entityFactory = entityFactory;
@@ -108,6 +111,7 @@ public class DefaultPropagationManager implements PropagationManager {
this.mappingManager = mappingManager;
this.derAttrHandler = derAttrHandler;
this.anyUtilsFactory = anyUtilsFactory;
+ this.jexlTools = jexlTools;
}
@Override
@@ -385,7 +389,7 @@ public class DefaultPropagationManager implements PropagationManager {
List<String> mandatoryMissing = new ArrayList<>();
List<String> mandatoryNullOrEmpty = new ArrayList<>();
mappingItems.filter(item -> !item.isConnObjectKey()
- && JexlUtils.evaluateMandatoryCondition(item.getMandatoryCondition(), any, derAttrHandler)).
+ && jexlTools.evaluateMandatoryCondition(item.getMandatoryCondition(), any, derAttrHandler)).
forEach(item -> {
Attribute attr = AttributeUtil.find(item.getExtAttrName(), preparedAttrs.attributes());
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java
index c2de716df7..d4c5f310e4 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/propagation/LDAPMembershipPropagationActions.java
@@ -25,7 +25,6 @@ import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.jexl3.JexlContext;
-import org.apache.commons.jexl3.MapContext;
import org.apache.commons.lang3.StringUtils;
import org.apache.syncope.common.lib.request.MembershipUR;
import org.apache.syncope.common.lib.request.UserUR;
@@ -41,7 +40,8 @@ import org.apache.syncope.core.persistence.api.entity.group.Group;
import org.apache.syncope.core.persistence.api.entity.task.PropagationData;
import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.api.DerAttrHandler;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlContextBuilder;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.provisioning.api.propagation.PropagationActions;
import org.apache.syncope.core.provisioning.api.propagation.PropagationTaskInfo;
import org.apache.syncope.core.spring.implementation.InstanceScope;
@@ -66,6 +66,9 @@ public class LDAPMembershipPropagationActions implements PropagationActions {
protected static final Logger LOG = LoggerFactory.getLogger(LDAPMembershipPropagationActions.class);
+ @Autowired
+ protected JexlTools jexlTools;
+
@Autowired
protected DerAttrHandler derAttrHandler;
@@ -87,12 +90,13 @@ public class LDAPMembershipPropagationActions implements PropagationActions {
protected String evaluateGroupConnObjectLink(final String connObjectLinkTemplate, final Group group) {
LOG.debug("Evaluating connObjectLink for {}", group);
- JexlContext jexlContext = new MapContext();
- JexlUtils.addFieldsToContext(group, jexlContext);
- JexlUtils.addPlainAttrsToContext(group.getPlainAttrs(), jexlContext);
- JexlUtils.addDerAttrsToContext(group, derAttrHandler, jexlContext);
+ JexlContext jexlContext = new JexlContextBuilder().
+ fields(group).
+ plainAttrs(group.getPlainAttrs()).
+ derAttrs(group, derAttrHandler).
+ build();
- return JexlUtils.evaluateExpr(connObjectLinkTemplate, jexlContext).toString();
+ return jexlTools.evaluateExpression(connObjectLinkTemplate, jexlContext).toString();
}
protected void buildManagedGroupConnObjectLinks(
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
index 329ca5b328..5fdbd767df 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/pushpull/SinglePullJobDelegate.java
@@ -39,6 +39,7 @@ import org.apache.syncope.core.persistence.api.entity.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.task.AnyTemplatePullTask;
import org.apache.syncope.core.persistence.api.entity.task.PullTask;
import org.apache.syncope.core.provisioning.api.Connector;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.provisioning.api.job.JobExecutionException;
import org.apache.syncope.core.provisioning.api.pushpull.InboundActions;
import org.apache.syncope.core.provisioning.api.pushpull.ProvisioningProfile;
@@ -46,7 +47,6 @@ import org.apache.syncope.core.provisioning.api.pushpull.ReconFilterBuilder;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopePullResultHandler;
import org.apache.syncope.core.provisioning.api.pushpull.SyncopeSinglePullExecutor;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
-import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
import org.identityconnectors.framework.common.objects.ObjectClass;
import org.springframework.beans.factory.annotation.Autowired;
@@ -58,6 +58,9 @@ public class SinglePullJobDelegate extends PullJobDelegate implements SyncopeSin
@Autowired
protected RealmSearchDAO realmSearchDAO;
+ @Autowired
+ protected TemplateUtils templateUtils;
+
@Override
public List<ProvisioningReport> pull(
final ExternalResource resource,
@@ -89,7 +92,7 @@ public class SinglePullJobDelegate extends PullJobDelegate implements SyncopeSin
task.setRemediation(pullTaskTO.isRemediation());
// validate JEXL expressions from templates and proceed if fine
- TemplateUtils.check(pullTaskTO.getTemplates(), ClientExceptionType.InvalidPullTask);
+ templateUtils.check(pullTaskTO.getTemplates(), ClientExceptionType.InvalidPullTask);
pullTaskTO.getTemplates().forEach((type, template) -> anyTypeDAO.findById(type).ifPresentOrElse(
anyType -> {
AnyTemplatePullTask anyTemplate = task.getTemplate(anyType.getKey()).orElse(null);
diff --git a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
index df79c2ecad..bde38d1b7f 100644
--- a/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
+++ b/core/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/utils/ConnObjectUtils.java
@@ -50,6 +50,7 @@ import org.apache.syncope.core.persistence.api.entity.policy.PasswordPolicy;
import org.apache.syncope.core.persistence.api.entity.task.InboundTask;
import org.apache.syncope.core.persistence.api.entity.user.User;
import org.apache.syncope.core.provisioning.api.MappingManager;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.provisioning.api.serialization.POJOHelper;
import org.apache.syncope.core.spring.security.PasswordGenerator;
import org.identityconnectors.common.security.GuardedByteArray;
diff --git a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/OIDCC4UILogicContext.java b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/OIDCC4UILogicContext.java
index c3c2e88318..be6614e234 100644
--- a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/OIDCC4UILogicContext.java
+++ b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/OIDCC4UILogicContext.java
@@ -31,8 +31,8 @@ import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
import org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder;
import org.apache.syncope.core.provisioning.api.data.OIDCC4UIProviderDataBinder;
import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.provisioning.java.pushpull.InboundMatcher;
-import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
import org.apache.syncope.core.spring.security.AuthDataAccessor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
diff --git a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
index 9fc7a35d32..48a8a29a5a 100644
--- a/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
+++ b/ext/oidcc4ui/logic/src/main/java/org/apache/syncope/core/logic/oidc/OIDCUserManager.java
@@ -48,9 +48,9 @@ import org.apache.syncope.core.provisioning.api.ProvisioningManager;
import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
import org.apache.syncope.core.provisioning.api.data.ItemTransformer;
import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.provisioning.java.pushpull.InboundMatcher;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
-import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
import org.apache.syncope.core.spring.implementation.ImplementationManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/ext/oidcc4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCC4UIProviderDataBinderImpl.java b/ext/oidcc4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCC4UIProviderDataBinderImpl.java
index b78c2b4e8b..76f09062ba 100644
--- a/ext/oidcc4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCC4UIProviderDataBinderImpl.java
+++ b/ext/oidcc4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCC4UIProviderDataBinderImpl.java
@@ -38,7 +38,7 @@ import org.apache.syncope.core.persistence.api.entity.OIDCC4UIUserTemplate;
import org.apache.syncope.core.provisioning.api.IntAttrName;
import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.data.OIDCC4UIProviderDataBinder;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -56,18 +56,22 @@ public class OIDCC4UIProviderDataBinderImpl implements OIDCC4UIProviderDataBinde
protected final IntAttrNameParser intAttrNameParser;
+ protected final JexlTools jexlTools;
+
public OIDCC4UIProviderDataBinderImpl(
final AnyTypeDAO anyTypeDAO,
final OIDCC4UIProviderDAO oidcOPDAO,
final ImplementationDAO implementationDAO,
final OIDCC4UIEntityFactory entityFactory,
- final IntAttrNameParser intAttrNameParser) {
+ final IntAttrNameParser intAttrNameParser,
+ final JexlTools jexlTools) {
this.anyTypeDAO = anyTypeDAO;
this.oidcOPDAO = oidcOPDAO;
this.implementationDAO = implementationDAO;
this.entityFactory = entityFactory;
this.intAttrNameParser = intAttrNameParser;
+ this.jexlTools = jexlTools;
}
@Override
@@ -101,9 +105,8 @@ public class OIDCC4UIProviderDataBinderImpl implements OIDCC4UIProviderDataBinde
LOG.error("'{}' not existing", itemTO.getIntAttrName());
invalidMapping.getElements().add('\'' + itemTO.getIntAttrName() + "' not existing");
} else {
- // no mandatory condition implies mandatory condition false
- if (!JexlUtils.isExpressionValid(itemTO.getMandatoryCondition() == null
- ? "false" : itemTO.getMandatoryCondition())) {
+ if (itemTO.getMandatoryCondition() != null
+ && !jexlTools.isExpressionValid(itemTO.getMandatoryCondition())) {
SyncopeClientException invalidMandatoryCondition = SyncopeClientException.build(
ClientExceptionType.InvalidValues);
diff --git a/ext/oidcc4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCC4UIProvisioningContext.java b/ext/oidcc4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCC4UIProvisioningContext.java
index 844584a090..5dc7811a79 100644
--- a/ext/oidcc4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCC4UIProvisioningContext.java
+++ b/ext/oidcc4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/OIDCC4UIProvisioningContext.java
@@ -24,6 +24,7 @@ import org.apache.syncope.core.persistence.api.dao.OIDCC4UIProviderDAO;
import org.apache.syncope.core.persistence.api.entity.OIDCC4UIEntityFactory;
import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.data.OIDCC4UIProviderDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -38,13 +39,15 @@ public class OIDCC4UIProvisioningContext {
final OIDCC4UIProviderDAO oidcOPDAO,
final ImplementationDAO implementationDAO,
final OIDCC4UIEntityFactory entityFactory,
- final IntAttrNameParser intAttrNameParser) {
+ final IntAttrNameParser intAttrNameParser,
+ final JexlTools jexlTools) {
return new OIDCC4UIProviderDataBinderImpl(
anyTypeDAO,
oidcOPDAO,
implementationDAO,
entityFactory,
- intAttrNameParser);
+ intAttrNameParser,
+ jexlTools);
}
}
diff --git a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogicContext.java b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogicContext.java
index 10a084b613..51f439cace 100644
--- a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogicContext.java
+++ b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/SAML2SP4UILogicContext.java
@@ -30,8 +30,8 @@ import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
import org.apache.syncope.core.provisioning.api.data.AccessTokenDataBinder;
import org.apache.syncope.core.provisioning.api.data.SAML2SP4UIIdPDataBinder;
import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.provisioning.java.pushpull.InboundMatcher;
-import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
import org.apache.syncope.core.spring.security.AuthDataAccessor;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
diff --git a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2SP4UIUserManager.java b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2SP4UIUserManager.java
index 3c6bbe1d49..8459b88ad0 100644
--- a/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2SP4UIUserManager.java
+++ b/ext/saml2sp4ui/logic/src/main/java/org/apache/syncope/core/logic/saml2/SAML2SP4UIUserManager.java
@@ -50,9 +50,9 @@ import org.apache.syncope.core.provisioning.api.SAML2SP4UIIdPActions;
import org.apache.syncope.core.provisioning.api.UserProvisioningManager;
import org.apache.syncope.core.provisioning.api.data.ItemTransformer;
import org.apache.syncope.core.provisioning.api.data.UserDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.TemplateUtils;
import org.apache.syncope.core.provisioning.java.pushpull.InboundMatcher;
import org.apache.syncope.core.provisioning.java.utils.MappingUtils;
-import org.apache.syncope.core.provisioning.java.utils.TemplateUtils;
import org.apache.syncope.core.spring.implementation.ImplementationManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
diff --git a/ext/saml2sp4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/SAML2SP4UIProvisioningContext.java b/ext/saml2sp4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/SAML2SP4UIProvisioningContext.java
index 47296eb0db..fa7cfa65be 100644
--- a/ext/saml2sp4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/SAML2SP4UIProvisioningContext.java
+++ b/ext/saml2sp4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/SAML2SP4UIProvisioningContext.java
@@ -24,6 +24,7 @@ import org.apache.syncope.core.persistence.api.dao.SAML2SP4UIIdPDAO;
import org.apache.syncope.core.persistence.api.entity.SAML2SP4UIEntityFactory;
import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.data.SAML2SP4UIIdPDataBinder;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.provisioning.java.data.SAML2SP4UIIdPDataBinderImpl;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
@@ -39,13 +40,15 @@ public class SAML2SP4UIProvisioningContext {
final SAML2SP4UIIdPDAO idapDAO,
final ImplementationDAO implementationDAO,
final SAML2SP4UIEntityFactory entityFactory,
- final IntAttrNameParser intAttrNameParser) {
+ final IntAttrNameParser intAttrNameParser,
+ final JexlTools jexlTools) {
return new SAML2SP4UIIdPDataBinderImpl(
anyTypeDAO,
idapDAO,
implementationDAO,
entityFactory,
- intAttrNameParser);
+ intAttrNameParser,
+ jexlTools);
}
}
diff --git a/ext/saml2sp4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SP4UIIdPDataBinderImpl.java b/ext/saml2sp4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SP4UIIdPDataBinderImpl.java
index ddb21fcf54..5bf8c4d66f 100644
--- a/ext/saml2sp4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SP4UIIdPDataBinderImpl.java
+++ b/ext/saml2sp4ui/provisioning-java/src/main/java/org/apache/syncope/core/provisioning/java/data/SAML2SP4UIIdPDataBinderImpl.java
@@ -39,7 +39,7 @@ import org.apache.syncope.core.persistence.api.entity.SAML2SP4UIUserTemplate;
import org.apache.syncope.core.provisioning.api.IntAttrName;
import org.apache.syncope.core.provisioning.api.IntAttrNameParser;
import org.apache.syncope.core.provisioning.api.data.SAML2SP4UIIdPDataBinder;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -57,18 +57,22 @@ public class SAML2SP4UIIdPDataBinderImpl implements SAML2SP4UIIdPDataBinder {
protected final IntAttrNameParser intAttrNameParser;
+ protected final JexlTools jexlTools;
+
public SAML2SP4UIIdPDataBinderImpl(
final AnyTypeDAO anyTypeDAO,
final SAML2SP4UIIdPDAO idapDAO,
final ImplementationDAO implementationDAO,
final SAML2SP4UIEntityFactory entityFactory,
- final IntAttrNameParser intAttrNameParser) {
+ final IntAttrNameParser intAttrNameParser,
+ final JexlTools jexlTools) {
this.anyTypeDAO = anyTypeDAO;
this.idapDAO = idapDAO;
this.implementationDAO = implementationDAO;
this.entityFactory = entityFactory;
this.intAttrNameParser = intAttrNameParser;
+ this.jexlTools = jexlTools;
}
@Override
@@ -102,9 +106,8 @@ public class SAML2SP4UIIdPDataBinderImpl implements SAML2SP4UIIdPDataBinder {
LOG.error("'{}' not existing", itemTO.getIntAttrName());
invalidMapping.getElements().add('\'' + itemTO.getIntAttrName() + "' not existing");
} else {
- // no mandatory condition implies mandatory condition false
- if (!JexlUtils.isExpressionValid(itemTO.getMandatoryCondition() == null
- ? "false" : itemTO.getMandatoryCondition())) {
+ if (itemTO.getMandatoryCondition() != null
+ && !jexlTools.isExpressionValid(itemTO.getMandatoryCondition())) {
SyncopeClientException invalidMandatoryCondition = SyncopeClientException.build(
ClientExceptionType.InvalidValues);
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 22eb5dda26..c7a95330e9 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
@@ -69,7 +69,8 @@ import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
import org.apache.syncope.core.persistence.api.entity.ExternalResource;
import org.apache.syncope.core.persistence.api.entity.user.UMembership;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlContextBuilder;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.spring.security.AuthDataAccessor;
import org.apache.syncope.ext.scimv2.api.BadRequestException;
import org.apache.syncope.ext.scimv2.api.data.Group;
@@ -139,16 +140,20 @@ public class SCIMDataBinder {
protected final GroupDAO groupDAO;
+ protected final JexlTools jexlTools;
+
public SCIMDataBinder(
final SCIMConfManager confManager,
final UserLogic userLogic,
final AuthDataAccessor authDataAccessor,
- final GroupDAO groupDAO) {
+ final GroupDAO groupDAO,
+ final JexlTools jexlTools) {
this.confManager = confManager;
this.userLogic = userLogic;
this.authDataAccessor = authDataAccessor;
this.groupDAO = groupDAO;
+ this.jexlTools = jexlTools;
}
protected <E extends Enum<?>> void fill(
@@ -524,11 +529,10 @@ public class SCIMDataBinder {
}
switch (schema) {
- case "name":
+ case "name" ->
groupTO.setName(value);
- break;
- default:
+ default ->
groupTO.getPlainAttrs().add(new Attr.Builder(schema).value(value).build());
}
}
@@ -568,12 +572,10 @@ public class SCIMDataBinder {
values.stream().filter(value -> value.getType() != null).forEach(value -> confs.stream().
filter(object -> value.getType().equals(object.getType().name())
- && attrPatches.stream().noneMatch(attrPatch ->
- attrPatch.getAttr().getSchema().equals(object.getValue()))).findFirst().
- ifPresent(conf -> attrs.add(
- new Attr.Builder(conf.getValue()).value(value.getValue()).build())));
+ && attrPatches.stream().noneMatch(p -> p.getAttr().getSchema().equals(object.getValue()))).findFirst().
+ ifPresent(conf -> attrs.add(new Attr.Builder(conf.getValue()).value(value.getValue()).build())));
}
-
+
public void populateUserUR(
final UserUR userUR,
final UserTO before,
@@ -720,48 +722,48 @@ public class SCIMDataBinder {
forEach(address -> conf.getUserConf().getAddresses().stream().
filter(object -> address.getType().equals(object.getType().name())).findFirst().
ifPresent(addressConf -> {
- setAttribute(
- before,
- userUR,
- addressConf.getFormatted(),
- address.getFormatted(),
- op);
+ setAttribute(
+ before,
+ userUR,
+ addressConf.getFormatted(),
+ address.getFormatted(),
+ op);
- setAttribute(
- before,
- userUR,
- addressConf.getStreetAddress(),
- address.getStreetAddress(),
- op);
+ setAttribute(
+ before,
+ userUR,
+ addressConf.getStreetAddress(),
+ address.getStreetAddress(),
+ op);
- setAttribute(
- before,
- userUR,
- addressConf.getLocality(),
- address.getLocality(),
- op);
+ setAttribute(
+ before,
+ userUR,
+ addressConf.getLocality(),
+ address.getLocality(),
+ op);
- setAttribute(
- before,
- userUR,
- addressConf.getRegion(),
- address.getRegion(),
- op);
+ setAttribute(
+ before,
+ userUR,
+ addressConf.getRegion(),
+ address.getRegion(),
+ op);
- setAttribute(
- before,
- userUR,
- addressConf.getPostalCode(),
- address.getPostalCode(),
- op);
+ setAttribute(
+ before,
+ userUR,
+ addressConf.getPostalCode(),
+ address.getPostalCode(),
+ op);
- setAttribute(
- before,
- userUR,
- addressConf.getCountry(),
- address.getCountry(),
- op);
- }));
+ setAttribute(
+ before,
+ userUR,
+ addressConf.getCountry(),
+ address.getCountry(),
+ op);
+ }));
for (int i = 0; i < user.getX509Certificates().size(); i++) {
Value certificate = user.getX509Certificates().get(i);
@@ -827,23 +829,15 @@ public class SCIMDataBinder {
before, userUR, syncopeAttr, user.getExtensionInfo().getAttributes().get(scimAttr), op));
}
- user.getGroups().forEach(group -> {
- if (before.getMembership(group.getValue()).isEmpty()
- && userUR.getMemberships().stream().noneMatch(membershipUR ->
- membershipUR.getGroup().equals(group.getValue()))) {
- userUR.getMemberships().add(new MembershipUR.Builder(group.getValue())
- .operation(PatchOperation.ADD_REPLACE).build());
- }
- });
+ user.getGroups().stream().
+ filter(group -> before.getMembership(group.getValue()).isEmpty()
+ && userUR.getMemberships().stream().noneMatch(m -> m.getGroup().equals(group.getValue()))).
+ forEach(group -> userUR.getMemberships().add(new MembershipUR.Builder(group.getValue()).build()));
- user.getRoles().forEach(role -> {
- if (!before.getRoles().contains(role.getValue())
- && userUR.getRoles().stream().noneMatch(roleUR ->
- roleUR.getValue().equals(role.getValue()))) {
- userUR.getRoles().add(new StringPatchItem.Builder()
- .value(role.getValue()).operation(PatchOperation.ADD_REPLACE).build());
- }
- });
+ user.getRoles().stream().
+ filter(role -> !before.getRoles().contains(role.getValue())
+ && userUR.getRoles().stream().noneMatch(roleUR -> roleUR.getValue().equals(role.getValue()))).
+ forEach(role -> userUR.getRoles().add(new StringPatchItem.Builder().value(role.getValue()).build()));
}
public UserTO toUserTO(final SCIMUser user, final boolean checkSchemas) {
@@ -1068,28 +1062,29 @@ public class SCIMDataBinder {
switch (schema) {
case "username" -> {
if (!value.equals(before.getUsername()) && userUR.getUsername() == null) {
- userUR.setUsername(
- new StringReplacePatchItem.Builder().value(value).operation(PatchOperation.ADD_REPLACE)
- .build());
+ userUR.setUsername(new StringReplacePatchItem.Builder().value(value).build());
}
}
default -> {
if ((before.getPlainAttr(schema).isEmpty()
|| !value.equals(before.getPlainAttr(schema).get().getValues().getFirst()))
- && userUR.getPlainAttrs().stream().noneMatch(attrPatch ->
- attrPatch.getAttr().getSchema().equals(schema))
+ && userUR.getPlainAttrs().stream().noneMatch(p -> p.getAttr().getSchema().equals(schema))
&& op.getOp() != PatchOp.remove) {
- userUR.getPlainAttrs()
- .add(new AttrPatch.Builder(new Attr.Builder(schema).value(value).build()).operation(
- PatchOperation.ADD_REPLACE).build());
+
+ userUR.getPlainAttrs().add(
+ new AttrPatch.Builder(new Attr.Builder(schema).value(value).build()).
+ operation(PatchOperation.ADD_REPLACE).
+ build());
}
if (before.getPlainAttr(schema).isPresent()
- && userUR.getPlainAttrs().stream().noneMatch(attrPatch ->
- attrPatch.getAttr().getSchema().equals(schema))
+ && userUR.getPlainAttrs().stream().noneMatch(p -> p.getAttr().getSchema().equals(schema))
&& op.getOp() == PatchOp.remove) {
- userUR.getPlainAttrs().add(new AttrPatch.Builder(new Attr.Builder(schema).build()).operation(
- PatchOperation.DELETE).build());
+
+ userUR.getPlainAttrs().add(
+ new AttrPatch.Builder(new Attr.Builder(schema).build()).
+ operation(PatchOperation.DELETE).
+ build());
}
}
}
@@ -1114,7 +1109,7 @@ public class SCIMDataBinder {
final SCIMPatchOperation op) {
confs.stream().
- filter(conf -> BooleanUtils.toBoolean(JexlUtils.evaluateExpr(
+ filter(conf -> BooleanUtils.toBoolean(jexlTools.evaluateExpression(
filter2JexlExpression(op.getPath().getFilter()),
new MapContext(Map.of("type", conf.getType().name()))).toString())).findFirst().
ifPresent(conf -> {
@@ -1194,7 +1189,7 @@ public class SCIMDataBinder {
if (group != null && before.getMembership(groupKey).isEmpty()) {
List<? extends ExternalResource> filteredResources = group.getResources().stream()
.filter(resource -> resource.getProvisions().stream()
- .anyMatch(provision -> AnyTypeKind.USER.name().equals(provision.getAnyType())))
+ .anyMatch(provision -> AnyTypeKind.USER.name().equals(provision.getAnyType())))
.toList();
filteredResources.forEach(resource -> resources.add(resource.getKey()));
if (!filteredResources.isEmpty()) {
@@ -1215,9 +1210,9 @@ public class SCIMDataBinder {
switch (op.getPath().getAttribute()) {
case "externalId" ->
- setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- conf.getUserConf().getExternalId(),
- op);
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ conf.getUserConf().getExternalId(),
+ op);
case "userName" -> {
if (op.getOp() != PatchOp.remove && !CollectionUtils.isEmpty(op.getValue())) {
@@ -1279,37 +1274,38 @@ public class SCIMDataBinder {
}
case "displayName" ->
- setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- conf.getUserConf().getDisplayName(),
- op);
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ conf.getUserConf().getDisplayName(),
+ op);
case "nickName" ->
- setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- conf.getUserConf().getNickName(), op);
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ conf.getUserConf().getNickName(), op);
case "profileUrl" ->
- setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- conf.getUserConf().getProfileUrl(),
- op);
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ conf.getUserConf().getProfileUrl(),
+ op);
case "title" ->
- setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- conf.getUserConf().getTitle(), op);
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ conf.getUserConf().getTitle(), op);
case "userType" ->
- setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- conf.getUserConf().getUserType(), op);
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ conf.getUserConf().getUserType(), op);
- case "preferredLanguage" -> setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- conf.getUserConf().getPreferredLanguage(), op);
+ case "preferredLanguage" ->
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ conf.getUserConf().getPreferredLanguage(), op);
case "locale" ->
- setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- conf.getUserConf().getLocale(), op);
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ conf.getUserConf().getLocale(), op);
case "timezone" ->
- setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- conf.getUserConf().getTimezone(), op);
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ conf.getUserConf().getTimezone(), op);
case "emails" -> {
if (!CollectionUtils.isEmpty(op.getValue())
@@ -1361,54 +1357,62 @@ public class SCIMDataBinder {
}
case "addresses" -> {
- if (!CollectionUtils.isEmpty(op.getValue()) && op.getValue()
- .getFirst() instanceof final SCIMUser after) {
- after.getAddresses().stream().filter(address -> address.getType() != null).forEach(
+ if (!CollectionUtils.isEmpty(op.getValue())
+ && op.getValue().getFirst() instanceof final SCIMUser after) {
+
+ after.getAddresses().stream().
+ filter(address -> address.getType() != null).forEach(
address -> conf.getUserConf().getAddresses().stream()
- .filter(object -> address.getType().equals(object.getType().name())).findFirst()
- .ifPresent(addressConf ->
- setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- addressConf, op)));
+ .filter(ac -> address.getType().equals(ac.getType().name())).findFirst()
+ .ifPresent(ac -> setAttribute(userURs.get(numberUR.get().intValue()).
+ getPlainAttrs(), ac, op)));
} else if (op.getPath().getFilter() != null) {
- conf.getUserConf().getAddresses().stream().filter(addressConf -> BooleanUtils.toBoolean(
- JexlUtils.evaluateExpr(filter2JexlExpression(op.getPath().getFilter()),
- new MapContext(Map.of("type", addressConf.getType().name()))).
- toString()))
- .findFirst().ifPresent(addressConf ->
- setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- addressConf, op));
+ conf.getUserConf().getAddresses().stream().
+ filter(address -> BooleanUtils.toBoolean(
+ jexlTools.evaluateExpression(filter2JexlExpression(op.getPath().getFilter()),
+ new JexlContextBuilder().with("type", address.getType().name()).build()).
+ toString())).findFirst()
+ .ifPresent(ac -> setAttribute(userURs.get(numberUR.get().intValue()).
+ getPlainAttrs(), ac, op));
}
}
- case "employeeNumber" -> setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- Optional.ofNullable(conf.getEnterpriseUserConf()).map(SCIMEnterpriseUserConf::getEmployeeNumber)
- .orElse(null), op);
-
- case "costCenter" -> setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- Optional.ofNullable(conf.getEnterpriseUserConf()).map(SCIMEnterpriseUserConf::getCostCenter)
- .orElse(null), op);
-
- case "organization" -> setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- Optional.ofNullable(conf.getEnterpriseUserConf()).map(SCIMEnterpriseUserConf::getOrganization)
- .orElse(null), op);
-
- case "division" -> setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- Optional.ofNullable(conf.getEnterpriseUserConf()).map(SCIMEnterpriseUserConf::getDivision)
- .orElse(null), op);
-
- case "department" -> setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- Optional.ofNullable(conf.getEnterpriseUserConf()).map(SCIMEnterpriseUserConf::getDepartment)
- .orElse(null), op);
-
- case "manager" -> setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- Optional.ofNullable(conf.getEnterpriseUserConf()).map(SCIMEnterpriseUserConf::getManager)
- .map(SCIMManagerConf::getKey).orElse(null), op);
+ case "employeeNumber" ->
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ Optional.ofNullable(conf.getEnterpriseUserConf()).
+ map(SCIMEnterpriseUserConf::getEmployeeNumber).orElse(null), op);
+
+ case "costCenter" ->
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ Optional.ofNullable(conf.getEnterpriseUserConf()).
+ map(SCIMEnterpriseUserConf::getCostCenter).orElse(null), op);
+
+ case "organization" ->
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ Optional.ofNullable(conf.getEnterpriseUserConf()).
+ map(SCIMEnterpriseUserConf::getOrganization).orElse(null), op);
+
+ case "division" ->
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ Optional.ofNullable(conf.getEnterpriseUserConf()).
+ map(SCIMEnterpriseUserConf::getDivision).orElse(null), op);
+
+ case "department" ->
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ Optional.ofNullable(conf.getEnterpriseUserConf()).
+ map(SCIMEnterpriseUserConf::getDepartment).orElse(null), op);
+
+ case "manager" ->
+ setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
+ Optional.ofNullable(conf.getEnterpriseUserConf()).
+ map(SCIMEnterpriseUserConf::getManager).map(SCIMManagerConf::getKey).
+ orElse(null), op);
default -> {
Optional.ofNullable(conf.getExtensionUserConf())
.flatMap(schema -> Optional.ofNullable(schema.asMap().get(op.getPath().getAttribute())))
.ifPresent(schema -> setAttribute(userURs.get(numberUR.get().intValue()).getPlainAttrs(),
- schema, op));
+ schema, op));
}
}
});
@@ -1579,7 +1583,7 @@ public class SCIMDataBinder {
schemas,
new Meta(
"urn:ietf:params:scim:schemas:extension:syncope:2.0:"
- + scimExtensionAnyObjectConf.getType(),
+ + scimExtensionAnyObjectConf.getType(),
anyObjectTO.getCreationDate(),
Optional.ofNullable(anyObjectTO.getLastChangeDate()).orElse(anyObjectTO.getCreationDate()),
anyObjectTO.getETagValue(),
@@ -1614,14 +1618,12 @@ public class SCIMDataBinder {
public AnyObjectTO toAnyObjectTO(final SCIMAnyObject anyObject, final boolean checkSchemas) {
SCIMConf conf = confManager.get();
Set<String> expectedSchemas = new HashSet<>();
- Optional<SCIMExtensionAnyObjectConf> scimExtensionAnyObjectConf =
- conf.getExtensionAnyObjectsConf().stream()
- .filter(scimExtAnyObjectConf ->
- scimExtAnyObjectConf.getType().equals(anyObject.getExtensionUrn()
- .substring(anyObject.getExtensionUrn().lastIndexOf(':') + 1)))
- .findFirst();
- scimExtensionAnyObjectConf.ifPresent(scimExtAnyObjectConf ->
- expectedSchemas.add("urn:ietf:params:scim:schemas:extension:syncope:2.0:"
+ Optional<SCIMExtensionAnyObjectConf> scimExtensionAnyObjectConf = conf.getExtensionAnyObjectsConf().stream()
+ .filter(ec -> ec.getType().equals(anyObject.getExtensionUrn()
+ .substring(anyObject.getExtensionUrn().lastIndexOf(':') + 1)))
+ .findFirst();
+ scimExtensionAnyObjectConf.ifPresent(scimExtAnyObjectConf
+ -> expectedSchemas.add("urn:ietf:params:scim:schemas:extension:syncope:2.0:"
+ scimExtAnyObjectConf.getType()));
if (checkSchemas
&& (!anyObject.getSchemas().containsAll(expectedSchemas)
@@ -1683,7 +1685,7 @@ public class SCIMDataBinder {
setAttribute(anyObjectUR.getPlainAttrs(), scimExtensionAnyObjectConf.get().getExternalId(), op);
}
scimExtensionAnyObjectConf.flatMap(extensionAnyObjectConf -> Optional.of(extensionAnyObjectConf)
- .flatMap(schema -> Optional.ofNullable(schema.asMap().get(op.getPath().getAttribute()))))
+ .flatMap(schema -> Optional.ofNullable(schema.asMap().get(op.getPath().getAttribute()))))
.ifPresent(schema -> setAttribute(anyObjectUR.getPlainAttrs(), schema, op));
}
diff --git a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMLogicContext.java b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMLogicContext.java
index 968dd69dfc..63f21217e2 100644
--- a/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMLogicContext.java
+++ b/ext/scimv2/logic/src/main/java/org/apache/syncope/core/logic/SCIMLogicContext.java
@@ -22,6 +22,7 @@ import org.apache.syncope.common.keymaster.client.api.ConfParamOps;
import org.apache.syncope.core.logic.init.SCIMLoader;
import org.apache.syncope.core.logic.scim.SCIMConfManager;
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.spring.security.AuthDataAccessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
@@ -48,9 +49,10 @@ public class SCIMLogicContext {
final SCIMConfManager confManager,
final UserLogic userLogic,
final AuthDataAccessor authDataAccessor,
- final GroupDAO groupDAO) {
+ final GroupDAO groupDAO,
+ final JexlTools jexlTools) {
- return new SCIMDataBinder(confManager, userLogic, authDataAccessor, groupDAO);
+ return new SCIMDataBinder(confManager, userLogic, authDataAccessor, groupDAO, jexlTools);
}
@ConditionalOnMissingBean
diff --git a/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/SCIMDataBinderTest.java b/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/SCIMDataBinderTest.java
index 0a88e094b3..87f3af282b 100644
--- a/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/SCIMDataBinderTest.java
+++ b/ext/scimv2/logic/src/test/java/org/apache/syncope/core/logic/SCIMDataBinderTest.java
@@ -30,6 +30,9 @@ import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Stream;
+import org.apache.commons.jexl3.JexlBuilder;
+import org.apache.commons.jexl3.JexlEngine;
+import org.apache.commons.jexl3.introspection.JexlPermissions;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.syncope.common.lib.Attr;
import org.apache.syncope.common.lib.SyncopeConstants;
@@ -46,6 +49,8 @@ import org.apache.syncope.common.lib.types.StatusRType;
import org.apache.syncope.core.logic.scim.SCIMConfManager;
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
import org.apache.syncope.core.persistence.api.entity.ExternalResource;
+import org.apache.syncope.core.provisioning.api.jexl.EmptyClassLoader;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.core.spring.security.AuthDataAccessor;
import org.apache.syncope.ext.scimv2.api.data.Group;
import org.apache.syncope.ext.scimv2.api.data.SCIMPatchOp;
@@ -85,7 +90,17 @@ class SCIMDataBinderTest {
UserLogic userLogic = mock(UserLogic.class);
AuthDataAccessor authDataAccessor = mock(AuthDataAccessor.class);
groupDAO = mock(GroupDAO.class);
- dataBinder = new SCIMDataBinder(scimConfManager, userLogic, authDataAccessor, groupDAO);
+
+ JexlEngine jexlEngine = new JexlBuilder().
+ loader(new EmptyClassLoader()).
+ permissions(JexlPermissions.RESTRICTED.compose("java.time.*", "org.apache.syncope.*")).
+ cache(512).
+ silent(false).
+ strict(false).
+ create();
+ JexlTools jexlTools = new JexlTools(jexlEngine);
+
+ dataBinder = new SCIMDataBinder(scimConfManager, userLogic, authDataAccessor, groupDAO, jexlTools);
}
@ParameterizedTest
@@ -116,10 +131,10 @@ class SCIMDataBinderTest {
assertNull(result.getRight());
assertEquals(1, result.getLeft().size());
assertEquals(1, result.getLeft().getFirst().getPlainAttrs().size());
- assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch ->
- PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
- && attrPatch.getAttr().getSchema().equals("surname")
- && attrPatch.getAttr().getValues().contains("Rossini")));
+ assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch
+ -> PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
+ && attrPatch.getAttr().getSchema().equals("surname")
+ && attrPatch.getAttr().getValues().contains("Rossini")));
operations.clear();
operations.add(getOperation("name", "givenName", PatchOp.add, "Gioacchino"));
@@ -130,14 +145,14 @@ class SCIMDataBinderTest {
assertNull(result.getRight());
assertEquals(1, result.getLeft().size());
assertEquals(2, result.getLeft().getFirst().getPlainAttrs().size());
- assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch ->
- PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
- && attrPatch.getAttr().getSchema().equals("firstname")
- && attrPatch.getAttr().getValues().contains("Gioacchino")));
- assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch ->
- PatchOperation.DELETE.equals(attrPatch.getOperation())
- && attrPatch.getAttr().getSchema().equals("surname")
- && attrPatch.getAttr().getValues().isEmpty()));
+ assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch
+ -> PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
+ && attrPatch.getAttr().getSchema().equals("firstname")
+ && attrPatch.getAttr().getValues().contains("Gioacchino")));
+ assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch
+ -> PatchOperation.DELETE.equals(attrPatch.getOperation())
+ && attrPatch.getAttr().getSchema().equals("surname")
+ && attrPatch.getAttr().getValues().isEmpty()));
operations.clear();
operations.add(getOperation("name", "familyName", PatchOp.add, "Verdi"));
@@ -151,14 +166,14 @@ class SCIMDataBinderTest {
assertEquals(2, result.getLeft().getFirst().getPlainAttrs().size());
assertEquals(PatchOperation.ADD_REPLACE, result.getLeft().getFirst().getUsername().getOperation());
assertEquals("gverdi", result.getLeft().getFirst().getUsername().getValue());
- assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch ->
- PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
- && attrPatch.getAttr().getSchema().equals("surname")
- && attrPatch.getAttr().getValues().contains("Verdi")));
- assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch ->
- PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
- && attrPatch.getAttr().getSchema().equals("firstname")
- && attrPatch.getAttr().getValues().contains("Giuseppe")));
+ assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch
+ -> PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
+ && attrPatch.getAttr().getSchema().equals("surname")
+ && attrPatch.getAttr().getValues().contains("Verdi")));
+ assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch
+ -> PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
+ && attrPatch.getAttr().getSchema().equals("firstname")
+ && attrPatch.getAttr().getValues().contains("Giuseppe")));
operations.clear();
operations.add(getOperation("name", "familyName", PatchOp.replace, "Puccini"));
@@ -172,14 +187,14 @@ class SCIMDataBinderTest {
assertEquals(StatusRType.REACTIVATE, result.getRight().getType());
assertEquals(1, result.getLeft().size());
assertEquals(2, result.getLeft().getFirst().getPlainAttrs().size());
- assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch ->
- PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
- && attrPatch.getAttr().getSchema().equals("surname")
- && attrPatch.getAttr().getValues().contains("Puccini")));
- assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch ->
- PatchOperation.DELETE.equals(attrPatch.getOperation())
- && attrPatch.getAttr().getSchema().equals("firstname")
- && attrPatch.getAttr().getValues().isEmpty()));
+ assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch
+ -> PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
+ && attrPatch.getAttr().getSchema().equals("surname")
+ && attrPatch.getAttr().getValues().contains("Puccini")));
+ assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch
+ -> PatchOperation.DELETE.equals(attrPatch.getOperation())
+ && attrPatch.getAttr().getSchema().equals("firstname")
+ && attrPatch.getAttr().getValues().isEmpty()));
UserTO userTO = new UserTO();
userTO.setUsername("bellini");
@@ -211,10 +226,10 @@ class SCIMDataBinderTest {
assertEquals(PatchOperation.ADD_REPLACE, result.getLeft().getFirst().getUsername().getOperation());
assertEquals("bellini", result.getLeft().getFirst().getUsername().getValue());
assertEquals(1, result.getLeft().getFirst().getPlainAttrs().size());
- assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch ->
- PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
- && attrPatch.getAttr().getSchema().equals("surname")
- && attrPatch.getAttr().getValues().contains("Bellini")));
+ assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch
+ -> PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
+ && attrPatch.getAttr().getSchema().equals("surname")
+ && attrPatch.getAttr().getValues().contains("Bellini")));
userTO.setUsername("bellini");
userTO.setSuspended(true);
@@ -230,14 +245,14 @@ class SCIMDataBinderTest {
assertEquals(1, result.getLeft().size());
assertNull(result.getLeft().getFirst().getUsername());
assertEquals(1, result.getLeft().getFirst().getPlainAttrs().size());
- assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch ->
- PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
- && attrPatch.getAttr().getSchema().equals("firstname")
- && attrPatch.getAttr().getValues().contains("Gioacchino")));
+ assertTrue(result.getLeft().getFirst().getPlainAttrs().stream().anyMatch(attrPatch
+ -> PatchOperation.ADD_REPLACE.equals(attrPatch.getOperation())
+ && attrPatch.getAttr().getSchema().equals("firstname")
+ && attrPatch.getAttr().getValues().contains("Gioacchino")));
assertEquals(1, result.getLeft().getFirst().getRoles().size());
- assertTrue(result.getLeft().getFirst().getRoles().stream().anyMatch(role ->
- PatchOperation.ADD_REPLACE.equals(role.getOperation())
- && role.getValue().equals("User reviewer")));
+ assertTrue(result.getLeft().getFirst().getRoles().stream().anyMatch(role
+ -> PatchOperation.ADD_REPLACE.equals(role.getOperation())
+ && role.getValue().equals("User reviewer")));
userTO = new UserTO();
Group group = new Group("37d15e4c-cdc1-460b-a591-8505c8133806", null, "root", null);
@@ -302,16 +317,16 @@ class SCIMDataBinderTest {
assertEquals(3, result.getLeft().size());
assertTrue(result.getLeft().get(0).isEmpty());
assertEquals(2, result.getLeft().get(1).getMemberships().size());
- assertTrue(result.getLeft().get(1).getMemberships().stream().anyMatch(membershipUR ->
- PatchOperation.ADD_REPLACE.equals(membershipUR.getOperation())
- && membershipUR.getGroup().equals("37d15e4c-cdc1-460b-a591-8505c8133806")));
- assertTrue(result.getLeft().get(1).getMemberships().stream().anyMatch(membershipUR ->
- PatchOperation.ADD_REPLACE.equals(membershipUR.getOperation())
- && membershipUR.getGroup().equals("29f96485-729e-4d31-88a1-6fc60e4677f3")));
+ assertTrue(result.getLeft().get(1).getMemberships().stream().anyMatch(membershipUR
+ -> PatchOperation.ADD_REPLACE.equals(membershipUR.getOperation())
+ && membershipUR.getGroup().equals("37d15e4c-cdc1-460b-a591-8505c8133806")));
+ assertTrue(result.getLeft().get(1).getMemberships().stream().anyMatch(membershipUR
+ -> PatchOperation.ADD_REPLACE.equals(membershipUR.getOperation())
+ && membershipUR.getGroup().equals("29f96485-729e-4d31-88a1-6fc60e4677f3")));
assertEquals(1, result.getLeft().get(2).getMemberships().size());
- assertTrue(result.getLeft().get(2).getMemberships().stream().anyMatch(membershipUR ->
- PatchOperation.ADD_REPLACE.equals(membershipUR.getOperation())
- && membershipUR.getGroup().equals("f779c0d4-633b-4be5-8f57-32eb478a3ca5")));
+ assertTrue(result.getLeft().get(2).getMemberships().stream().anyMatch(membershipUR
+ -> PatchOperation.ADD_REPLACE.equals(membershipUR.getOperation())
+ && membershipUR.getGroup().equals("f779c0d4-633b-4be5-8f57-32eb478a3ca5")));
}
private SCIMPatchOperation getOperation(
diff --git a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMv2RESTCXFContext.java b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMv2RESTCXFContext.java
index 15bc81232b..f5a0a228c3 100644
--- a/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMv2RESTCXFContext.java
+++ b/ext/scimv2/scim-rest-cxf/src/main/java/org/apache/syncope/ext/scimv2/cxf/SCIMv2RESTCXFContext.java
@@ -38,6 +38,7 @@ import org.apache.syncope.core.logic.scim.SCIMConfManager;
import org.apache.syncope.core.persistence.api.dao.AnyObjectDAO;
import org.apache.syncope.core.persistence.api.dao.GroupDAO;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.ext.scimv2.api.service.SCIMAnyObjectService;
import org.apache.syncope.ext.scimv2.api.service.SCIMGroupService;
import org.apache.syncope.ext.scimv2.api.service.SCIMService;
@@ -122,8 +123,16 @@ public class SCIMv2RESTCXFContext {
final SCIMConfManager confManager,
final SCIMLogic scimLogic) {
- return new SCIMServiceImpl(userDAO, groupDAO, anyObjectDAO, userLogic, groupLogic, anyObjectLogic, binder,
- confManager, scimLogic);
+ return new SCIMServiceImpl(
+ userDAO,
+ groupDAO,
+ anyObjectDAO,
+ userLogic,
+ groupLogic,
+ anyObjectLogic,
+ binder,
+ confManager,
+ scimLogic);
}
@ConditionalOnMissingBean
@@ -136,10 +145,19 @@ public class SCIMv2RESTCXFContext {
final GroupLogic groupLogic,
final AnyObjectLogic anyObjectLogic,
final SCIMDataBinder binder,
- final SCIMConfManager confManager) {
-
- return new SCIMGroupServiceImpl(userDAO, groupDAO, anyObjectDAO, userLogic, groupLogic, anyObjectLogic, binder,
- confManager);
+ final SCIMConfManager confManager,
+ final JexlTools jexlTools) {
+
+ return new SCIMGroupServiceImpl(
+ userDAO,
+ groupDAO,
+ anyObjectDAO,
+ userLogic,
+ groupLogic,
+ anyObjectLogic,
+ binder,
+ confManager,
+ jexlTools);
}
@ConditionalOnMissingBean
@@ -154,7 +172,14 @@ public class SCIMv2RESTCXFContext {
final SCIMDataBinder binder,
final SCIMConfManager confManager) {
- return new SCIMUserServiceImpl(userDAO, groupDAO, anyObjectDAO, userLogic, groupLogic, anyObjectLogic, binder,
+ return new SCIMUserServiceImpl(
+ userDAO,
+ groupDAO,
+ anyObjectDAO,
+ userLogic,
+ groupLogic,
+ anyObjectLogic,
+ binder,
confManager);
}
@@ -170,7 +195,14 @@ public class SCIMv2RESTCXFContext {
final SCIMDataBinder binder,
final SCIMConfManager confManager) {
- return new SCIMAnyObjectServiceImpl(userDAO, groupDAO, anyObjectDAO, userLogic, groupLogic, anyObjectLogic,
- binder, confManager);
+ return new SCIMAnyObjectServiceImpl(
+ userDAO,
+ groupDAO,
+ anyObjectDAO,
+ userLogic,
+ groupLogic,
+ anyObjectLogic,
+ binder,
+ confManager);
}
}
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 1266596644..24343ff89b 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
@@ -50,7 +50,7 @@ import org.apache.syncope.core.persistence.api.dao.GroupDAO;
import org.apache.syncope.core.persistence.api.dao.UserDAO;
import org.apache.syncope.core.persistence.api.dao.search.MembershipCond;
import org.apache.syncope.core.persistence.api.dao.search.SearchCond;
-import org.apache.syncope.core.provisioning.api.jexl.JexlUtils;
+import org.apache.syncope.core.provisioning.api.jexl.JexlTools;
import org.apache.syncope.ext.scimv2.api.BadRequestException;
import org.apache.syncope.ext.scimv2.api.data.ListResponse;
import org.apache.syncope.ext.scimv2.api.data.Member;
@@ -68,6 +68,8 @@ import org.springframework.util.CollectionUtils;
public class SCIMGroupServiceImpl extends AbstractSCIMService<SCIMGroup> implements SCIMGroupService {
+ protected final JexlTools jexlTools;
+
public SCIMGroupServiceImpl(
final UserDAO userDAO,
final GroupDAO groupDAO,
@@ -76,9 +78,11 @@ public class SCIMGroupServiceImpl extends AbstractSCIMService<SCIMGroup> impleme
final GroupLogic groupLogic,
final AnyObjectLogic anyObjectLogic,
final SCIMDataBinder binder,
- final SCIMConfManager confManager) {
+ final SCIMConfManager confManager,
+ final JexlTools jexlTools) {
super(userDAO, groupDAO, anyObjectDAO, userLogic, groupLogic, anyObjectLogic, binder, confManager);
+ this.jexlTools = jexlTools;
}
private void changeMembership(final String user, final String group, final PatchOp patchOp) {
@@ -157,7 +161,7 @@ public class SCIMGroupServiceImpl extends AbstractSCIMService<SCIMGroup> impleme
if (CollectionUtils.isEmpty(op.getValue())) {
members(id).stream().filter(member -> op.getPath().getFilter() == null
? true
- : BooleanUtils.toBoolean(JexlUtils.evaluateExpr(
+ : BooleanUtils.toBoolean(jexlTools.evaluateExpression(
SCIMDataBinder.filter2JexlExpression(op.getPath().getFilter()),
new MapContext(Map.of("value", member))).toString())).
forEach(member -> changeMembership(member, id, op.getOp()));
diff --git a/pom.xml b/pom.xml
index 6fa7a86d41..e0bc5bf019 100644
--- a/pom.xml
+++ b/pom.xml
@@ -457,7 +457,7 @@ under the License.
<debezium.version>3.0</debezium.version>
- <commons-lang3.version>3.19.0</commons-lang3.version>
+ <commons-lang3.version>3.20.0</commons-lang3.version>
<commons-jexl.version>3.6.0</commons-jexl.version>
<commons-text.version>1.14.0</commons-text.version>
@@ -524,7 +524,7 @@ under the License.
<cargo.deployable.ping.timeout>60000</cargo.deployable.ping.timeout>
<tomcat.version>11.0.14</tomcat.version>
- <wildfly.version>38.0.0.Final</wildfly.version>
+ <wildfly.version>38.0.1.Final</wildfly.version>
<payara.version>6.2025.11</payara.version>
<jakarta.faces.version>4.1.4</jakarta.faces.version>
diff --git a/src/main/asciidoc/reference-guide/concepts/implementations.adoc b/src/main/asciidoc/reference-guide/concepts/implementations.adoc
index d74ed98c1d..1c7ec5f3fd 100644
--- a/src/main/asciidoc/reference-guide/concepts/implementations.adoc
+++ b/src/main/asciidoc/reference-guide/concepts/implementations.adoc
@@ -41,7 +41,7 @@ ifeval::["{snapshotOrRelease}" == "release"]
https://github.com/ilgrosso/syncope/blob/syncope-{docVersion}/core/spring/src/main/resources/META-INF/groovy.blacklist[groovy.blacklist^]
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
-https://github.com/ilgrosso/syncope/blob/4_0_X/core/spring/src/main/resources/META-INF/groovy.blacklist[groovy.blacklist^]
+https://github.com/ilgrosso/syncope/blob/master/core/spring/src/main/resources/META-INF/groovy.blacklist[groovy.blacklist^]
endif::[]
.
====
diff --git a/src/main/asciidoc/reference-guide/usage/core.adoc b/src/main/asciidoc/reference-guide/usage/core.adoc
index 1710e90d91..93264fda9b 100644
--- a/src/main/asciidoc/reference-guide/usage/core.adoc
+++ b/src/main/asciidoc/reference-guide/usage/core.adoc
@@ -66,12 +66,12 @@ header value.
====
The relevant security configuration lies in
ifeval::["{snapshotOrRelease}" == "release"]
-https://github.com/apache/syncope/blob/syncope-{docVersion}/core/spring/src/main/resources/securityContext.xml[securityContext.xml^];
+https://github.com/apache/syncope/blob/syncope-{docVersion}/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java[WebSecurityContext^];
endif::[]
ifeval::["{snapshotOrRelease}" == "snapshot"]
-https://github.com/apache/syncope/blob/master/core/spring/src/main/resources/securityContext.xml[securityContext.xml^];
+https://github.com/apache/syncope/blob/master/core/spring/src/main/java/org/apache/syncope/core/spring/security/WebSecurityContext.java[WebSecurityContext^];
endif::[]
-while normally not needed, this configuration can be anyway customized via the <<override-behavior,override behavior>>.
+while normally not needed, this configuration can be anyway <<extending-configuration,customized>>.
https://en.wikipedia.org/wiki/Basic_access_authentication[HTTP Basic Authentication] is set for use by default.
====
@@ -561,3 +561,15 @@ for example, `/a/b/c` with prefix `o=isp,` becomes `o=isp,ou=c,ou=b,ou=a`
connector object, or empty list if not found
* `syncope:base64Encode(value)` - encodes the given byte array as Base64-encoded string
* `syncope:base64Decode(value)` - decodes the given string as byte array using Base64 encoding
+
+[TIP]
+====
+More custom functions can be added by providing implementations of the
+ifeval::["{snapshotOrRelease}" == "release"]
+https://github.com/apache/syncope/blob/syncope-{docVersion}/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlFunctions.java[JexlFunctions^]
+endif::[]
+ifeval::["{snapshotOrRelease}" == "snapshot"]
+https://github.com/apache/syncope/blob/master/core/provisioning-api/src/main/java/org/apache/syncope/core/provisioning/api/jexl/JexlFunctions.java[JexlFunctions^]
+endif::[]
+interface and registering them as beans, taking care of chosing unique namespace identifiers other than `syncope`.
+====