providers)
return builder.create(true);
}
+ public static ContainerBuilder bootstrapFactories(ContainerBuilder builder) {
+ return builder
+ // TODO: SpringObjectFactoryTest fails when these are SINGLETON
+ .factory(ObjectFactory.class, Scope.PROTOTYPE)
+ .factory(ActionFactory.class, DefaultActionFactory.class, Scope.PROTOTYPE)
+ .factory(ResultFactory.class, DefaultResultFactory.class, Scope.PROTOTYPE)
+ .factory(InterceptorFactory.class, DefaultInterceptorFactory.class, Scope.PROTOTYPE)
+ .factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.PROTOTYPE)
+ .factory(ConverterFactory.class, StrutsConverterFactory.class, Scope.PROTOTYPE)
+ .factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class, Scope.PROTOTYPE)
+
+ .factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
+ .factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
+ .factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
+
+ .factory(XWorkConverter.class, Scope.SINGLETON)
+ .factory(XWorkBasicConverter.class, Scope.SINGLETON)
+ .factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
+ .factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
+ .factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
+ .factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
+ .factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
+
+ .factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
+ .factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
+ .factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
+ .factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
+ .factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
+
+ .factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
+
+ .factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
+ .factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
+ .factory(OgnlUtil.class, Scope.SINGLETON)
+ .factory(SecurityMemberAccess.class, Scope.PROTOTYPE)
+ .factory(OgnlGuard.class, StrutsOgnlGuard.class, Scope.SINGLETON)
+ .factory(ProviderAllowlist.class, Scope.SINGLETON)
+
+ .factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON);
+ }
+
+ public static ContainerBuilder bootstrapTypeConverters(ContainerBuilder builder) {
+ return builder
+ .factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_COLLECTION, CollectionConverter.class, Scope.SINGLETON)
+ .factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_ARRAY, ArrayConverter.class, Scope.SINGLETON)
+ .factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_DATE, DateConverter.class, Scope.SINGLETON)
+ .factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_NUMBER, NumberConverter.class, Scope.SINGLETON)
+ .factory(TypeConverter.class, StrutsConstants.STRUTS_CONVERTER_STRING, StringConverter.class, Scope.SINGLETON);
+ }
+
/**
*
* This builds the internal runtime configuration used by Xwork for finding and configuring Actions from the
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java b/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
index af2eff4d8b..20f3abce88 100644
--- a/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
+++ b/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java
@@ -20,62 +20,24 @@
import com.opensymphony.xwork2.ActionProxyFactory;
import com.opensymphony.xwork2.DefaultActionProxyFactory;
-import com.opensymphony.xwork2.DefaultLocaleProviderFactory;
-import com.opensymphony.xwork2.DefaultTextProvider;
import com.opensymphony.xwork2.DefaultUnknownHandlerManager;
-import com.opensymphony.xwork2.FileManager;
import com.opensymphony.xwork2.FileManagerFactory;
-import com.opensymphony.xwork2.LocaleProviderFactory;
-import com.opensymphony.xwork2.LocalizedTextProvider;
-import com.opensymphony.xwork2.ObjectFactory;
-import com.opensymphony.xwork2.StrutsTextProviderFactory;
-import com.opensymphony.xwork2.TextProvider;
-import com.opensymphony.xwork2.TextProviderFactory;
import com.opensymphony.xwork2.UnknownHandlerManager;
import com.opensymphony.xwork2.config.Configuration;
import com.opensymphony.xwork2.config.ConfigurationException;
import com.opensymphony.xwork2.config.ConfigurationProvider;
import com.opensymphony.xwork2.config.impl.DefaultConfiguration;
-import com.opensymphony.xwork2.conversion.ConversionAnnotationProcessor;
-import com.opensymphony.xwork2.conversion.ConversionFileProcessor;
-import com.opensymphony.xwork2.conversion.ConversionPropertiesProcessor;
import com.opensymphony.xwork2.conversion.NullHandler;
-import com.opensymphony.xwork2.conversion.ObjectTypeDeterminer;
-import com.opensymphony.xwork2.conversion.TypeConverterCreator;
-import com.opensymphony.xwork2.conversion.TypeConverterHolder;
import com.opensymphony.xwork2.conversion.impl.ArrayConverter;
import com.opensymphony.xwork2.conversion.impl.CollectionConverter;
import com.opensymphony.xwork2.conversion.impl.DateConverter;
-import com.opensymphony.xwork2.conversion.impl.DefaultConversionAnnotationProcessor;
-import com.opensymphony.xwork2.conversion.impl.DefaultConversionFileProcessor;
-import com.opensymphony.xwork2.conversion.impl.DefaultObjectTypeDeterminer;
import com.opensymphony.xwork2.conversion.impl.InstantiatingNullHandler;
import com.opensymphony.xwork2.conversion.impl.NumberConverter;
import com.opensymphony.xwork2.conversion.impl.StringConverter;
-import com.opensymphony.xwork2.conversion.impl.XWorkBasicConverter;
-import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
-import com.opensymphony.xwork2.factory.ActionFactory;
-import com.opensymphony.xwork2.factory.ConverterFactory;
-import com.opensymphony.xwork2.factory.DefaultActionFactory;
-import com.opensymphony.xwork2.factory.DefaultInterceptorFactory;
-import com.opensymphony.xwork2.factory.DefaultResultFactory;
-import com.opensymphony.xwork2.factory.DefaultUnknownHandlerFactory;
-import com.opensymphony.xwork2.factory.InterceptorFactory;
-import com.opensymphony.xwork2.factory.ResultFactory;
-import com.opensymphony.xwork2.factory.StrutsConverterFactory;
-import com.opensymphony.xwork2.factory.UnknownHandlerFactory;
import com.opensymphony.xwork2.inject.ContainerBuilder;
import com.opensymphony.xwork2.inject.Scope;
-import com.opensymphony.xwork2.ognl.BeanInfoCacheFactory;
-import com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory;
-import com.opensymphony.xwork2.ognl.DefaultOgnlExpressionCacheFactory;
-import com.opensymphony.xwork2.ognl.ExpressionCacheFactory;
import com.opensymphony.xwork2.ognl.ObjectProxy;
import com.opensymphony.xwork2.ognl.OgnlReflectionContextFactory;
-import com.opensymphony.xwork2.ognl.OgnlReflectionProvider;
-import com.opensymphony.xwork2.ognl.OgnlUtil;
-import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
-import com.opensymphony.xwork2.ognl.SecurityMemberAccess;
import com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor;
import com.opensymphony.xwork2.ognl.accessor.HttpParametersPropertyAccessor;
import com.opensymphony.xwork2.ognl.accessor.ObjectAccessor;
@@ -94,17 +56,11 @@
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
import com.opensymphony.xwork2.security.NotExcludedAcceptedPatternsChecker;
import com.opensymphony.xwork2.util.CompoundRoot;
-import com.opensymphony.xwork2.util.OgnlTextParser;
import com.opensymphony.xwork2.util.PatternMatcher;
-import com.opensymphony.xwork2.util.StrutsLocalizedTextProvider;
-import com.opensymphony.xwork2.util.TextParser;
-import com.opensymphony.xwork2.util.ValueStackFactory;
import com.opensymphony.xwork2.util.WildcardHelper;
-import com.opensymphony.xwork2.util.fs.DefaultFileManager;
import com.opensymphony.xwork2.util.fs.DefaultFileManagerFactory;
import com.opensymphony.xwork2.util.location.LocatableProperties;
import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory;
-import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
import com.opensymphony.xwork2.validator.ActionValidatorManager;
import com.opensymphony.xwork2.validator.AnnotationActionValidatorManager;
import com.opensymphony.xwork2.validator.DefaultActionValidatorManager;
@@ -114,15 +70,10 @@
import com.opensymphony.xwork2.validator.ValidatorFileParser;
import ognl.MethodAccessor;
import ognl.PropertyAccessor;
-import org.apache.struts2.conversion.StrutsConversionPropertiesProcessor;
-import org.apache.struts2.conversion.StrutsTypeConverterCreator;
-import org.apache.struts2.conversion.StrutsTypeConverterHolder;
import org.apache.struts2.dispatcher.HttpParameters;
import org.apache.struts2.dispatcher.Parameter;
import org.apache.struts2.interceptor.exec.ExecutorProvider;
import org.apache.struts2.interceptor.exec.StrutsExecutorProvider;
-import org.apache.struts2.ognl.OgnlGuard;
-import org.apache.struts2.ognl.StrutsOgnlGuard;
import org.apache.struts2.url.QueryStringBuilder;
import org.apache.struts2.url.QueryStringParser;
import org.apache.struts2.url.StrutsQueryStringBuilder;
@@ -162,96 +113,60 @@ public boolean needsReload() {
}
@Override
- public void register(ContainerBuilder builder, LocatableProperties props)
- throws ConfigurationException {
+ public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
- builder
- .factory(ObjectFactory.class)
- .factory(ActionFactory.class, DefaultActionFactory.class)
- .factory(ResultFactory.class, DefaultResultFactory.class)
- .factory(InterceptorFactory.class, DefaultInterceptorFactory.class)
- .factory(com.opensymphony.xwork2.factory.ValidatorFactory.class, com.opensymphony.xwork2.factory.DefaultValidatorFactory.class)
- .factory(ConverterFactory.class, StrutsConverterFactory.class)
- .factory(UnknownHandlerFactory.class, DefaultUnknownHandlerFactory.class)
+ DefaultConfiguration.bootstrapFactories(builder)
+ .factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
- .factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
- .factory(ObjectTypeDeterminer.class, DefaultObjectTypeDeterminer.class, Scope.SINGLETON)
+ .factory(ActionProxyFactory.class, DefaultActionProxyFactory.class, Scope.SINGLETON)
- .factory(XWorkConverter.class, Scope.SINGLETON)
- .factory(XWorkBasicConverter.class, Scope.SINGLETON)
- .factory(ConversionPropertiesProcessor.class, StrutsConversionPropertiesProcessor.class, Scope.SINGLETON)
- .factory(ConversionFileProcessor.class, DefaultConversionFileProcessor.class, Scope.SINGLETON)
- .factory(ConversionAnnotationProcessor.class, DefaultConversionAnnotationProcessor.class, Scope.SINGLETON)
- .factory(TypeConverterCreator.class, StrutsTypeConverterCreator.class, Scope.SINGLETON)
- .factory(TypeConverterHolder.class, StrutsTypeConverterHolder.class, Scope.SINGLETON)
+ .factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
+ .factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
+ .factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
- .factory(FileManager.class, "system", DefaultFileManager.class, Scope.SINGLETON)
- .factory(FileManagerFactory.class, DefaultFileManagerFactory.class, Scope.SINGLETON)
- .factory(ValueStackFactory.class, OgnlValueStackFactory.class, Scope.SINGLETON)
- .factory(ValidatorFactory.class, DefaultValidatorFactory.class, Scope.SINGLETON)
- .factory(ValidatorFileParser.class, DefaultValidatorFileParser.class, Scope.SINGLETON)
- .factory(PatternMatcher.class, WildcardHelper.class, Scope.SINGLETON)
- .factory(ReflectionProvider.class, OgnlReflectionProvider.class, Scope.SINGLETON)
- .factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
+ .factory(ReflectionContextFactory.class, OgnlReflectionContextFactory.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Object.class.getName(), ObjectAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Iterator.class.getName(), XWorkIteratorPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Enumeration.class.getName(), XWorkEnumerationAccessor.class, Scope.SINGLETON)
- .factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
+ .factory(UnknownHandlerManager.class, DefaultUnknownHandlerManager.class, Scope.SINGLETON)
- // silly workarounds for ognl since there is no way to flush its caches
- .factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
- .factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
+ // silly workarounds for ognl since there is no way to flush its caches
+ .factory(PropertyAccessor.class, List.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, ArrayList.class.getName(), XWorkListPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, HashSet.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Set.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, HashMap.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Map.class.getName(), XWorkMapPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Collection.class.getName(), XWorkCollectionPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, ObjectProxy.class.getName(), ObjectProxyPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, HttpParameters.class.getName(), HttpParametersPropertyAccessor.class, Scope.SINGLETON)
+ .factory(PropertyAccessor.class, Parameter.class.getName(), ParameterPropertyAccessor.class, Scope.SINGLETON)
- .factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
- .factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
+ .factory(MethodAccessor.class, Object.class.getName(), XWorkMethodAccessor.class, Scope.SINGLETON)
+ .factory(MethodAccessor.class, CompoundRoot.class.getName(), CompoundRootAccessor.class, Scope.SINGLETON)
- .factory(TextParser.class, OgnlTextParser.class, Scope.SINGLETON)
+ .factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
+ .factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
+ .factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
- .factory(NullHandler.class, Object.class.getName(), InstantiatingNullHandler.class, Scope.SINGLETON)
- .factory(ActionValidatorManager.class, AnnotationActionValidatorManager.class, Scope.SINGLETON)
- .factory(ActionValidatorManager.class, "no-annotations", DefaultActionValidatorManager.class, Scope.SINGLETON)
+ .factory(CollectionConverter.class, Scope.SINGLETON)
+ .factory(ArrayConverter.class, Scope.SINGLETON)
+ .factory(DateConverter.class, Scope.SINGLETON)
+ .factory(NumberConverter.class, Scope.SINGLETON)
+ .factory(StringConverter.class, Scope.SINGLETON)
- .factory(TextProvider.class, "system", DefaultTextProvider.class, Scope.SINGLETON)
- .factory(LocalizedTextProvider.class, StrutsLocalizedTextProvider.class, Scope.SINGLETON)
- .factory(TextProviderFactory.class, StrutsTextProviderFactory.class, Scope.SINGLETON)
- .factory(LocaleProviderFactory.class, DefaultLocaleProviderFactory.class, Scope.SINGLETON)
+ .factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
+ .factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
+ .factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class, Scope.SINGLETON)
- .factory(ExpressionCacheFactory.class, DefaultOgnlExpressionCacheFactory.class, Scope.SINGLETON)
- .factory(BeanInfoCacheFactory.class, DefaultOgnlBeanInfoCacheFactory.class, Scope.SINGLETON)
- .factory(OgnlUtil.class, Scope.SINGLETON)
- .factory(SecurityMemberAccess.class, Scope.PROTOTYPE)
- .factory(OgnlGuard.class, StrutsOgnlGuard.class, Scope.SINGLETON)
- .factory(CollectionConverter.class, Scope.SINGLETON)
- .factory(ArrayConverter.class, Scope.SINGLETON)
- .factory(DateConverter.class, Scope.SINGLETON)
- .factory(NumberConverter.class, Scope.SINGLETON)
- .factory(StringConverter.class, Scope.SINGLETON)
+ .factory(QueryStringBuilder.class, StrutsQueryStringBuilder.class, Scope.SINGLETON)
+ .factory(QueryStringParser.class, StrutsQueryStringParser.class, Scope.SINGLETON)
+ .factory(UrlEncoder.class, StrutsUrlEncoder.class, Scope.SINGLETON)
+ .factory(UrlDecoder.class, StrutsUrlDecoder.class, Scope.SINGLETON)
- .factory(ExcludedPatternsChecker.class, DefaultExcludedPatternsChecker.class, Scope.PROTOTYPE)
- .factory(AcceptedPatternsChecker.class, DefaultAcceptedPatternsChecker.class, Scope.PROTOTYPE)
- .factory(NotExcludedAcceptedPatternsChecker.class, DefaultNotExcludedAcceptedPatternsChecker.class
- , Scope.SINGLETON)
-
- .factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON)
-
- .factory(QueryStringBuilder.class, StrutsQueryStringBuilder.class, Scope.SINGLETON)
- .factory(QueryStringParser.class, StrutsQueryStringParser.class, Scope.SINGLETON)
- .factory(UrlEncoder.class, StrutsUrlEncoder.class, Scope.SINGLETON)
- .factory(UrlDecoder.class, StrutsUrlDecoder.class, Scope.SINGLETON)
-
- .factory(ExecutorProvider.class, StrutsExecutorProvider.class, Scope.SINGLETON)
- ;
+ .factory(ExecutorProvider.class, StrutsExecutorProvider.class, Scope.SINGLETON);
for (Map.Entry entry : DefaultConfiguration.BOOTSTRAP_CONSTANTS.entrySet()) {
props.setProperty(entry.getKey(), String.valueOf(entry.getValue()));
diff --git a/core/src/main/java/com/opensymphony/xwork2/config/providers/XmlDocConfigurationProvider.java b/core/src/main/java/com/opensymphony/xwork2/config/providers/XmlDocConfigurationProvider.java
index 0fcdac0ed5..bae5537891 100644
--- a/core/src/main/java/com/opensymphony/xwork2/config/providers/XmlDocConfigurationProvider.java
+++ b/core/src/main/java/com/opensymphony/xwork2/config/providers/XmlDocConfigurationProvider.java
@@ -45,9 +45,11 @@
import com.opensymphony.xwork2.util.location.Location;
import com.opensymphony.xwork2.util.location.LocationUtils;
import org.apache.commons.lang3.BooleanUtils;
+import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import org.apache.struts2.ognl.ProviderAllowlist;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
@@ -92,8 +94,10 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
protected ObjectFactory objectFactory;
protected Map dtdMappings = new HashMap<>();
protected Configuration configuration;
+ protected ProviderAllowlist providerAllowlist;
protected boolean throwExceptionOnDuplicateBeans = true;
protected ValueSubstitutor valueSubstitutor;
+ protected Set> allowlistClasses = new HashSet<>();
@Inject
public void setObjectFactory(ObjectFactory objectFactory) {
@@ -131,8 +135,22 @@ public void init(Configuration configuration) {
this.configuration = configuration;
}
+ private void registerAllowlist() {
+ providerAllowlist = configuration.getContainer().getInstance(ProviderAllowlist.class);
+ providerAllowlist.registerAllowlist(this, allowlistClasses);
+ }
+
@Override
public void destroy() {
+ providerAllowlist.clearAllowlist(this);
+ }
+
+ protected Class> allowAndLoadClass(String className) throws ClassNotFoundException {
+ Class> clazz = loadClass(className);
+ allowlistClasses.add(clazz);
+ allowlistClasses.addAll(ClassUtils.getAllSuperclasses(clazz));
+ allowlistClasses.addAll(ClassUtils.getAllInterfaces(clazz));
+ return clazz;
}
protected Class> loadClass(String className) throws ClassNotFoundException {
@@ -169,6 +187,7 @@ public static void iterateChildrenByTagName(Element el, String tagName, Consumer
@Override
public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
+ allowlistClasses.clear();
Map loadedBeans = new HashMap<>();
for (Document doc : documents) {
iterateElementChildren(doc, child -> {
@@ -197,7 +216,7 @@ protected void registerBeanSelection(Element child, ContainerBuilder containerBu
String name = child.getAttribute("name");
String impl = child.getAttribute("class");
try {
- Class> classImpl = loadClass(impl);
+ Class> classImpl = ClassLoaderUtil.loadClass(impl, getClass());
if (BeanSelectionProvider.class.isAssignableFrom(classImpl)) {
BeanSelectionProvider provider = (BeanSelectionProvider) classImpl.newInstance();
provider.register(containerBuilder, props);
@@ -303,7 +322,7 @@ public void loadPackages() throws ConfigurationException {
loadExtraConfiguration(doc);
}
- if (reloads.size() > 0) {
+ if (!reloads.isEmpty()) {
reloadRequiredPackages(reloads);
}
@@ -312,6 +331,7 @@ public void loadPackages() throws ConfigurationException {
}
declaredPackages.clear();
+ registerAllowlist();
configuration = null;
}
@@ -442,13 +462,8 @@ protected void addAction(Element actionElement, PackageConfig.Builder packageCon
Location location = DomHelper.getLocationObject(actionElement);
- if (location == null) {
- LOG.warn("Location null for {}", className);
- }
-
- if (!className.isEmpty() && !verifyAction(className, name, location)) {
- LOG.error("Unable to verify action [{}] with class [{}], from [{}]", name, className, location);
- return;
+ if (!className.isEmpty()) {
+ verifyAction(className, name, location);
}
Map results;
@@ -499,27 +514,30 @@ protected ActionConfig buildActionConfig(Element actionElement,
*/
@Deprecated
protected boolean verifyAction(String className, String name, Location loc) {
- return verifyAction(className, loc);
+ verifyAction(className, loc);
+ return true;
}
- protected boolean verifyAction(String className, Location loc) {
+ protected void verifyAction(String className, Location loc) {
if (className.contains("{")) {
LOG.debug("Action class [{}] contains a wildcard replacement value, so it can't be verified", className);
- return true;
+ return;
}
try {
+ Class> clazz = allowAndLoadClass(className);
if (objectFactory.isNoArgConstructorRequired()) {
- Class> clazz = loadClass(className);
if (!Modifier.isPublic(clazz.getModifiers())) {
throw new ConfigurationException("Action class [" + className + "] is not public", loc);
}
clazz.getConstructor();
}
} catch (ClassNotFoundException e) {
- LOG.debug("Class not found for action [{}]", className, e);
- throw new ConfigurationException("Action class [" + className + "] not found", loc);
+ if (objectFactory.isNoArgConstructorRequired()) {
+ throw new ConfigurationException("Action class [" + className + "] not found", e, loc);
+ }
+ LOG.warn("Action class [" + className + "] not found");
+ LOG.debug("Action class [" + className + "] not found", e);
} catch (NoSuchMethodException e) {
- LOG.debug("No constructor found for action [{}]", className, e);
throw new ConfigurationException("Action class [" + className + "] does not have a public no-arg constructor", e, loc);
} catch (RuntimeException ex) {
// Probably not a big deal, like request or session-scoped Spring beans that need a real request
@@ -527,10 +545,8 @@ protected boolean verifyAction(String className, Location loc) {
LOG.debug("Action verification cause", ex);
} catch (Exception ex) {
// Default to failing fast
- LOG.debug("Unable to verify action class [{}]", className, ex);
- throw new ConfigurationException(ex, loc);
+ throw new ConfigurationException("Unable to verify action class [" + className + "]", ex, loc);
}
- return true;
}
protected void addResultTypes(PackageConfig.Builder packageContext, Element element) {
@@ -541,9 +557,7 @@ protected void addResultTypes(PackageConfig.Builder packageContext, Element elem
Location loc = DomHelper.getLocationObject(resultTypeElement);
Class> clazz = verifyResultType(className, loc);
- if (clazz == null) {
- return;
- }
+
String paramName = null;
try {
paramName = (String) clazz.getField("DEFAULT_PARAM").get(null);
@@ -572,11 +586,10 @@ protected ResultTypeConfig buildResultTypeConfig(Element resultTypeElement, Loca
protected Class> verifyResultType(String className, Location loc) {
try {
- return loadClass(className);
+ return allowAndLoadClass(className);
} catch (ClassNotFoundException | NoClassDefFoundError e) {
- LOG.warn("Result class [{}] doesn't exist ({}) at {}, ignoring", className, e.getClass().getSimpleName(), loc, e);
+ throw new ConfigurationException("Result class [" + className + "] not found", e, loc);
}
- return null;
}
/**
@@ -888,7 +901,12 @@ protected void loadDefaultClassRef(PackageConfig.Builder packageContext, Element
NodeList defaultClassRefList = element.getElementsByTagName("default-class-ref");
if (defaultClassRefList.getLength() > 0) {
Element defaultClassRefElement = (Element) defaultClassRefList.item(0);
- packageContext.defaultClassRef(defaultClassRefElement.getAttribute("class"));
+
+ String className = defaultClassRefElement.getAttribute("class");
+ Location location = DomHelper.getLocationObject(defaultClassRefElement);
+ verifyAction(className, location);
+
+ packageContext.defaultClassRef(className);
}
}
@@ -927,10 +945,26 @@ protected void loadInterceptors(PackageConfig.Builder context, Element element)
iterateChildrenByTagName(
element,
"interceptor",
- interceptorElement -> context.addInterceptorConfig(buildInterceptorConfig(interceptorElement)));
+ interceptorElement -> {
+ String className = interceptorElement.getAttribute("class");
+ Location location = DomHelper.getLocationObject(interceptorElement);
+
+ verifyInterceptor(className, location);
+
+ context.addInterceptorConfig(buildInterceptorConfig(interceptorElement));
+ });
loadInterceptorStacks(element, context);
}
+ protected void verifyInterceptor(String className, Location loc) {
+ try {
+ allowAndLoadClass(className);
+ } catch (ClassNotFoundException | NoClassDefFoundError e) {
+ LOG.warn("Interceptor class [" + className + "] at location " + loc + " not found");
+ LOG.debug("Interceptor class [" + className + "] not found", e);
+ }
+ }
+
protected InterceptorConfig buildInterceptorConfig(Element interceptorElement) {
String interceptorName = interceptorElement.getAttribute("name");
String className = interceptorElement.getAttribute("class");
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java
index a9e3045cc9..18a73c47ac 100644
--- a/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/OgnlUtil.java
@@ -863,6 +863,7 @@ protected Map createDefaultContext(Object root, ClassResolver cl
}
SecurityMemberAccess memberAccess = container.getInstance(SecurityMemberAccess.class);
+ memberAccess.useEnforceAllowlistEnabled(Boolean.FALSE.toString());
if (devMode) {
if (!warnReported.get()) {
diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java
index a5d2aa0b43..4600c6f37b 100644
--- a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java
@@ -25,6 +25,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.ognl.ProviderAllowlist;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
@@ -39,6 +40,7 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static com.opensymphony.xwork2.util.ConfigParseUtil.toClassObjectsSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toClassesSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewClassesSet;
import static com.opensymphony.xwork2.util.ConfigParseUtil.toNewPackageNamesSet;
@@ -57,6 +59,19 @@ public class SecurityMemberAccess implements MemberAccess {
private static final Logger LOG = LogManager.getLogger(SecurityMemberAccess.class);
+ private static final Set ALLOWLIST_REQUIRED_PACKAGES = unmodifiableSet(new HashSet<>(Arrays.asList(
+ "org.apache.struts2.components",
+ "org.apache.struts2.views.jsp",
+ "com.opensymphony.xwork2.validator.validators"
+ )));
+
+ private static final Set> ALLOWLIST_REQUIRED_CLASSES = unmodifiableSet(new HashSet<>(Arrays.asList(
+ java.lang.Enum.class,
+ java.util.Date.class,
+ java.util.HashMap.class
+ )));
+
+ private final ProviderAllowlist providerAllowlist;
private boolean allowStaticFieldAccess = true;
private Set excludeProperties = emptySet();
private Set acceptProperties = emptySet();
@@ -65,12 +80,14 @@ public class SecurityMemberAccess implements MemberAccess {
private Set excludedPackageNames = emptySet();
private Set excludedPackageExemptClasses = emptySet();
private boolean enforceAllowlistEnabled = false;
- private Set allowlistClasses = emptySet();
+ private Set> allowlistClasses = emptySet();
private Set allowlistPackageNames = emptySet();
private boolean disallowProxyMemberAccess = false;
private boolean disallowDefaultPackageAccess = false;
- public SecurityMemberAccess() {
+ @Inject
+ public SecurityMemberAccess(@Inject ProviderAllowlist providerAllowlist) {
+ this.providerAllowlist = providerAllowlist;
}
/**
@@ -79,10 +96,11 @@ public SecurityMemberAccess() {
* - block or allow access to properties (configurable-after-construction)
*
* @param allowStaticFieldAccess if set to true static fields (constants) will be accessible
- * @deprecated since 6.4.0, use {@link #SecurityMemberAccess()} instead.
+ * @deprecated since 6.4.0, use {@link #SecurityMemberAccess(ProviderAllowlist)} instead.
*/
@Deprecated
public SecurityMemberAccess(boolean allowStaticFieldAccess) {
+ this(null);
useAllowStaticFieldAccess(String.valueOf(allowStaticFieldAccess));
}
@@ -199,7 +217,11 @@ protected boolean checkAllowlist(Object target, Member member) {
}
protected boolean isClassAllowlisted(Class> clazz) {
- return allowlistClasses.contains(clazz.getName()) || isClassBelongsToPackages(clazz, allowlistPackageNames);
+ return allowlistClasses.contains(clazz)
+ || ALLOWLIST_REQUIRED_CLASSES.contains(clazz)
+ || (providerAllowlist != null && providerAllowlist.getProviderAllowlist().contains(clazz))
+ || isClassBelongsToPackages(clazz, ALLOWLIST_REQUIRED_PACKAGES)
+ || isClassBelongsToPackages(clazz, allowlistPackageNames);
}
/**
@@ -411,7 +433,7 @@ public void useEnforceAllowlistEnabled(String enforceAllowlistEnabled) {
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_CLASSES, required = false)
public void useAllowlistClasses(String commaDelimitedClasses) {
- this.allowlistClasses = toClassesSet(commaDelimitedClasses);
+ this.allowlistClasses = toClassObjectsSet(commaDelimitedClasses);
}
@Inject(value = StrutsConstants.STRUTS_ALLOWLIST_PACKAGE_NAMES, required = false)
diff --git a/core/src/main/java/com/opensymphony/xwork2/util/ConfigParseUtil.java b/core/src/main/java/com/opensymphony/xwork2/util/ConfigParseUtil.java
index 8debd07db0..4617286a2d 100644
--- a/core/src/main/java/com/opensymphony/xwork2/util/ConfigParseUtil.java
+++ b/core/src/main/java/com/opensymphony/xwork2/util/ConfigParseUtil.java
@@ -43,6 +43,11 @@ public static Set toClassesSet(String newDelimitedClasses) throws Config
return unmodifiableSet(classNames);
}
+ public static Set> toClassObjectsSet(String newDelimitedClasses) throws ConfigurationException {
+ Set classNames = commaDelimitedStringToSet(newDelimitedClasses);
+ return unmodifiableSet(validateClasses(classNames, OgnlUtil.class.getClassLoader()));
+ }
+
public static Set toNewClassesSet(Set oldClasses, String newDelimitedClasses) throws ConfigurationException {
Set classNames = commaDelimitedStringToSet(newDelimitedClasses);
validateClasses(classNames, OgnlUtil.class.getClassLoader());
@@ -64,14 +69,16 @@ public static Set toNewPatternsSet(Set oldPatterns, String new
return unmodifiableSet(newPatterns);
}
- public static void validateClasses(Set classNames, ClassLoader validatingClassLoader) throws ConfigurationException {
+ public static Set> validateClasses(Set classNames, ClassLoader validatingClassLoader) throws ConfigurationException {
+ Set> classes = new HashSet<>();
for (String className : classNames) {
try {
- validatingClassLoader.loadClass(className);
+ classes.add(validatingClassLoader.loadClass(className));
} catch (ClassNotFoundException e) {
throw new ConfigurationException("Cannot load class for exclusion/exemption configuration: " + className, e);
}
}
+ return classes;
}
public static Set toPackageNamesSet(String newDelimitedPackageNames) throws ConfigurationException {
diff --git a/core/src/main/java/org/apache/struts2/ognl/ProviderAllowlist.java b/core/src/main/java/org/apache/struts2/ognl/ProviderAllowlist.java
new file mode 100644
index 0000000000..d103728853
--- /dev/null
+++ b/core/src/main/java/org/apache/struts2/ognl/ProviderAllowlist.java
@@ -0,0 +1,73 @@
+/*
+ * 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.struts2.ognl;
+
+import com.opensymphony.xwork2.config.ConfigurationProvider;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import static java.util.Collections.unmodifiableSet;
+
+/**
+ * Allows {@link ConfigurationProvider}s to register classes that should be allowed to be used in OGNL expressions.
+ *
+ * @since 6.4.0
+ */
+public class ProviderAllowlist {
+
+ private final Map>> allowlistMap;
+ private Set> allowlistClasses;
+
+ public ProviderAllowlist() {
+ allowlistMap = new HashMap<>();
+ reconstructAllowlist();
+ }
+
+ public synchronized void registerAllowlist(ConfigurationProvider configurationProvider, Set> allowlist) {
+ Set> existingAllowlist = allowlistMap.get(configurationProvider);
+ if (existingAllowlist != null) {
+ clearAllowlist(configurationProvider);
+ }
+ this.allowlistMap.put(configurationProvider, new HashSet<>(allowlist));
+ this.allowlistClasses.addAll(allowlist);
+ }
+
+ public synchronized void clearAllowlist(ConfigurationProvider configurationProvider) {
+ Set> allowlist = allowlistMap.get(configurationProvider);
+ if (allowlist == null) {
+ return;
+ }
+ this.allowlistMap.remove(configurationProvider);
+ reconstructAllowlist();
+ }
+
+ public Set> getProviderAllowlist() {
+ return unmodifiableSet(allowlistClasses);
+ }
+
+ private void reconstructAllowlist() {
+ this.allowlistClasses = allowlistMap.values().stream().reduce(new HashSet<>(), (a, b) -> {
+ a.addAll(b);
+ return a;
+ });
+ }
+}
diff --git a/core/src/main/resources/struts-beans.xml b/core/src/main/resources/struts-beans.xml
index 273b43b87d..8c751c0c19 100644
--- a/core/src/main/resources/struts-beans.xml
+++ b/core/src/main/resources/struts-beans.xml
@@ -169,6 +169,7 @@
+
diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml
index 90657630e3..0fdcc2b372 100644
--- a/core/src/main/resources/struts-default.xml
+++ b/core/src/main/resources/struts-default.xml
@@ -44,8 +44,6 @@
-
+ java.lang.ThreadLocal
+ "/>
+ java.lang.ThreadLocal
+ "/>
@@ -98,7 +100,8 @@
org.wildfly.extension.undertow.deployment,
org.yaml.snakeyaml,
sun.misc,
- sun.reflect"/>
+ sun.reflect
+ "/>
+ sun.reflect
+ "/>
diff --git a/core/src/test/java/com/opensymphony/xwork2/config/providers/ConfigurationProviderOgnlAllowlistTest.java b/core/src/test/java/com/opensymphony/xwork2/config/providers/ConfigurationProviderOgnlAllowlistTest.java
new file mode 100644
index 0000000000..4fa4aad8ba
--- /dev/null
+++ b/core/src/test/java/com/opensymphony/xwork2/config/providers/ConfigurationProviderOgnlAllowlistTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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 com.opensymphony.xwork2.config.providers;
+
+import com.opensymphony.xwork2.XWorkJUnit4TestCase;
+import com.opensymphony.xwork2.config.ConfigurationProvider;
+import org.apache.struts2.config.StrutsXmlConfigurationProvider;
+import org.apache.struts2.ognl.ProviderAllowlist;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ConfigurationProviderOgnlAllowlistTest extends XWorkJUnit4TestCase {
+
+ private final ConfigurationProvider testXml1 = new StrutsXmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork-test-allowlist.xml");
+ private final ConfigurationProvider testXml2 = new StrutsXmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork-test-allowlist-2.xml");
+ private ProviderAllowlist providerAllowlist;
+
+ @Before
+ public void setUp() throws Exception {
+ loadConfigurationProviders(testXml1, testXml2);
+ providerAllowlist = container.getInstance(ProviderAllowlist.class);
+ }
+
+ @Test
+ public void allowlist() throws Exception {
+ loadConfigurationProviders(testXml1, testXml2);
+ providerAllowlist = container.getInstance(ProviderAllowlist.class);
+
+ assertThat(providerAllowlist.getProviderAllowlist()).containsExactlyInAnyOrder(
+ Class.forName("com.opensymphony.xwork2.interceptor.ValidationAware"),
+ Class.forName("com.opensymphony.xwork2.LocaleProvider"),
+ Class.forName("java.io.Serializable"),
+ Class.forName("com.opensymphony.xwork2.mock.MockResult"),
+ Class.forName("com.opensymphony.xwork2.interceptor.ConditionalInterceptor"),
+ Class.forName("com.opensymphony.xwork2.ActionSupport"),
+ Class.forName("com.opensymphony.xwork2.ActionChainResult"),
+ Class.forName("com.opensymphony.xwork2.TextProvider"),
+ Class.forName("org.apache.struts2.interceptor.NoOpInterceptor"),
+ Class.forName("com.opensymphony.xwork2.interceptor.Interceptor"),
+ Class.forName("java.lang.Object"),
+ Class.forName("com.opensymphony.xwork2.Validateable"),
+ Class.forName("com.opensymphony.xwork2.mock.MockInterceptor"),
+ Class.forName("com.opensymphony.xwork2.Action"),
+ Class.forName("com.opensymphony.xwork2.interceptor.AbstractInterceptor"),
+ Class.forName("com.opensymphony.xwork2.Result"),
+ Class.forName("com.opensymphony.xwork2.SimpleAction")
+ );
+ }
+
+ @Test
+ public void allowlist_1only() throws Exception {
+ loadConfigurationProviders(testXml1);
+ providerAllowlist = container.getInstance(ProviderAllowlist.class);
+
+ assertThat(providerAllowlist.getProviderAllowlist()).containsExactlyInAnyOrder(
+ Class.forName("com.opensymphony.xwork2.interceptor.ValidationAware"),
+ Class.forName("com.opensymphony.xwork2.LocaleProvider"),
+ Class.forName("java.io.Serializable"),
+ Class.forName("com.opensymphony.xwork2.mock.MockResult"),
+ Class.forName("com.opensymphony.xwork2.interceptor.ConditionalInterceptor"),
+ Class.forName("com.opensymphony.xwork2.ActionSupport"),
+ Class.forName("com.opensymphony.xwork2.TextProvider"),
+ Class.forName("com.opensymphony.xwork2.interceptor.Interceptor"),
+ Class.forName("java.lang.Object"),
+ Class.forName("com.opensymphony.xwork2.Validateable"),
+ Class.forName("com.opensymphony.xwork2.mock.MockInterceptor"),
+ Class.forName("com.opensymphony.xwork2.Action"),
+ Class.forName("com.opensymphony.xwork2.interceptor.AbstractInterceptor"),
+ Class.forName("com.opensymphony.xwork2.Result"),
+ Class.forName("com.opensymphony.xwork2.SimpleAction")
+ );
+ }
+
+ @Test
+ public void allowlist_2only() throws Exception {
+ loadConfigurationProviders(testXml2);
+ providerAllowlist = container.getInstance(ProviderAllowlist.class);
+
+ assertThat(providerAllowlist.getProviderAllowlist()).containsExactlyInAnyOrder(
+ Class.forName("com.opensymphony.xwork2.interceptor.ValidationAware"),
+ Class.forName("com.opensymphony.xwork2.LocaleProvider"),
+ Class.forName("java.io.Serializable"),
+ Class.forName("com.opensymphony.xwork2.interceptor.ConditionalInterceptor"),
+ Class.forName("com.opensymphony.xwork2.ActionSupport"),
+ Class.forName("com.opensymphony.xwork2.ActionChainResult"),
+ Class.forName("com.opensymphony.xwork2.TextProvider"),
+ Class.forName("org.apache.struts2.interceptor.NoOpInterceptor"),
+ Class.forName("com.opensymphony.xwork2.interceptor.Interceptor"),
+ Class.forName("java.lang.Object"),
+ Class.forName("com.opensymphony.xwork2.Validateable"),
+ Class.forName("com.opensymphony.xwork2.Action"),
+ Class.forName("com.opensymphony.xwork2.interceptor.AbstractInterceptor"),
+ Class.forName("com.opensymphony.xwork2.Result")
+ );
+ }
+}
diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java
index 3549ed27f8..7e5a22bcc9 100644
--- a/core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java
+++ b/core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java
@@ -24,6 +24,7 @@
import com.opensymphony.xwork2.util.Foo;
import ognl.MemberAccess;
import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.struts2.ognl.ProviderAllowlist;
import org.junit.Before;
import org.junit.Test;
@@ -44,22 +45,28 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
public class SecurityMemberAccessTest {
private Map context;
private FooBar target;
protected SecurityMemberAccess sma;
+ private ProviderAllowlist mockedProviderAllowlist;
@Before
public void setUp() throws Exception {
context = new HashMap<>();
target = new FooBar();
+ mockedProviderAllowlist = mock(ProviderAllowlist.class);
assignNewSma(true);
}
protected void assignNewSma(boolean allowStaticFieldAccess) {
- sma = new SecurityMemberAccess(allowStaticFieldAccess);
+ when(mockedProviderAllowlist.getProviderAllowlist()).thenReturn(new HashSet<>());
+ sma = new SecurityMemberAccess(mockedProviderAllowlist);
+ sma.useAllowStaticFieldAccess(String.valueOf(allowStaticFieldAccess));
}
private T reflectField(String fieldName) throws IllegalAccessException {
diff --git a/core/src/test/java/org/apache/struts2/ognl/ProviderAllowlistTest.java b/core/src/test/java/org/apache/struts2/ognl/ProviderAllowlistTest.java
new file mode 100644
index 0000000000..baf91b9b52
--- /dev/null
+++ b/core/src/test/java/org/apache/struts2/ognl/ProviderAllowlistTest.java
@@ -0,0 +1,88 @@
+/*
+ * 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.struts2.ognl;
+
+import com.opensymphony.xwork2.config.ConfigurationProvider;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+
+import java.util.HashSet;
+
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProviderAllowlistTest {
+
+ @Rule
+ public final MockitoRule mockito = MockitoJUnit.rule();
+
+ private ProviderAllowlist providerAllowlist;
+
+ @Mock
+ private ConfigurationProvider provider1;
+
+ @Mock
+ private ConfigurationProvider provider2;
+
+ @Before
+ public void setUp() throws Exception {
+ providerAllowlist = new ProviderAllowlist();
+ }
+
+ @Test
+ public void registerAllowlist() {
+ providerAllowlist.registerAllowlist(provider1, new HashSet<>(asList(String.class, Integer.class)));
+ providerAllowlist.registerAllowlist(provider2, new HashSet<>(asList(Double.class, Integer.class)));
+
+ assertThat(providerAllowlist.getProviderAllowlist()).containsExactlyInAnyOrder(String.class, Integer.class, Double.class);
+ }
+
+ @Test
+ public void registerAllowlist_twice() {
+ providerAllowlist.registerAllowlist(provider1, new HashSet<>(asList(String.class, Integer.class)));
+ providerAllowlist.registerAllowlist(provider1, new HashSet<>(asList(Double.class, Integer.class)));
+
+ assertThat(providerAllowlist.getProviderAllowlist()).containsExactlyInAnyOrder(Integer.class, Double.class);
+ }
+
+ @Test
+ public void clearAllowlist() {
+ providerAllowlist.registerAllowlist(provider1, new HashSet<>(asList(String.class, Integer.class)));
+ providerAllowlist.registerAllowlist(provider2, new HashSet<>(asList(Double.class, Integer.class)));
+
+ providerAllowlist.clearAllowlist(provider1);
+
+ assertThat(providerAllowlist.getProviderAllowlist()).containsExactlyInAnyOrder(Integer.class, Double.class);
+ }
+
+ @Test
+ public void clearAllowlist_both() {
+ providerAllowlist.registerAllowlist(provider1, new HashSet<>(asList(String.class, Integer.class)));
+ providerAllowlist.registerAllowlist(provider2, new HashSet<>(asList(Double.class, Integer.class)));
+
+ providerAllowlist.clearAllowlist(provider1);
+ providerAllowlist.clearAllowlist(provider2);
+
+ assertThat(providerAllowlist.getProviderAllowlist()).isEmpty();
+ }
+}
diff --git a/core/src/test/resources/com/opensymphony/xwork2/config/providers/xwork-test-allowlist-2.xml b/core/src/test/resources/com/opensymphony/xwork2/config/providers/xwork-test-allowlist-2.xml
new file mode 100644
index 0000000000..f5e9b184d1
--- /dev/null
+++ b/core/src/test/resources/com/opensymphony/xwork2/config/providers/xwork-test-allowlist-2.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/src/test/resources/com/opensymphony/xwork2/config/providers/xwork-test-allowlist.xml b/core/src/test/resources/com/opensymphony/xwork2/config/providers/xwork-test-allowlist.xml
new file mode 100644
index 0000000000..1de061efd1
--- /dev/null
+++ b/core/src/test/resources/com/opensymphony/xwork2/config/providers/xwork-test-allowlist.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+
+
+
+
+ fooDefault
+
+
+
+
+
+
+
+
+ 18
+ 24
+
+
+
+
+
diff --git a/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsTestCase.java b/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsTestCase.java
index 608310838a..f64a9966f1 100644
--- a/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsTestCase.java
+++ b/plugins/junit/src/main/java/org/apache/struts2/junit/StrutsTestCase.java
@@ -21,7 +21,6 @@
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.ActionProxyFactory;
-import com.opensymphony.xwork2.XWorkTestCase;
import com.opensymphony.xwork2.config.Configuration;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.dispatcher.Dispatcher;
diff --git a/plugins/junit/src/main/java/org/apache/struts2/junit/XWorkJUnit4TestCase.java b/plugins/junit/src/main/java/org/apache/struts2/junit/XWorkJUnit4TestCase.java
index d36c3b0adc..12acf283e1 100644
--- a/plugins/junit/src/main/java/org/apache/struts2/junit/XWorkJUnit4TestCase.java
+++ b/plugins/junit/src/main/java/org/apache/struts2/junit/XWorkJUnit4TestCase.java
@@ -18,74 +18,5 @@
*/
package org.apache.struts2.junit;
-import com.opensymphony.xwork2.ActionProxyFactory;
-import com.opensymphony.xwork2.config.Configuration;
-import com.opensymphony.xwork2.config.ConfigurationException;
-import com.opensymphony.xwork2.config.ConfigurationManager;
-import com.opensymphony.xwork2.config.ConfigurationProvider;
-import com.opensymphony.xwork2.inject.Container;
-import com.opensymphony.xwork2.inject.ContainerBuilder;
-import com.opensymphony.xwork2.inject.Context;
-import com.opensymphony.xwork2.inject.Factory;
-import com.opensymphony.xwork2.inject.Scope;
-import com.opensymphony.xwork2.test.StubConfigurationProvider;
-import com.opensymphony.xwork2.util.XWorkTestCaseHelper;
-import com.opensymphony.xwork2.util.location.LocatableProperties;
-import org.junit.After;
-import org.junit.Before;
-
-public abstract class XWorkJUnit4TestCase {
-
- protected ConfigurationManager configurationManager;
- protected Configuration configuration;
- protected Container container;
- protected ActionProxyFactory actionProxyFactory;
-
- @Before
- public void setUp() throws Exception {
- configurationManager = XWorkTestCaseHelper.setUp();
- configuration = configurationManager.getConfiguration();
- container = configuration.getContainer();
- actionProxyFactory = container.getInstance(ActionProxyFactory.class);
- }
-
- @After
- public void tearDown() throws Exception {
- XWorkTestCaseHelper.tearDown(configurationManager);
- configurationManager = null;
- configuration = null;
- container = null;
- actionProxyFactory = null;
- }
-
- protected void loadConfigurationProviders(ConfigurationProvider... providers) {
- configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers);
- configuration = configurationManager.getConfiguration();
- container = configuration.getContainer();
- actionProxyFactory = container.getInstance(ActionProxyFactory.class);
- }
-
- protected void loadButAdd(final Class> type, final Object impl) {
- loadButAdd(type, Container.DEFAULT_NAME, impl);
- }
-
- protected void loadButAdd(final Class> type, final String name, final Object impl) {
- loadConfigurationProviders(new StubConfigurationProvider() {
- @Override
- public void register(ContainerBuilder builder,
- LocatableProperties props) throws ConfigurationException {
- builder.factory(type, name, new Factory() {
- public Object create(Context context) throws Exception {
- return impl;
- }
-
- @Override
- public Class type() {
- return impl.getClass();
- }
- }, Scope.SINGLETON);
- }
- });
- }
-
+public abstract class XWorkJUnit4TestCase extends com.opensymphony.xwork2.XWorkJUnit4TestCase {
}
diff --git a/plugins/junit/src/main/java/org/apache/struts2/junit/XWorkTestCase.java b/plugins/junit/src/main/java/org/apache/struts2/junit/XWorkTestCase.java
new file mode 100644
index 0000000000..e64184ba01
--- /dev/null
+++ b/plugins/junit/src/main/java/org/apache/struts2/junit/XWorkTestCase.java
@@ -0,0 +1,22 @@
+/*
+ * 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.struts2.junit;
+
+public abstract class XWorkTestCase extends com.opensymphony.xwork2.XWorkTestCase {
+}