diff --git a/assembly/pom.xml b/assembly/pom.xml index 90e4c8e96..94677eed6 100644 --- a/assembly/pom.xml +++ b/assembly/pom.xml @@ -104,7 +104,9 @@ + org.apache.maven.plugins maven-assembly-plugin + 3.6.0 make-assembly diff --git a/core/pom.xml b/core/pom.xml index bfc4d6e68..b76bb7615 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -185,6 +185,15 @@ org.apache.commons commons-text + + + + org.hibernate + hibernate-core + 5.6.15.Final + true + + org.springframework spring-test diff --git a/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java b/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java index 88790fc6f..8a4695e57 100644 --- a/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java +++ b/core/src/main/java/com/opensymphony/xwork2/XWorkTestCase.java @@ -36,6 +36,8 @@ import org.apache.commons.lang3.ClassUtils; import java.util.Locale; import java.util.Map; +import static java.util.Collections.singletonMap; + /** * Base JUnit TestCase to extend for XWork specific JUnit tests. Uses * the generic test setup for logic. @@ -56,9 +58,7 @@ public abstract class XWorkTestCase extends TestCase { @Override protected void setUp() throws Exception { configurationManager = XWorkTestCaseHelper.setUp(); - configuration = configurationManager.getConfiguration(); - container = configuration.getContainer(); - actionProxyFactory = container.getInstance(ActionProxyFactory.class); + reloadConfiguration(configurationManager); } @Override @@ -66,13 +66,17 @@ public abstract class XWorkTestCase extends TestCase { XWorkTestCaseHelper.tearDown(configurationManager); } - protected void loadConfigurationProviders(ConfigurationProvider... providers) { - configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers); + private void reloadConfiguration(ConfigurationManager configurationManager) { configuration = configurationManager.getConfiguration(); container = configuration.getContainer(); actionProxyFactory = container.getInstance(ActionProxyFactory.class); } + protected void loadConfigurationProviders(ConfigurationProvider... providers) { + configurationManager = XWorkTestCaseHelper.loadConfigurationProviders(configurationManager, providers); + reloadConfiguration(configurationManager); + } + protected void loadButSet(Map properties) { loadConfigurationProviders(new StubConfigurationProvider() { @Override @@ -115,4 +119,25 @@ public abstract class XWorkTestCase extends TestCase { .getContextMap(); } + protected void setStrutsConstant(String constant, String value) { + setStrutsConstant(singletonMap(constant, value)); + } + + protected void setStrutsConstant(final Map overwritePropeties) { + configurationManager.addContainerProvider(new StubConfigurationProvider() { + @Override + public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { + for (Map.Entry stringStringEntry : overwritePropeties.entrySet()) { + props.setProperty(stringStringEntry.getKey(), stringStringEntry.getValue(), null); + } + } + + @Override + public void destroy() { + } + }); + + configurationManager.reload(); + reloadConfiguration(configurationManager); + } } diff --git a/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java b/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java index 9174e651b..eafff831d 100644 --- a/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java +++ b/core/src/main/java/com/opensymphony/xwork2/config/entities/PackageConfig.java @@ -47,6 +47,7 @@ public class PackageConfig extends Located implements Comparable, protected String name; protected String namespace = ""; protected boolean isAbstract = false; + protected boolean isFinal = false; // a final package is unextendable protected boolean needsRefresh; protected boolean strictMethodInvocation = true; @@ -69,6 +70,7 @@ public class PackageConfig extends Located implements Comparable, this.name = orig.name; this.namespace = orig.namespace; this.isAbstract = orig.isAbstract; + this.isFinal = orig.isFinal; this.needsRefresh = orig.needsRefresh; this.actionConfigs = new LinkedHashMap<>(orig.actionConfigs); this.globalResultConfigs = new LinkedHashMap<>(orig.globalResultConfigs); @@ -85,6 +87,10 @@ public class PackageConfig extends Located implements Comparable, return isAbstract; } + public boolean isFinal() { + return isFinal; + } + public Map getActionConfigs() { return actionConfigs; } @@ -360,6 +366,7 @@ public class PackageConfig extends Located implements Comparable, PackageConfig that = (PackageConfig) o; if (isAbstract != that.isAbstract) return false; + if (isFinal != that.isFinal) return false; if (needsRefresh != that.needsRefresh) return false; if (strictMethodInvocation != that.strictMethodInvocation) return false; if (actionConfigs != null ? !actionConfigs.equals(that.actionConfigs) : that.actionConfigs != null) @@ -404,6 +411,7 @@ public class PackageConfig extends Located implements Comparable, result = 31 * result + name.hashCode(); result = 31 * result + (namespace != null ? namespace.hashCode() : 0); result = 31 * result + (isAbstract ? 1 : 0); + result = 31 * result + (isFinal ? 1 : 0); result = 31 * result + (needsRefresh ? 1 : 0); result = 31 * result + (strictMethodInvocation ? 1 : 0); return result; @@ -453,6 +461,11 @@ public class PackageConfig extends Located implements Comparable, return this; } + public Builder isFinal(boolean isFinal) { + target.isFinal = isFinal; + return this; + } + public Builder defaultInterceptorRef(String name) { target.defaultInterceptorRef = name; return this; diff --git a/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java b/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java index 7bf0e7c77..7c725e15b 100644 --- a/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java +++ b/core/src/main/java/com/opensymphony/xwork2/config/impl/DefaultConfiguration.java @@ -120,6 +120,7 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.TreeMap; import java.util.TreeSet; @@ -459,9 +460,12 @@ public class DefaultConfiguration implements Configuration { boolean appendNamedParameters = Boolean.parseBoolean( container.getInstance(String.class, StrutsConstants.STRUTS_MATCHER_APPEND_NAMED_PARAMETERS) ); + boolean fallbackToEmptyNamespace = Boolean.parseBoolean( + Optional.ofNullable(container.getInstance(String.class, StrutsConstants.STRUTS_ACTION_CONFIG_FALLBACK_TO_EMPTY_NAMESPACE)).orElse("true") + ); return new RuntimeConfigurationImpl(Collections.unmodifiableMap(namespaceActionConfigs), - Collections.unmodifiableMap(namespaceConfigs), matcher, appendNamedParameters); + Collections.unmodifiableMap(namespaceConfigs), matcher, appendNamedParameters, fallbackToEmptyNamespace); } private void setDefaultResults(Map results, PackageConfig packageContext) { @@ -536,14 +540,17 @@ public class DefaultConfiguration implements Configuration { private final Map namespaceActionConfigMatchers; private final NamespaceMatcher namespaceMatcher; private final Map namespaceConfigs; + private final boolean fallbackToEmptyNamespace; public RuntimeConfigurationImpl(Map> namespaceActionConfigs, Map namespaceConfigs, PatternMatcher matcher, - boolean appendNamedParameters) + boolean appendNamedParameters, + boolean fallbackToEmptyNamespace) { this.namespaceActionConfigs = namespaceActionConfigs; this.namespaceConfigs = namespaceConfigs; + this.fallbackToEmptyNamespace = fallbackToEmptyNamespace; this.namespaceActionConfigMatchers = new LinkedHashMap<>(); this.namespaceMatcher = new NamespaceMatcher(matcher, namespaceActionConfigs.keySet(), appendNamedParameters); @@ -583,14 +590,17 @@ public class DefaultConfiguration implements Configuration { } // fail over to empty namespace - if (config == null && StringUtils.isNotBlank(namespace)) { + if (config == null && shouldFallbackToEmptyNamespace(namespace)) { config = findActionConfigInNamespace("", name); } - return config; } + private boolean shouldFallbackToEmptyNamespace(String namespace) { + return StringUtils.isNotBlank(namespace) && ("/".equals(namespace) || fallbackToEmptyNamespace); + } + private ActionConfig findActionConfigInNamespace(String namespace, String name) { ActionConfig config = null; if (namespace == null) { 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 cad52fb79..6de202460 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 @@ -603,8 +603,8 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid */ protected PackageConfig.Builder buildPackageContext(Element packageElement) { String parent = packageElement.getAttribute("extends"); - String abstractVal = packageElement.getAttribute("abstract"); - boolean isAbstract = parseBoolean(abstractVal); + boolean isAbstract = parseBoolean(packageElement.getAttribute("abstract")); + boolean isFinal = parseBoolean(packageElement.getAttribute("final")); String name = defaultString(packageElement.getAttribute("name")); String namespace = defaultString(packageElement.getAttribute("namespace")); @@ -617,6 +617,7 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid PackageConfig.Builder cfg = new PackageConfig.Builder(name) .namespace(namespace) .isAbstract(isAbstract) + .isFinal(isFinal) .strictMethodInvocation(strictDMI) .location(DomHelper.getLocationObject(packageElement)); @@ -627,17 +628,23 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid // has parents, let's look it up List parents = new ArrayList<>(); for (String parentPackageName : ConfigurationUtil.buildParentListFromString(parent)) { - if (configuration.getPackageConfigNames().contains(parentPackageName)) { - parents.add(configuration.getPackageConfig(parentPackageName)); - } else if (declaredPackages.containsKey(parentPackageName)) { - if (configuration.getPackageConfig(parentPackageName) == null) { - addPackage(declaredPackages.get(parentPackageName)); + boolean isParentPackageConfigDefined = false; + if (configuration.getPackageConfigNames().contains(parentPackageName)) { // parent package already added to configuration + isParentPackageConfigDefined = true; + } else if (declaredPackages.containsKey(parentPackageName)) { // parent package declared but yet added to configuration + addPackage(declaredPackages.get(parentPackageName)); + isParentPackageConfigDefined = true; + } + + if (isParentPackageConfigDefined) { + PackageConfig parentPackageConfig = configuration.getPackageConfig(parentPackageName); + if (parentPackageConfig.isFinal()) { + throw new ConfigurationException("Parent package is final and unextendable: " + parentPackageName); } - parents.add(configuration.getPackageConfig(parentPackageName)); + parents.add(parentPackageConfig); } else { throw new ConfigurationException("Parent package is not defined: " + parentPackageName); } - } if (parents.isEmpty()) { diff --git a/core/src/main/java/com/opensymphony/xwork2/ognl/DefaultOgnlCacheFactory.java b/core/src/main/java/com/opensymphony/xwork2/ognl/DefaultOgnlCacheFactory.java index 29dc7fc7f..e503f4998 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/DefaultOgnlCacheFactory.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/DefaultOgnlCacheFactory.java @@ -32,6 +32,7 @@ public class DefaultOgnlCacheFactory implements OgnlCacheFactory implements OgnlCacheFactory buildOgnlCache() { - return buildOgnlCache(getCacheMaxSize(), DEFAULT_INIT_CAPACITY, DEFAULT_LOAD_FACTOR, defaultCacheType); + return buildOgnlCache(getCacheMaxSize(), initialCapacity, DEFAULT_LOAD_FACTOR, defaultCacheType); } @Override 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 510a65c60..43ae99240 100644 --- a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java +++ b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java @@ -31,7 +31,6 @@ import org.apache.struts2.ognl.ThreadAllowlist; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Member; -import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; import java.util.HashSet; @@ -87,6 +86,7 @@ public class SecurityMemberAccess implements MemberAccess { private boolean enforceAllowlistEnabled = false; private Set> allowlistClasses = emptySet(); private Set allowlistPackageNames = emptySet(); + private boolean disallowProxyObjectAccess = false; private boolean disallowProxyMemberAccess = false; private boolean disallowDefaultPackageAccess = false; @@ -160,6 +160,11 @@ public class SecurityMemberAccess implements MemberAccess { } } + if (!checkProxyObjectAccess(target)) { + LOG.warn("Access to proxy is blocked! Target [{}], proxy class [{}]", target, target.getClass().getName()); + return false; + } + if (!checkProxyMemberAccess(target, member)) { LOG.warn("Access to proxy is blocked! Member class [{}] of target [{}], member [{}]", member.getDeclaringClass(), target, member); return false; @@ -286,7 +291,14 @@ public class SecurityMemberAccess implements MemberAccess { } /** - * @return {@code true} if member access is allowed + * @return {@code true} if proxy object access is allowed + */ + protected boolean checkProxyObjectAccess(Object target) { + return !(disallowProxyObjectAccess && ProxyUtil.isProxy(target)); + } + + /** + * @return {@code true} if proxy member access is allowed */ protected boolean checkProxyMemberAccess(Object target, Member member) { return !(disallowProxyMemberAccess && ProxyUtil.isProxyMember(member, target)); @@ -300,10 +312,6 @@ public class SecurityMemberAccess implements MemberAccess { * @return {@code true} if member access is allowed */ protected boolean checkStaticMethodAccess(Member member) { - if (checkEnumAccess(member)) { - LOG.trace("Exempting Enum#values from static method check: class [{}]", member.getDeclaringClass()); - return true; - } return member instanceof Field || !isStatic(member); } @@ -334,17 +342,6 @@ public class SecurityMemberAccess implements MemberAccess { return Modifier.isPublic(member.getModifiers()); } - /** - * @return {@code true} if member access is allowed - */ - protected boolean checkEnumAccess(Member member) { - return member.getDeclaringClass().isEnum() - && isStatic(member) - && member instanceof Method - && member.getName().equals("values") - && ((Method) member).getParameterCount() == 0; - } - protected boolean isPackageExcluded(Class clazz) { return !excludedPackageExemptClasses.contains(clazz.getName()) && (isExcludedPackageNames(clazz) || isExcludedPackageNamePatterns(clazz)); } @@ -448,6 +445,11 @@ public class SecurityMemberAccess implements MemberAccess { this.allowlistPackageNames = toPackageNamesSet(commaDelimitedPackageNames); } + @Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_OBJECT_ACCESS, required = false) + public void useDisallowProxyObjectAccess(String disallowProxyObjectAccess) { + this.disallowProxyObjectAccess = BooleanUtils.toBoolean(disallowProxyObjectAccess); + } + @Inject(value = StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, required = false) public void useDisallowProxyMemberAccess(String disallowProxyMemberAccess) { this.disallowProxyMemberAccess = BooleanUtils.toBoolean(disallowProxyMemberAccess); diff --git a/core/src/main/java/com/opensymphony/xwork2/util/ProxyUtil.java b/core/src/main/java/com/opensymphony/xwork2/util/ProxyUtil.java index 9b0e7d4ea..c169af20b 100644 --- a/core/src/main/java/com/opensymphony/xwork2/util/ProxyUtil.java +++ b/core/src/main/java/com/opensymphony/xwork2/util/ProxyUtil.java @@ -18,13 +18,20 @@ */ package com.opensymphony.xwork2.util; +import com.opensymphony.xwork2.ognl.DefaultOgnlCacheFactory; +import com.opensymphony.xwork2.ognl.OgnlCache; +import com.opensymphony.xwork2.ognl.OgnlCacheFactory; import org.apache.commons.lang3.reflect.ConstructorUtils; import org.apache.commons.lang3.reflect.FieldUtils; import org.apache.commons.lang3.reflect.MethodUtils; +import org.hibernate.proxy.HibernateProxy; -import java.lang.reflect.*; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; /** * ProxyUtil @@ -38,11 +45,13 @@ public class ProxyUtil { private static final String SPRING_SPRINGPROXY_CLASS_NAME = "org.springframework.aop.SpringProxy"; private static final String SPRING_SINGLETONTARGETSOURCE_CLASS_NAME = "org.springframework.aop.target.SingletonTargetSource"; private static final String SPRING_TARGETCLASSAWARE_CLASS_NAME = "org.springframework.aop.TargetClassAware"; - - private static final Map, Boolean> isProxyCache = - new ConcurrentHashMap<>(256); - private static final Map isProxyMemberCache = - new ConcurrentHashMap<>(256); + private static final String HIBERNATE_HIBERNATEPROXY_CLASS_NAME = "org.hibernate.proxy.HibernateProxy"; + private static final int CACHE_MAX_SIZE = 10000; + private static final int CACHE_INITIAL_CAPACITY = 256; + private static final OgnlCache, Boolean> isProxyCache = new DefaultOgnlCacheFactory, Boolean>( + CACHE_MAX_SIZE, OgnlCacheFactory.CacheType.WTLFU, CACHE_INITIAL_CAPACITY).buildOgnlCache(); + private static final OgnlCache isProxyMemberCache = new DefaultOgnlCacheFactory( + CACHE_MAX_SIZE, OgnlCacheFactory.CacheType.WTLFU, CACHE_INITIAL_CAPACITY).buildOgnlCache(); /** * Determine the ultimate target class of the given instance, traversing @@ -75,7 +84,7 @@ public class ProxyUtil { return flag; } - boolean isProxy = isSpringAopProxy(object); + boolean isProxy = isSpringAopProxy(object) || isHibernateProxy(object); isProxyCache.put(clazz, isProxy); return isProxy; @@ -87,7 +96,7 @@ public class ProxyUtil { * @param object the object to check */ public static boolean isProxyMember(Member member, Object object) { - if (!Modifier.isStatic(member.getModifiers()) && !isProxy(object)) { + if (!Modifier.isStatic(member.getModifiers()) && !isProxy(object) && !isHibernateProxy(object)) { return false; } @@ -96,12 +105,40 @@ public class ProxyUtil { return flag; } - boolean isProxyMember = isSpringProxyMember(member); + boolean isProxyMember = isSpringProxyMember(member) || isHibernateProxyMember(member); isProxyMemberCache.put(member, isProxyMember); return isProxyMember; } + /** + * Check whether the given object is a Hibernate proxy. + * + * @param object the object to check + */ + public static boolean isHibernateProxy(Object object) { + try { + return HibernateProxy.class.isAssignableFrom(object.getClass()); + } catch (NoClassDefFoundError ignored) { + return false; + } + } + + /** + * Check whether the given member is a member of a Hibernate proxy. + * + * @param member the member to check + */ + public static boolean isHibernateProxyMember(Member member) { + try { + Class clazz = ClassLoaderUtil.loadClass(HIBERNATE_HIBERNATEPROXY_CLASS_NAME, ProxyUtil.class); + return hasMember(clazz, member); + } catch (ClassNotFoundException ignored) { + } + + return false; + } + /** * Determine the ultimate target class of the given spring bean instance, traversing * not only a top-level spring proxy but any number of nested spring proxies as well — diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java index e85ce9832..6383236a2 100644 --- a/core/src/main/java/org/apache/struts2/StrutsConstants.java +++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java @@ -237,6 +237,8 @@ public final class StrutsConstants { public static final String STRUTS_XWORKCONVERTER = "struts.xworkConverter"; public static final String STRUTS_ALWAYS_SELECT_FULL_NAMESPACE = "struts.mapper.alwaysSelectFullNamespace"; + /** Fallback to empty namespace when request namespace didn't match any in action configuration */ + public static final String STRUTS_ACTION_CONFIG_FALLBACK_TO_EMPTY_NAMESPACE = "struts.actionConfig.fallbackToEmptyNamespace"; /** The {@link com.opensymphony.xwork2.LocaleProviderFactory} implementation class */ public static final String STRUTS_LOCALE_PROVIDER_FACTORY = "struts.localeProviderFactory"; @@ -487,6 +489,7 @@ public final class StrutsConstants { public static final String STRUTS_TEXT_PROVIDER_FACTORY = "struts.textProviderFactory"; public static final String STRUTS_LOCALIZED_TEXT_PROVIDER = "struts.localizedTextProvider"; + public static final String STRUTS_DISALLOW_PROXY_OBJECT_ACCESS = "struts.disallowProxyObjectAccess"; public static final String STRUTS_DISALLOW_PROXY_MEMBER_ACCESS = "struts.disallowProxyMemberAccess"; public static final String STRUTS_DISALLOW_DEFAULT_PACKAGE_ACCESS = "struts.disallowDefaultPackageAccess"; diff --git a/core/src/main/java/org/apache/struts2/config/StrutsXmlConfigurationProvider.java b/core/src/main/java/org/apache/struts2/config/StrutsXmlConfigurationProvider.java index b3624b832..51dded4cf 100644 --- a/core/src/main/java/org/apache/struts2/config/StrutsXmlConfigurationProvider.java +++ b/core/src/main/java/org/apache/struts2/config/StrutsXmlConfigurationProvider.java @@ -54,6 +54,7 @@ public class StrutsXmlConfigurationProvider extends XmlConfigurationProvider { put("-//Apache Software Foundation//DTD Struts Configuration 2.3//EN", "struts-2.3.dtd"); put("-//Apache Software Foundation//DTD Struts Configuration 2.5//EN", "struts-2.5.dtd"); put("-//Apache Software Foundation//DTD Struts Configuration 6.0//EN", "struts-6.0.dtd"); + put("-//Apache Software Foundation//DTD Struts Configuration 6.5//EN", "struts-6.5.dtd"); }}); private File baseDir = null; private final String filename; diff --git a/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java b/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java index 96252a029..081584c78 100644 --- a/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java +++ b/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java @@ -90,6 +90,7 @@ public class ConstantConfig { private Boolean freemarkerWrapperAltMap; private BeanConfig xworkConverter; private Boolean mapperAlwaysSelectFullNamespace; + private Boolean actionConfigFallbackToEmptyNamespace; private BeanConfig localeProviderFactory; private String mapperIdParameterName; private Boolean ognlAllowStaticFieldAccess; @@ -145,6 +146,7 @@ public class ConstantConfig { private String strictMethodInvocationMethodRegex; private BeanConfig textProviderFactory; private BeanConfig localizedTextProvider; + private Boolean disallowProxyObjectAccess; private Boolean disallowProxyMemberAccess; private Integer ognlAutoGrowthCollectionLimit; private String staticContentPath; @@ -225,6 +227,7 @@ public class ConstantConfig { map.put(StrutsConstants.STRUTS_FREEMARKER_WRAPPER_ALT_MAP, Objects.toString(freemarkerWrapperAltMap, null)); map.put(StrutsConstants.STRUTS_XWORKCONVERTER, beanConfToString(xworkConverter)); map.put(StrutsConstants.STRUTS_ALWAYS_SELECT_FULL_NAMESPACE, Objects.toString(mapperAlwaysSelectFullNamespace, null)); + map.put(StrutsConstants.STRUTS_ACTION_CONFIG_FALLBACK_TO_EMPTY_NAMESPACE, Objects.toString(actionConfigFallbackToEmptyNamespace, null)); map.put(StrutsConstants.STRUTS_LOCALE_PROVIDER_FACTORY, beanConfToString(localeProviderFactory)); map.put(StrutsConstants.STRUTS_ID_PARAMETER_NAME, mapperIdParameterName); map.put(StrutsConstants.STRUTS_ALLOW_STATIC_FIELD_ACCESS, Objects.toString(ognlAllowStaticFieldAccess, null)); @@ -279,6 +282,7 @@ public class ConstantConfig { map.put(StrutsConstants.STRUTS_SMI_METHOD_REGEX, strictMethodInvocationMethodRegex); map.put(StrutsConstants.STRUTS_TEXT_PROVIDER_FACTORY, beanConfToString(textProviderFactory)); map.put(StrutsConstants.STRUTS_LOCALIZED_TEXT_PROVIDER, beanConfToString(localizedTextProvider)); + map.put(StrutsConstants.STRUTS_DISALLOW_PROXY_OBJECT_ACCESS, Objects.toString(disallowProxyObjectAccess, null)); map.put(StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, Objects.toString(disallowProxyMemberAccess, null)); map.put(StrutsConstants.STRUTS_OGNL_AUTO_GROWTH_COLLECTION_LIMIT, Objects.toString(ognlAutoGrowthCollectionLimit, null)); map.put(StrutsConstants.STRUTS_UI_STATIC_CONTENT_PATH, Objects.toString(staticContentPath, StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH)); @@ -812,6 +816,14 @@ public class ConstantConfig { this.mapperAlwaysSelectFullNamespace = mapperAlwaysSelectFullNamespace; } + public Boolean getActionConfigFallbackToEmptyNamespace() { + return actionConfigFallbackToEmptyNamespace; + } + + public void setActionConfigFallbackToEmptyNamespace(Boolean actionConfigFallbackToEmptyNamespace) { + this.actionConfigFallbackToEmptyNamespace = actionConfigFallbackToEmptyNamespace; + } + public BeanConfig getLocaleProviderFactory() { return localeProviderFactory; } @@ -1360,6 +1372,14 @@ public class ConstantConfig { this.localizedTextProvider = new BeanConfig(clazz, clazz.getName()); } + public Boolean getDisallowProxyObjectAccess() { + return disallowProxyObjectAccess; + } + + public void setDisallowProxyObjectAccess(Boolean disallowProxyObjectAccess) { + this.disallowProxyObjectAccess = disallowProxyObjectAccess; + } + public Boolean getDisallowProxyMemberAccess() { return disallowProxyMemberAccess; } diff --git a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java index 70bbf70dd..6d21ef693 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/Dispatcher.java @@ -78,6 +78,7 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; +import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; @@ -88,6 +89,10 @@ import java.util.Set; import java.util.concurrent.CopyOnWriteArrayList; import java.util.regex.Pattern; +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; +import static java.util.stream.Collectors.toList; + /** * A utility class the actual dispatcher delegates most of its tasks to. Each instance * of the primary dispatcher holds an instance of this dispatcher to be shared for @@ -162,6 +167,10 @@ public class Dispatcher { */ private Pattern multipartValidationPattern = Pattern.compile(MULTIPART_FORM_DATA_REGEX); + private String actionExcludedPatternsStr; + private String actionExcludedPatternsSeparator = ","; + private List actionExcludedPatterns; + /** * Provide list of default configuration files. */ @@ -340,6 +349,33 @@ public class Dispatcher { this.multipartValidationPattern = Pattern.compile(multipartValidationRegex); } + @Inject(value = StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR, required = false) + public void setActionExcludedPatternsSeparator(String separator) { + this.actionExcludedPatternsSeparator = separator; + } + + @Inject(value = StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN, required = false) + public void setActionExcludedPatterns(String excludedPatterns) { + this.actionExcludedPatternsStr = excludedPatterns; + } + + public List getActionExcludedPatterns() { + if (actionExcludedPatterns == null) { + initActionExcludedPatterns(); + } + return actionExcludedPatterns; + } + + private void initActionExcludedPatterns() { + if (actionExcludedPatternsStr == null || actionExcludedPatternsStr.trim().isEmpty()) { + actionExcludedPatterns = emptyList(); + return; + } + actionExcludedPatterns = unmodifiableList( + Arrays.stream(actionExcludedPatternsStr.split(actionExcludedPatternsSeparator)) + .map(String::trim).map(Pattern::compile).collect(toList())); + } + @Inject public void setValueStackFactory(ValueStackFactory valueStackFactory) { this.valueStackFactory = valueStackFactory; diff --git a/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java b/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java index 367aeba55..f5cf21a91 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/InitOperations.java @@ -19,10 +19,7 @@ package org.apache.struts2.dispatcher; import com.opensymphony.xwork2.ActionContext; -import org.apache.struts2.StrutsConstants; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; @@ -100,27 +97,11 @@ public class InitOperations { * @param dispatcher The dispatcher to check for exclude pattern configuration * @return a List of Patterns for request to exclude if apply, or null * @see org.apache.struts2.StrutsConstants#STRUTS_ACTION_EXCLUDE_PATTERN + * @deprecated since 6.4.0, use {@link Dispatcher#getActionExcludedPatterns()} instead. */ + @Deprecated public List buildExcludedPatternsList(Dispatcher dispatcher) { - String excludePatterns = dispatcher.getContainer().getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN); - String separator = dispatcher.getContainer().getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR); - if (separator == null) { - separator = ","; - } - return buildExcludedPatternsList(excludePatterns, separator); - } - - private List buildExcludedPatternsList(String patterns, String separator) { - if (null != patterns && patterns.trim().length() != 0) { - List list = new ArrayList<>(); - String[] tokens = patterns.split(separator); - for (String token : tokens) { - list.add(Pattern.compile(token.trim())); - } - return Collections.unmodifiableList(list); - } else { - return null; - } + return dispatcher.getActionExcludedPatterns(); } } diff --git a/core/src/main/java/org/apache/struts2/dispatcher/PrepareOperations.java b/core/src/main/java/org/apache/struts2/dispatcher/PrepareOperations.java index 95e0f2198..01c04d6aa 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/PrepareOperations.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/PrepareOperations.java @@ -223,21 +223,23 @@ public class PrepareOperations { * Check whether the request matches a list of exclude patterns. * * @param request The request to check patterns against - * @param excludedPatterns list of patterns for exclusion - * * @return true if the request URI matches one of the given patterns */ + public boolean isUrlExcluded(HttpServletRequest request) { + String uri = RequestUtils.getUri(request); + return dispatcher.getActionExcludedPatterns().stream().anyMatch(pattern -> pattern.matcher(uri).matches()); + } + + /** + * @deprecated since 6.4.0, use {@link #isUrlExcluded(HttpServletRequest)} instead. + */ + @Deprecated public boolean isUrlExcluded(HttpServletRequest request, List excludedPatterns) { if (excludedPatterns == null) { return false; } String uri = RequestUtils.getUri(request); - for (Pattern pattern : excludedPatterns) { - if (pattern.matcher(uri).matches()) { - return true; - } - } - return false; + return excludedPatterns.stream().anyMatch(pattern -> pattern.matcher(uri).matches()); } /** diff --git a/core/src/main/java/org/apache/struts2/dispatcher/filter/StrutsPrepareAndExecuteFilter.java b/core/src/main/java/org/apache/struts2/dispatcher/filter/StrutsPrepareAndExecuteFilter.java index e5f45d5b5..25b459812 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/filter/StrutsPrepareAndExecuteFilter.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/filter/StrutsPrepareAndExecuteFilter.java @@ -50,6 +50,12 @@ public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter { protected PrepareOperations prepare; protected ExecuteOperations execute; + + /** + * @deprecated since 6.4.0, use {@link Dispatcher#getActionExcludedPatterns} or + * {@link PrepareOperations#isUrlExcluded(HttpServletRequest)} instead. + */ + @Deprecated protected List excludedPatterns; public void init(FilterConfig filterConfig) throws ServletException { @@ -62,7 +68,7 @@ public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter { prepare = createPrepareOperations(dispatcher); execute = createExecuteOperations(dispatcher); - // Note: Currently, excluded patterns are not refreshed following an XWork config reload + this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); postInit(dispatcher, filterConfig); @@ -121,7 +127,7 @@ public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter { try { prepare.trackRecursion(request); String uri = RequestUtils.getUri(request); - if (prepare.isUrlExcluded(request, excludedPatterns)) { + if (prepare.isUrlExcluded(request)) { LOG.trace("Request: {} is excluded from handling by Struts, passing request to other filters", uri); chain.doFilter(request, response); } else { diff --git a/core/src/main/java/org/apache/struts2/dispatcher/filter/StrutsPrepareFilter.java b/core/src/main/java/org/apache/struts2/dispatcher/filter/StrutsPrepareFilter.java index 11191aa74..0969617d7 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/filter/StrutsPrepareFilter.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/filter/StrutsPrepareFilter.java @@ -43,6 +43,12 @@ public class StrutsPrepareFilter implements StrutsStatics, Filter { protected static final String REQUEST_EXCLUDED_FROM_ACTION_MAPPING = StrutsPrepareFilter.class.getName() + ".REQUEST_EXCLUDED_FROM_ACTION_MAPPING"; protected PrepareOperations prepare; + + /** + * @deprecated since 6.4.0, use {@link Dispatcher#getActionExcludedPatterns} or + * {@link PrepareOperations#isUrlExcluded(HttpServletRequest)} instead. + */ + @Deprecated protected List excludedPatterns; public void init(FilterConfig filterConfig) throws ServletException { @@ -53,7 +59,7 @@ public class StrutsPrepareFilter implements StrutsStatics, Filter { dispatcher = init.initDispatcher(config); prepare = createPrepareOperations(dispatcher); - // Note: Currently, excluded patterns are not refreshed following an XWork config reload + this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); postInit(dispatcher, filterConfig); @@ -102,7 +108,7 @@ public class StrutsPrepareFilter implements StrutsStatics, Filter { boolean didWrap = false; try { prepare.trackRecursion(request); - if (prepare.isUrlExcluded(request, excludedPatterns)) { + if (prepare.isUrlExcluded(request)) { request.setAttribute(REQUEST_EXCLUDED_FROM_ACTION_MAPPING, true); } else { request.setAttribute(REQUEST_EXCLUDED_FROM_ACTION_MAPPING, false); diff --git a/core/src/main/resources/org/apache/struts2/default.properties b/core/src/main/resources/org/apache/struts2/default.properties index 4ce1f3e5a..14200e690 100644 --- a/core/src/main/resources/org/apache/struts2/default.properties +++ b/core/src/main/resources/org/apache/struts2/default.properties @@ -214,6 +214,9 @@ struts.xslt.nocache=false ### Whether to always select the namespace to be everything before the last slash or not struts.mapper.alwaysSelectFullNamespace=false +### Whether to fallback to empty namespace when request namespace does not match any in configuration +struts.actionConfig.fallbackToEmptyNamespace=true + ### Whether to allow static field access in OGNL expressions or not struts.ognl.allowStaticFieldAccess=true diff --git a/core/src/main/resources/struts-6.5.dtd b/core/src/main/resources/struts-6.5.dtd new file mode 100644 index 000000000..6c2ef0d05 --- /dev/null +++ b/core/src/main/resources/struts-6.5.dtd @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/core/src/main/resources/struts-excluded-classes.xml b/core/src/main/resources/struts-excluded-classes.xml index f3f4f3f19..58b89aae4 100644 --- a/core/src/main/resources/struts-excluded-classes.xml +++ b/core/src/main/resources/struts-excluded-classes.xml @@ -92,6 +92,7 @@ org.apache.catalina.core, org.apache.commons.beanutils, org.apache.commons.collections, + org.apache.jasper, org.apache.struts2.ognl, org.apache.tomcat, org.apache.velocity, @@ -124,6 +125,7 @@ org.apache.catalina.core, org.apache.commons.beanutils, org.apache.commons.collections, + org.apache.jasper, org.apache.struts2.ognl, org.apache.tomcat, org.apache.velocity, diff --git a/core/src/test/java/com/opensymphony/xwork2/config/ConfigurationTest.java b/core/src/test/java/com/opensymphony/xwork2/config/ConfigurationTest.java index b52b9d48c..520f8c240 100644 --- a/core/src/test/java/com/opensymphony/xwork2/config/ConfigurationTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/config/ConfigurationTest.java @@ -31,6 +31,7 @@ import com.opensymphony.xwork2.inject.ContainerBuilder; import com.opensymphony.xwork2.mock.MockInterceptor; import com.opensymphony.xwork2.test.StubConfigurationProvider; import com.opensymphony.xwork2.util.location.LocatableProperties; +import org.apache.struts2.StrutsConstants; import org.apache.struts2.config.StrutsXmlConfigurationProvider; import org.apache.struts2.dispatcher.HttpParameters; @@ -239,6 +240,41 @@ public class ConfigurationTest extends XWorkTestCase { mockContainerProvider.verify(); } + public void testGetActionConfigFallbackToEmptyNamespaceWhenNamespaceDontMatchAndEmptyNamespaceFallbackIsEnabled() { + // struts.actionConfig.fallbackToEmptyNamespace default to true, so it is enabled + RuntimeConfiguration configuration = configurationManager.getConfiguration().getRuntimeConfiguration(); + + // check namespace that doesn't match fallback to empty namespace + ActionConfig actionConfig = configuration.getActionConfig("/something/that/is/not/in/the/namespace/config", "LazyFoo"); + assertEquals("default", actionConfig.getPackageName()); // fallback to empty namespace (package name is default) + assertEquals("LazyFoo", actionConfig.getName()); + + // check non-empty namespace and name in config still matches + assertNotNull(configuration.getActionConfig("includeTest", "Foo")); + + // check root namespace and name in config still matches + actionConfig = configuration.getActionConfig("/", "LazyFoo"); + assertEquals("default", actionConfig.getPackageName()); + assertEquals("LazyFoo", actionConfig.getName()); + } + + public void testGetActionConfigReturnNullWhenNamespaceDontMatchAndEmptyNamespaceFallbackIsDisabled() { + // set the struts.actionConfig.fallbackToEmptyNamespace to false and reload the configuration + setStrutsConstant(StrutsConstants.STRUTS_ACTION_CONFIG_FALLBACK_TO_EMPTY_NAMESPACE, "false"); + RuntimeConfiguration configuration = configurationManager.getConfiguration().getRuntimeConfiguration(); + + // check namespace that doesn't match NOT fallback to empty namespace and return null + assertNull(configuration.getActionConfig("/something/that/is/not/in/the/namespace/config", "LazyFoo")); + + // check non-empty namespace and name in config still matches + assertNotNull(configuration.getActionConfig("includeTest", "Foo")); + + // check root namespace and name in config still matches + ActionConfig actionConfig = configuration.getActionConfig("/", "LazyFoo"); + assertEquals("default", actionConfig.getPackageName()); + assertEquals("LazyFoo", actionConfig.getName()); + } + public void testInitForPackageProviders() { loadConfigurationProviders(new StubConfigurationProvider() { diff --git a/core/src/test/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProviderPackagesTest.java b/core/src/test/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProviderPackagesTest.java index b39baecda..ed8910bfa 100644 --- a/core/src/test/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProviderPackagesTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProviderPackagesTest.java @@ -36,10 +36,9 @@ import java.util.List; public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase { public void testBadInheritance() throws ConfigurationException { - final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-bad-inheritance.xml"; ConfigurationProvider provider = null; try { - provider = buildConfigurationProvider(filename); + provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-bad-inheritance.xml")); fail("Should have thrown a ConfigurationException"); provider.init(configuration); provider.loadPackages(); @@ -49,8 +48,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase } public void testBasicPackages() throws ConfigurationException { - final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-basic-packages.xml"; - ConfigurationProvider provider = buildConfigurationProvider(filename); + ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-basic-packages.xml")); provider.init(configuration); provider.loadPackages(); @@ -70,8 +68,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase } public void testDefaultPackage() throws ConfigurationException { - final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-default-package.xml"; - ConfigurationProvider provider = buildConfigurationProvider(filename); + ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-default-package.xml")); provider.init(configuration); provider.loadPackages(); @@ -84,8 +81,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase } public void testPackageInheritance() throws ConfigurationException { - final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-package-inheritance.xml"; - ConfigurationProvider provider = buildConfigurationProvider(filename); + ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-package-inheritance.xml")); provider.init(configuration); provider.loadPackages(); @@ -111,7 +107,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase assertTrue(multipleParents.contains(defaultPackage)); assertTrue(multipleParents.contains(abstractPackage)); assertTrue(multipleParents.contains(singlePackage)); - + PackageConfig parentBelow = configuration.getPackageConfig("testParentBelow"); assertEquals(1, parentBelow.getParents().size()); List parentBelowParents = parentBelow.getParents(); @@ -129,7 +125,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase assertNull(runtimeConfiguration.getActionConfig("/single", "abstract")); assertNotNull(runtimeConfiguration.getActionConfig("/single", "single")); assertNull(runtimeConfiguration.getActionConfig("/single", "multiple")); - + assertNotNull(runtimeConfiguration.getActionConfig("/parentBelow", "default")); assertNotNull(runtimeConfiguration.getActionConfig("/parentBelow", "abstract")); assertNotNull(runtimeConfiguration.getActionConfig("/parentBelow", "single")); @@ -138,13 +134,57 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase } + public void testPackageWithFinalAttributeLoads() throws ConfigurationException { + ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-package-final.xml")); + + provider.init(configuration); + provider.loadPackages(); + + // test expectations + assertEquals(3, configuration.getPackageConfigs().size()); + PackageConfig defaultPackage = configuration.getPackageConfig("default"); + assertNotNull(defaultPackage); + assertEquals("default", defaultPackage.getName()); + + // final package extends default + PackageConfig finalPackage = configuration.getPackageConfig("finalPackage"); + assertNotNull(finalPackage); + assertEquals("finalPackage", finalPackage.getName()); + assertEquals(1, finalPackage.getParents().size()); + assertEquals(defaultPackage, finalPackage.getParents().get(0)); + + // normal package extends default + PackageConfig normalPackage = configuration.getPackageConfig("normalPackage"); + assertNotNull(normalPackage); + assertEquals("normalPackage", normalPackage.getName()); + assertEquals(1, normalPackage.getParents().size()); + assertEquals(defaultPackage, normalPackage.getParents().get(0)); + + configurationManager.addContainerProvider(provider); + configurationManager.reload(); + + RuntimeConfiguration runtimeConfiguration = configurationManager.getConfiguration().getRuntimeConfiguration(); + assertNotNull(runtimeConfiguration.getActionConfig("/final", "default")); + assertNotNull(runtimeConfiguration.getActionConfig("/final", "actionFinal")); + + assertNotNull(runtimeConfiguration.getActionConfig("/normal", "default")); + assertNotNull(runtimeConfiguration.getActionConfig("/normal", "actionNormal")); + } + + public void testExtendsFinalPackageThrowsConfigurationException() throws ConfigurationException { + try { + buildConfigurationProvider(getXmlConfigFilePath("xwork-test-package-extends-final.xml")); + } catch (ConfigurationException e) { + assertEquals("Parent package is final and unextendable: parentLevelTwo", e.getMessage()); + } + } + public void testDefaultClassRef() throws ConfigurationException { - final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-defaultclassref-package.xml"; final String hasDefaultClassRefPkgName = "hasDefaultClassRef"; final String noDefaultClassRefPkgName = "noDefaultClassRef"; final String testDefaultClassRef = "com.opensymphony.xwork2.ActionSupport"; - ConfigurationProvider provider = buildConfigurationProvider(filename); + ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-defaultclassref-package.xml")); provider.init(configuration); // setup our expectations @@ -157,4 +197,8 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase assertEquals(expectedDefaultClassRefPackage, configuration.getPackageConfig(hasDefaultClassRefPkgName)); assertEquals(expectedNoDefaultClassRefPackage, configuration.getPackageConfig(noDefaultClassRefPkgName)); } + + private String getXmlConfigFilePath(String fileName) { + return "com/opensymphony/xwork2/config/providers/" + fileName; + } } diff --git a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java index 3bdfd67fc..7fb560c5b 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/ognl/OgnlValueStackTest.java @@ -437,12 +437,12 @@ public class OgnlValueStackTest extends XWorkTestCase { } /** - * Allow access Enums without enabling access to static methods + * Enum methods should also be banned alongside static methods */ public void testEnum() throws Exception { - assertEquals("ONE", vs.findValue("@com.opensymphony.xwork2.ognl.MyNumbers@values()[0]", String.class)); - assertEquals("TWO", vs.findValue("@com.opensymphony.xwork2.ognl.MyNumbers@values()[1]", String.class)); - assertEquals("THREE", vs.findValue("@com.opensymphony.xwork2.ognl.MyNumbers@values()[2]", String.class)); + assertNull("ONE", vs.findValue("@com.opensymphony.xwork2.ognl.MyNumbers@values()[0]", String.class)); + assertNull("TWO", vs.findValue("@com.opensymphony.xwork2.ognl.MyNumbers@values()[1]", String.class)); + assertNull("THREE", vs.findValue("@com.opensymphony.xwork2.ognl.MyNumbers@values()[2]", String.class)); } public void testStaticMethodDisallow() { 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 03bad82e4..381b7d0ad 100644 --- a/core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessTest.java @@ -413,7 +413,7 @@ public class SecurityMemberAccessTest { boolean actual = sma.isAccessible(context, MyValues.class, values, null); // then - assertTrue("Access to enums is blocked!", actual); + assertFalse("Access to enums is allowed!", actual); } @Test diff --git a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java index aabe90e70..07a9b37b8 100644 --- a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java +++ b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java @@ -55,10 +55,12 @@ import java.util.HashSet; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.regex.Pattern; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Collections.emptyMap; import static java.util.Collections.singletonMap; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; @@ -589,6 +591,30 @@ public class DispatcherTest extends StrutsJUnit4InternalTestCase { assertEquals(Locale.CANADA_FRENCH, dispatcher.getLocale(request)); } + @Test + public void testExcludePatterns() { + initDispatcher(singletonMap(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN, "/ns1/.*\\.json,/ns2/.*\\.json")); + + assertThat(dispatcher.getActionExcludedPatterns()).extracting(Pattern::toString).containsOnly( + "/ns1/.*\\.json", + "/ns2/.*\\.json" + ); + } + + @Test + public void testExcludePatternsUsingCustomSeparator() { + Map props = new HashMap<>(); + props.put(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN, "/ns1/[a-z]{1,10}.json///ns2/[a-z]{1,10}.json"); + props.put(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR, "//"); + + initDispatcher(props); + + assertThat(dispatcher.getActionExcludedPatterns()).extracting(Pattern::toString).containsOnly( + "/ns1/[a-z]{1,10}.json", + "/ns2/[a-z]{1,10}.json" + ); + } + public static Dispatcher spyDispatcherWithConfigurationManager(Dispatcher dispatcher, ConfigurationManager configurationManager) { Dispatcher spiedDispatcher = spy(dispatcher); doReturn(configurationManager).when(spiedDispatcher).createConfigurationManager(any()); diff --git a/core/src/test/java/org/apache/struts2/dispatcher/InitOperationsTest.java b/core/src/test/java/org/apache/struts2/dispatcher/InitOperationsTest.java deleted file mode 100644 index aa2aaeaa3..000000000 --- a/core/src/test/java/org/apache/struts2/dispatcher/InitOperationsTest.java +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.struts2.dispatcher; - -import com.opensymphony.xwork2.config.ConfigurationException; -import com.opensymphony.xwork2.inject.ContainerBuilder; -import com.opensymphony.xwork2.util.location.LocatableProperties; -import org.apache.struts2.StrutsConstants; -import org.apache.struts2.StrutsInternalTestCase; -import org.apache.struts2.config.PropertiesConfigurationProvider; - -import java.util.List; -import java.util.regex.Pattern; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -public class InitOperationsTest extends StrutsInternalTestCase { - - public void testExcludePatterns() { - // given - loadConfigurationProviders(new PropertiesConfigurationProvider() { - @Override - public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { - props.setProperty(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN, "/ns1/.*\\.json,/ns2/.*\\.json"); - } - }); - - Dispatcher mockDispatcher = mock(Dispatcher.class); - when(mockDispatcher.getContainer()).thenReturn(container); - - // when - InitOperations init = new InitOperations(); - List patterns = init.buildExcludedPatternsList(mockDispatcher); - - // then - assertThat(patterns).extracting(Pattern::toString).containsOnly( - "/ns1/.*\\.json", - "/ns2/.*\\.json" - ); - } - - public void testExcludePatternsUsingCustomSeparator() { - // given - loadConfigurationProviders(new PropertiesConfigurationProvider() { - @Override - public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { - props.setProperty(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN, "/ns1/[a-z]{1,10}.json///ns2/[a-z]{1,10}.json"); - props.setProperty(StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR, "//"); - } - }); - - Dispatcher mockDispatcher = mock(Dispatcher.class); - when(mockDispatcher.getContainer()).thenReturn(container); - - // when - InitOperations init = new InitOperations(); - - String separator = container.getInstance(String.class, StrutsConstants.STRUTS_ACTION_EXCLUDE_PATTERN_SEPARATOR); - List patterns = init.buildExcludedPatternsList(mockDispatcher); - - // then - assertThat(separator).isNotBlank().isEqualTo("//"); - assertThat(patterns).extracting(Pattern::toString).containsOnly( - "/ns1/[a-z]{1,10}.json", - "/ns2/[a-z]{1,10}.json" - ); - } -} diff --git a/core/src/test/java/org/apache/struts2/dispatcher/StrutsPrepareAndExecuteFilterIntegrationTest.java b/core/src/test/java/org/apache/struts2/dispatcher/StrutsPrepareAndExecuteFilterIntegrationTest.java index cc434571c..470c6d47e 100644 --- a/core/src/test/java/org/apache/struts2/dispatcher/StrutsPrepareAndExecuteFilterIntegrationTest.java +++ b/core/src/test/java/org/apache/struts2/dispatcher/StrutsPrepareAndExecuteFilterIntegrationTest.java @@ -26,13 +26,10 @@ import org.springframework.mock.web.MockFilterConfig; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; -import jakarta.servlet.FilterConfig; import jakarta.servlet.ServletException; import jakarta.servlet.ServletRequest; import jakarta.servlet.ServletResponse; import java.io.IOException; -import java.util.ArrayList; -import java.util.regex.Pattern; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -127,6 +124,7 @@ public class StrutsPrepareAndExecuteFilterIntegrationTest { MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletResponse response = new MockHttpServletResponse(); MockFilterConfig filterConfig = new MockFilterConfig(); + filterConfig.addInitParameter("struts.action.excludePattern", ".*hello.*"); MockFilterChain filterChain = new MockFilterChain() { @Override public void doFilter(ServletRequest req, ServletResponse res) { @@ -135,14 +133,7 @@ public class StrutsPrepareAndExecuteFilterIntegrationTest { }; request.setRequestURI("/hello.action"); - StrutsPrepareAndExecuteFilter filter = new StrutsPrepareAndExecuteFilter() { - @Override - public void init( FilterConfig filterConfig ) throws ServletException { - super.init(filterConfig); - excludedPatterns = new ArrayList<>(); - excludedPatterns.add(Pattern.compile(".*hello.*")); - } - }; + StrutsPrepareAndExecuteFilter filter = new StrutsPrepareAndExecuteFilter(); filter.init(filterConfig); filter.doFilter(request, response, filterChain); assertEquals(200, response.getStatus()); diff --git a/core/src/test/java/org/apache/struts2/ognl/OgnlSetPossiblePropertyTest.java b/core/src/test/java/org/apache/struts2/ognl/OgnlSetPossiblePropertyTest.java new file mode 100644 index 000000000..546d2fd29 --- /dev/null +++ b/core/src/test/java/org/apache/struts2/ognl/OgnlSetPossiblePropertyTest.java @@ -0,0 +1,240 @@ +/* + * 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.XWorkTestCase; +import com.opensymphony.xwork2.ognl.OgnlValueStack; +import com.opensymphony.xwork2.util.ValueStackFactory; +import ognl.OgnlRuntime; +import org.apache.struts2.StrutsConstants; + +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.Map; + +import static org.junit.Assert.assertNotEquals; + +public class OgnlSetPossiblePropertyTest extends XWorkTestCase { + private OgnlValueStack vs; + + public T setUpClass(Class holderClass) throws Exception { + Map properties = new HashMap<>(); + properties.put(StrutsConstants.STRUTS_EXCLUDED_CLASSES, holderClass.getName() + "$ExcludedField"); + loadButSet(properties); + vs = (OgnlValueStack) container.getInstance(ValueStackFactory.class).createValueStack(); + + T nonExcludedHolder = holderClass.getDeclaredConstructor().newInstance(); + vs.push(nonExcludedHolder); + + return nonExcludedHolder; + } + + public void testSetFieldValueDontAssignWhenHolderClassAndFieldClassHaveOnlyPublicFields() throws Exception { + /* Case: to test setFieldValue without having set method + * + * NonExcludedHolder class + * - field: public + * ExcludeField class + * - field: public + */ + HolderWithPublicField holder = setUpClass(HolderWithPublicField.class); + vs.setValue("excludedField.excludedFieldString", "EXPLOITED"); + + assertNotEquals("EXPLOITED", holder.excludedField.excludedFieldString); + } + + public void testSetMethodValueDontAssignWhenHolderAndFieldClassWithPublicMethodsAndPrivateFields() throws Exception { + /* Case: to test setMethodValue, so to make fields as private + * + * NonExcludedHolder class + * - field: private + * - method: public + * ExcludeField class + * - field: private + * - method: public + */ + HolderWithPublicMethod holder = setUpClass(HolderWithPublicMethod.class); + vs.setValue("excludedField.excludedFieldString", "EXPLOITED"); + + assertNotEquals("EXPLOITED", holder.excludedField.excludedFieldString); + } + + public void testSetFieldValueDontAssignWhenHolderClassWithGetMethodAndFieldClassWithPublicField() throws Exception { + /* Case: to test setFieldValue when holder get method is public and field class set method is private so fallback to set field + * + * NonExcludedHolder class + * - field: private + * - method: public + * ExcludeField class + * - field: public + * - method: private + */ + HolderWhoseFieldWithPrivateMethod holder = setUpClass(HolderWhoseFieldWithPrivateMethod.class); + vs.setValue("excludedField.excludedFieldString", "EXPLOITED"); + + assertNotEquals("EXPLOITED", holder.excludedField.excludedFieldString); + } + + public void testSetMethodValueDontAssignWhenHolderClassWithGetMethodAndFieldClassWithPublicMethod() throws Exception { + /* Case: to test setMethodValue when holder get method is public and field class field is private so only call to set method + * + * NonExcludedHolder class + * - field: private + * - method: public + * ExcludeField class + * - field: private + * - method: public + */ + HolderWhoseFieldWithPublicMethod holder = setUpClass(HolderWhoseFieldWithPublicMethod.class); + vs.setValue("excludedField.excludedFieldString", "EXPLOITED"); + + assertNotEquals("EXPLOITED", holder.excludedField.excludedFieldString); + } + + public void testWriteMethodValueDontAssignWhenWriteMethodIsNotAccessible() throws Exception { + /* Case: to test invoke method from getWriteMethod when holder get method is public and field class field / set method is private so fallback to write method + * + * NonExcludedHolder class + * - field: private + * - method: public + * ExcludeField class + * - field: private + * - set method: private + * - write method: public + */ + HolderWhoseFieldWithPublicWriteMethod holder = setUpClass(HolderWhoseFieldWithPublicWriteMethod.class); + Method writeMethod = OgnlRuntime.getWriteMethod(HolderWhoseFieldWithPublicWriteMethod.ExcludedField.class, "excludedFieldString"); + vs.setValue("excludedField.excludedFieldString", "EXPLOITED"); + + assertEquals("setexcludedfieldstring", writeMethod.getName()); + assertNotEquals("EXPLOITED", holder.excludedField.excludedFieldString); + } + + public void testWriteMethodValueDontAssignWhenPublicSetterDifferentFieldName() throws Exception { + /* Case: to test invoke method from getWriteMethod when holder get method is public and field class field / set method is of different name + * + * NonExcludedHolder class + * - field: private + * - method: public + * ExcludeField class + * - field: private + * - set method: public (but not matching with field name) + */ + HolderWhoseFieldWithPublicSetterDifferentFieldName holder = setUpClass(HolderWhoseFieldWithPublicSetterDifferentFieldName.class); + vs.setValue("excludedField.excludedFieldString", "EXPLOITED"); + + assertNotEquals("EXPLOITED", holder.excludedField.excludedFieldStringInternal); + } + + + public static class HolderWithPublicField { + public ExcludedField excludedField = new ExcludedField(); + + public static class ExcludedField { + public String excludedFieldString = "defaultValue"; + } + } + + public static class HolderWithPublicMethod { + private ExcludedField excludedField = new ExcludedField(); + + public ExcludedField getExcludedField() { + return excludedField; + } + + public static class ExcludedField { + private String excludedFieldString = "defaultValue"; + + public void setExcludedFieldString(String value) { + this.excludedFieldString = value; + } + } + } + + public static class HolderWhoseFieldWithPrivateMethod { + private ExcludedField excludedField = new ExcludedField(); + + + public ExcludedField getExcludedField() { + return excludedField; + } + + public static class ExcludedField { + public String excludedFieldString = "defaultValue"; + + private void setExcludedFieldString(String value) { + this.excludedFieldString = value; + } + } + } + + public static class HolderWhoseFieldWithPublicMethod { + private ExcludedField excludedField = new ExcludedField(); + + + public ExcludedField getExcludedField() { + return excludedField; + } + + public static class ExcludedField { + public String excludedFieldString = "defaultValue"; + + private void setExcludedFieldString(String value) { + this.excludedFieldString = value; + } + } + } + + public static class HolderWhoseFieldWithPublicWriteMethod { + private ExcludedField excludedField = new ExcludedField(); + + + public ExcludedField getExcludedField() { + return excludedField; + } + + public static class ExcludedField { + private String excludedFieldString = "defaultValue"; + + private void setExcludedFieldString(String value) { + this.excludedFieldString = value; + } + + public void setexcludedfieldstring(String value) { + this.excludedFieldString = value; + } + } + } + + public static class HolderWhoseFieldWithPublicSetterDifferentFieldName { + private ExcludedField excludedField = new ExcludedField(); + + public ExcludedField getExcludedField() { + return excludedField; + } + + public static class ExcludedField { + private String excludedFieldStringInternal = "defaultValue"; + + public void setExcludedFieldString(String value) { + this.excludedFieldStringInternal = value; + } + } + } +} diff --git a/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java b/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java index 7f4b54581..b7db51513 100644 --- a/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java +++ b/core/src/test/java/org/apache/struts2/views/jsp/ui/DebugTagTest.java @@ -217,23 +217,9 @@ public class DebugTagTest extends AbstractUITagTest { /** * Overwrite the Struts Constant and reload container */ - private void setStrutsConstant(final Map overwritePropeties) { - configurationManager.addContainerProvider(new StubConfigurationProvider() { - @Override - public boolean needsReload() { - return true; - } - - @Override - public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException { - for (Map.Entry stringStringEntry : overwritePropeties.entrySet()) { - props.setProperty(stringStringEntry.getKey(), stringStringEntry.getValue(), null); - } - } - }); - - configurationManager.reload(); - container = configurationManager.getConfiguration().getContainer(); + @Override + protected void setStrutsConstant(final Map overwritePropeties) { + super.setStrutsConstant(overwritePropeties); stack.getActionContext().withContainer(container); } -} \ No newline at end of file +} diff --git a/core/src/test/resources/com/opensymphony/xwork2/config/providers/xwork-test-package-extends-final.xml b/core/src/test/resources/com/opensymphony/xwork2/config/providers/xwork-test-package-extends-final.xml new file mode 100644 index 000000000..4e4242235 --- /dev/null +++ b/core/src/test/resources/com/opensymphony/xwork2/config/providers/xwork-test-package-extends-final.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + diff --git a/core/src/test/resources/com/opensymphony/xwork2/config/providers/xwork-test-package-final.xml b/core/src/test/resources/com/opensymphony/xwork2/config/providers/xwork-test-package-final.xml new file mode 100644 index 000000000..2a49a153b --- /dev/null +++ b/core/src/test/resources/com/opensymphony/xwork2/config/providers/xwork-test-package-final.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + diff --git a/plugins/bean-validation/pom.xml b/plugins/bean-validation/pom.xml index 5d2f8f40a..bb13eb00a 100644 --- a/plugins/bean-validation/pom.xml +++ b/plugins/bean-validation/pom.xml @@ -43,7 +43,7 @@ jakarta.validation-api 3.0.2 - + org.hibernate.validator diff --git a/plugins/rest/pom.xml b/plugins/rest/pom.xml index bc02c8f2a..8318b37ef 100644 --- a/plugins/rest/pom.xml +++ b/plugins/rest/pom.xml @@ -66,13 +66,13 @@ commons-beanutils true - + mockobjects mockobjects-core test - + org.springframework spring-test diff --git a/plugins/spring/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessProxyTest.java b/plugins/spring/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessProxyTest.java index 4d8046de9..3838ca9ae 100644 --- a/plugins/spring/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessProxyTest.java +++ b/plugins/spring/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessProxyTest.java @@ -29,6 +29,11 @@ import java.util.Map; public class SecurityMemberAccessProxyTest extends XWorkTestCase { private Map context; + private ActionProxy proxy; + private Map members; + private final SecurityMemberAccess sma = new SecurityMemberAccess(true); + private final String PROXY_MEMBER_METHOD = "isExposeProxy"; + private final String TEST_SUB_BEAN_CLASS_METHOD = "setIssueId"; @Override public void setUp() throws Exception { @@ -39,30 +44,51 @@ public class SecurityMemberAccessProxyTest extends XWorkTestCase { XmlConfigurationProvider provider = new StrutsXmlConfigurationProvider("com/opensymphony/xwork2/spring/actionContext-xwork.xml"); container.inject(provider); loadConfigurationProviders(provider); + + // Setup proxy object + setupProxy(); } public void testProxyAccessIsBlocked() throws Exception { - ActionProxy proxy = actionProxyFactory.createActionProxy(null, - "chaintoAOPedTestSubBeanAction", null, context); + members.values().forEach(member -> { + // When disallowProxyObjectAccess is set to true, and disallowProxyMemberAccess is set to false, the proxy access is blocked + sma.useDisallowProxyObjectAccess(Boolean.TRUE.toString()); + sma.useDisallowProxyMemberAccess(Boolean.FALSE.toString()); + assertFalse(sma.isAccessible(context, proxy.getAction(), member, "")); - SecurityMemberAccess sma = new SecurityMemberAccess(true); + // When disallowProxyObjectAccess is set to true, and disallowProxyMemberAccess is set to true, the proxy access is blocked + sma.useDisallowProxyObjectAccess(Boolean.TRUE.toString()); + sma.useDisallowProxyMemberAccess(Boolean.TRUE.toString()); + assertFalse(sma.isAccessible(context, proxy.getAction(), member, "")); + }); + + // When disallowProxyObjectAccess is set to false, and disallowProxyMemberAccess is set to true, the proxy member access is blocked + sma.useDisallowProxyObjectAccess(Boolean.FALSE.toString()); sma.useDisallowProxyMemberAccess(Boolean.TRUE.toString()); - - Member member = proxy.getAction().getClass().getMethod("isExposeProxy"); - - boolean accessible = sma.isAccessible(context, proxy.getAction(), member, ""); - assertFalse(accessible); + assertFalse(sma.isAccessible(context, proxy.getAction(), members.get(PROXY_MEMBER_METHOD), "")); } public void testProxyAccessIsAccessible() throws Exception { - ActionProxy proxy = actionProxyFactory.createActionProxy(null, - "chaintoAOPedTestSubBeanAction", null, context); + members.values().forEach(member -> { + // When disallowProxyObjectAccess is set to false, and disallowProxyMemberAccess is set to false, the proxy access is allowed + sma.useDisallowProxyObjectAccess(Boolean.FALSE.toString()); + sma.useDisallowProxyMemberAccess(Boolean.FALSE.toString()); + assertTrue(sma.isAccessible(context, proxy.getAction(), member, "")); + }); - SecurityMemberAccess sma = new SecurityMemberAccess(true); + // When disallowProxyObjectAccess is set to false, and disallowProxyMemberAccess is set to true, the original class member access is allowed + sma.useDisallowProxyObjectAccess(Boolean.FALSE.toString()); + sma.useDisallowProxyMemberAccess(Boolean.TRUE.toString()); + assertTrue(sma.isAccessible(context, proxy.getAction(), members.get(TEST_SUB_BEAN_CLASS_METHOD), "")); + } - Member member = proxy.getAction().getClass().getMethod("isExposeProxy"); + private void setupProxy() throws NoSuchMethodException { + proxy = actionProxyFactory.createActionProxy(null, "chaintoAOPedTestSubBeanAction", null, context); - boolean accessible = sma.isAccessible(context, proxy.getAction(), member, ""); - assertTrue(accessible); + members = new HashMap<>(); + // method is proxy member + members.put(PROXY_MEMBER_METHOD, proxy.getAction().getClass().getMethod(PROXY_MEMBER_METHOD)); + // method is not proxy member but from POJO class + members.put(TEST_SUB_BEAN_CLASS_METHOD, proxy.getAction().getClass().getMethod(TEST_SUB_BEAN_CLASS_METHOD, String.class)); } } diff --git a/pom.xml b/pom.xml index 75a5ae786..d4fb87309 100644 --- a/pom.xml +++ b/pom.xml @@ -118,7 +118,7 @@ 2.23.1 3.2.5 5.8.0 - 3.3.4 + 3.3.5 2.5.0 2.0.12 6.0.13