Merge pull request #912 from atlassian-forks/issue/WW-5408-add-option-to-not-fallback-to-empty-namespace-when-unresolved
WW-5408 add option to not fallback to empty namespace when unresolved
diff --git a/core/pom.xml b/core/pom.xml
index 3fcfaaf..8165d19 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -230,6 +230,15 @@
<groupId>org.apache.commons</groupId>
<artifactId>commons-text</artifactId>
</dependency>
+
+ <!-- Optional used in com.opensymphony.xwork2.util.ProxyUtil to detect if object is HibernateProxy -->
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-core</artifactId>
+ <version>5.6.15.Final</version>
+ <optional>true</optional>
+ </dependency>
+
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
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 29dc7fc..e503f49 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 @@
private CacheType defaultCacheType;
private int cacheMaxSize;
+ private final int initialCapacity;
/**
* @deprecated since 6.4.0, use {@link #DefaultOgnlCacheFactory(int, CacheType)}
@@ -42,13 +43,18 @@
}
public DefaultOgnlCacheFactory(int cacheMaxSize, CacheType defaultCacheType) {
+ this(cacheMaxSize, defaultCacheType, DEFAULT_INIT_CAPACITY);
+ }
+
+ public DefaultOgnlCacheFactory(int cacheMaxSize, CacheType defaultCacheType, int initialCapacity) {
this.cacheMaxSize = cacheMaxSize;
this.defaultCacheType = defaultCacheType;
+ this.initialCapacity = initialCapacity;
}
@Override
public OgnlCache<Key, Value> 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 510a65c..b0ee1f2 100644
--- a/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java
+++ b/core/src/main/java/com/opensymphony/xwork2/ognl/SecurityMemberAccess.java
@@ -87,6 +87,7 @@
private boolean enforceAllowlistEnabled = false;
private Set<Class<?>> allowlistClasses = emptySet();
private Set<String> allowlistPackageNames = emptySet();
+ private boolean disallowProxyObjectAccess = false;
private boolean disallowProxyMemberAccess = false;
private boolean disallowDefaultPackageAccess = false;
@@ -160,6 +161,11 @@
}
}
+ 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 +292,14 @@
}
/**
- * @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));
@@ -448,6 +461,11 @@
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 9b0e7d4..c169af2 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;
/**
* <code>ProxyUtil</code>
@@ -38,11 +45,13 @@
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<Class<?>, Boolean> isProxyCache =
- new ConcurrentHashMap<>(256);
- private static final Map<Member, Boolean> 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<Class<?>, Boolean> isProxyCache = new DefaultOgnlCacheFactory<Class<?>, Boolean>(
+ CACHE_MAX_SIZE, OgnlCacheFactory.CacheType.WTLFU, CACHE_INITIAL_CAPACITY).buildOgnlCache();
+ private static final OgnlCache<Member, Boolean> isProxyMemberCache = new DefaultOgnlCacheFactory<Member, Boolean>(
+ CACHE_MAX_SIZE, OgnlCacheFactory.CacheType.WTLFU, CACHE_INITIAL_CAPACITY).buildOgnlCache();
/**
* Determine the ultimate target class of the given instance, traversing
@@ -75,7 +84,7 @@
return flag;
}
- boolean isProxy = isSpringAopProxy(object);
+ boolean isProxy = isSpringAopProxy(object) || isHibernateProxy(object);
isProxyCache.put(clazz, isProxy);
return isProxy;
@@ -87,7 +96,7 @@
* @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,13 +105,41 @@
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 —
* as long as possible without side effects, that is, just for singleton targets.
diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java
index 7a7f4b2..91b8eb2 100644
--- a/core/src/main/java/org/apache/struts2/StrutsConstants.java
+++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java
@@ -482,6 +482,7 @@
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/entities/ConstantConfig.java b/core/src/main/java/org/apache/struts2/config/entities/ConstantConfig.java
index 3f7484f..11b30c2 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
@@ -146,6 +146,7 @@
private String strictMethodInvocationMethodRegex;
private BeanConfig textProviderFactory;
private BeanConfig localizedTextProvider;
+ private Boolean disallowProxyObjectAccess;
private Boolean disallowProxyMemberAccess;
private Integer ognlAutoGrowthCollectionLimit;
private String staticContentPath;
@@ -281,6 +282,7 @@
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));
@@ -1370,6 +1372,14 @@
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 5bad0b4..af5bb54 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 javax.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.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,9 @@
*/
private Pattern multipartValidationPattern = Pattern.compile(MULTIPART_FORM_DATA_REGEX);
+ private String actionExcludedPatternsSeparator = ",";
+ private List<Pattern> actionExcludedPatterns = emptyList();
+
/**
* Provide list of default configuration files.
*/
@@ -340,6 +348,27 @@
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.actionExcludedPatterns = buildExcludedPatternsList(excludedPatterns, actionExcludedPatternsSeparator);
+ }
+
+ private static List<Pattern> buildExcludedPatternsList(String patterns, String separator) {
+ if (patterns == null || patterns.trim().isEmpty()) {
+ return emptyList();
+ }
+ return unmodifiableList(Arrays.stream(patterns.split(separator)).map(String::trim).map(Pattern::compile).collect(toList()));
+ }
+
+ public List<Pattern> getActionExcludedPatterns() {
+ return actionExcludedPatterns;
+ }
+
@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 367aeba..f5cf21a 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 @@
* @param dispatcher The dispatcher to check for exclude pattern configuration
* @return a List of Patterns for request to exclude if apply, or <tt>null</tt>
* @see org.apache.struts2.StrutsConstants#STRUTS_ACTION_EXCLUDE_PATTERN
+ * @deprecated since 6.4.0, use {@link Dispatcher#getActionExcludedPatterns()} instead.
*/
+ @Deprecated
public List<Pattern> 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<Pattern> buildExcludedPatternsList(String patterns, String separator) {
- if (null != patterns && patterns.trim().length() != 0) {
- List<Pattern> 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 6888c5b..ab43235 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 @@
* 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 <tt>true</tt> 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<Pattern> 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 e91a1b9..bce6ec4 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 @@
protected PrepareOperations prepare;
protected ExecuteOperations execute;
+
+ /**
+ * @deprecated since 6.4.0, use {@link Dispatcher#getActionExcludedPatterns} or
+ * {@link PrepareOperations#isUrlExcluded(HttpServletRequest)} instead.
+ */
+ @Deprecated
protected List<Pattern> excludedPatterns;
public void init(FilterConfig filterConfig) throws ServletException {
@@ -62,7 +68,7 @@
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 @@
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 81dffb6..475a139 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 @@
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<Pattern> excludedPatterns;
public void init(FilterConfig filterConfig) throws ServletException {
@@ -53,7 +59,7 @@
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 @@
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/test/java/org/apache/struts2/dispatcher/DispatcherTest.java b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java
index 8884667..54b2a96 100644
--- a/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java
+++ b/core/src/test/java/org/apache/struts2/dispatcher/DispatcherTest.java
@@ -54,10 +54,12 @@
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;
@@ -588,6 +590,30 @@
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<String, String> 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 aa2aaea..0000000
--- 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<Pattern> 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<Pattern> 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 eb87235..c7d5257 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.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
-import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.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 @@
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 @@
};
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/plugins/spring/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessProxyTest.java b/plugins/spring/src/test/java/com/opensymphony/xwork2/ognl/SecurityMemberAccessProxyTest.java
index 4d8046d..3838ca9 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 @@
public class SecurityMemberAccessProxyTest extends XWorkTestCase {
private Map<String, Object> context;
+ private ActionProxy proxy;
+ private Map<String, Member> 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 @@
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));
}
}