diff --git a/config/src/main/java/org/springframework/security/config/http/MatcherType.java b/config/src/main/java/org/springframework/security/config/http/MatcherType.java index 8a1dcb16cf..68b1bd960f 100644 --- a/config/src/main/java/org/springframework/security/config/http/MatcherType.java +++ b/config/src/main/java/org/springframework/security/config/http/MatcherType.java @@ -22,9 +22,7 @@ import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; -import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.AnyRequestMatcher; import org.springframework.security.web.util.matcher.RegexRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -38,8 +36,7 @@ import org.springframework.util.StringUtils; */ public enum MatcherType { - ant(AntPathRequestMatcher.class), path(PathPatternRequestMatcher.class), regex(RegexRequestMatcher.class), - ciRegex(RegexRequestMatcher.class), mvc(MvcRequestMatcher.class); + path(PathPatternRequestMatcher.class), regex(RegexRequestMatcher.class), ciRegex(RegexRequestMatcher.class); private static final String ATT_MATCHER_TYPE = "request-matcher"; diff --git a/etc/checkstyle/checkstyle.xml b/etc/checkstyle/checkstyle.xml index 8963755388..297f8a8442 100644 --- a/etc/checkstyle/checkstyle.xml +++ b/etc/checkstyle/checkstyle.xml @@ -19,7 +19,6 @@ - diff --git a/web/src/main/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcher.java b/web/src/main/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcher.java deleted file mode 100644 index 51e36d28a8..0000000000 --- a/web/src/main/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcher.java +++ /dev/null @@ -1,251 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed 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 - * - * https://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.springframework.security.web.servlet.util.matcher; - -import java.util.Map; -import java.util.Objects; - -import jakarta.servlet.http.HttpServletRequest; - -import org.springframework.http.HttpMethod; -import org.springframework.security.web.util.matcher.RequestMatcher; -import org.springframework.security.web.util.matcher.RequestVariablesExtractor; -import org.springframework.util.AntPathMatcher; -import org.springframework.util.PathMatcher; -import org.springframework.web.servlet.handler.HandlerMappingIntrospector; -import org.springframework.web.servlet.handler.MatchableHandlerMapping; -import org.springframework.web.servlet.handler.RequestMatchResult; -import org.springframework.web.util.UrlPathHelper; - -/** - * A {@link RequestMatcher} that uses Spring MVC's {@link HandlerMappingIntrospector} to - * match the path and extract variables. - * - *

- * It is important to understand that Spring MVC's matching is relative to the servlet - * path. This means if you have mapped any servlet to a path that starts with "/" and is - * greater than one, you should also specify the {@link #setServletPath(String)} attribute - * to differentiate mappings. - *

- * - * @author Rob Winch - * @author Eddú Meléndez - * @author Evgeniy Cheban - * @since 4.1.1 - * @deprecated Please use {@link PathPatternRequestMatcher} instead - */ -@Deprecated(forRemoval = true) -public class MvcRequestMatcher implements RequestMatcher, RequestVariablesExtractor { - - private final DefaultMatcher defaultMatcher = new DefaultMatcher(); - - private final HandlerMappingIntrospector introspector; - - private final String pattern; - - private HttpMethod method; - - private String servletPath; - - public MvcRequestMatcher(HandlerMappingIntrospector introspector, String pattern) { - this.introspector = introspector; - this.pattern = pattern; - } - - @Override - public boolean matches(HttpServletRequest request) { - if (notMatchMethodOrServletPath(request)) { - return false; - } - MatchableHandlerMapping mapping = getMapping(request); - if (mapping == null) { - return this.defaultMatcher.matches(request); - } - RequestMatchResult matchResult = mapping.match(request, this.pattern); - return matchResult != null; - } - - @Override - @Deprecated - public Map extractUriTemplateVariables(HttpServletRequest request) { - return matcher(request).getVariables(); - } - - @Override - public MatchResult matcher(HttpServletRequest request) { - if (notMatchMethodOrServletPath(request)) { - return MatchResult.notMatch(); - } - MatchableHandlerMapping mapping = getMapping(request); - if (mapping == null) { - return this.defaultMatcher.matcher(request); - } - RequestMatchResult result = mapping.match(request, this.pattern); - return (result != null) ? MatchResult.match(result.extractUriTemplateVariables()) : MatchResult.notMatch(); - } - - private boolean notMatchMethodOrServletPath(HttpServletRequest request) { - return this.method != null && !this.method.name().equals(request.getMethod()) - || this.servletPath != null && !this.servletPath.equals(request.getServletPath()); - } - - private MatchableHandlerMapping getMapping(HttpServletRequest request) { - try { - return this.introspector.getMatchableHandlerMapping(request); - } - catch (Throwable ex) { - return null; - } - } - - /** - * @param method the method to set - */ - public void setMethod(HttpMethod method) { - this.method = method; - } - - /** - * The servlet path to match on. The default is undefined which means any servlet - * path. - * @param servletPath the servletPath to set - */ - public void setServletPath(String servletPath) { - this.servletPath = servletPath; - } - - protected final String getServletPath() { - return this.servletPath; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - MvcRequestMatcher that = (MvcRequestMatcher) o; - return Objects.equals(this.pattern, that.pattern) && Objects.equals(this.method, that.method) - && Objects.equals(this.servletPath, that.servletPath); - } - - @Override - public int hashCode() { - return Objects.hash(this.pattern, this.method, this.servletPath); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("Mvc [pattern='").append(this.pattern).append("'"); - if (this.servletPath != null) { - sb.append(", servletPath='").append(this.servletPath).append("'"); - } - if (this.method != null) { - sb.append(", ").append(this.method); - } - sb.append("]"); - return sb.toString(); - } - - private class DefaultMatcher implements RequestMatcher { - - private final UrlPathHelper pathHelper = new UrlPathHelper(); - - private final PathMatcher pathMatcher = new AntPathMatcher(); - - @Override - public boolean matches(HttpServletRequest request) { - String lookupPath = this.pathHelper.getLookupPathForRequest(request); - return matches(lookupPath); - } - - private boolean matches(String lookupPath) { - return this.pathMatcher.match(MvcRequestMatcher.this.pattern, lookupPath); - } - - @Override - public MatchResult matcher(HttpServletRequest request) { - String lookupPath = this.pathHelper.getLookupPathForRequest(request); - if (matches(lookupPath)) { - Map variables = this.pathMatcher - .extractUriTemplateVariables(MvcRequestMatcher.this.pattern, lookupPath); - return MatchResult.match(variables); - } - return MatchResult.notMatch(); - } - - } - - /** - * A builder for {@link MvcRequestMatcher} - * - * @author Marcus Da Coregio - * @since 5.8 - */ - public static final class Builder { - - private final HandlerMappingIntrospector introspector; - - private String servletPath; - - /** - * Construct a new instance of this builder - */ - public Builder(HandlerMappingIntrospector introspector) { - this.introspector = introspector; - } - - /** - * Sets the servlet path to be used by the {@link MvcRequestMatcher} generated by - * this builder - * @param servletPath the servlet path to use - * @return the {@link Builder} for further configuration - */ - public Builder servletPath(String servletPath) { - this.servletPath = servletPath; - return this; - } - - /** - * Creates an {@link MvcRequestMatcher} that uses the provided pattern to match - * @param pattern the pattern used to match - * @return the generated {@link MvcRequestMatcher} - */ - public MvcRequestMatcher pattern(String pattern) { - return pattern(null, pattern); - } - - /** - * Creates an {@link MvcRequestMatcher} that uses the provided pattern and HTTP - * method to match - * @param method the {@link HttpMethod}, can be null - * @param pattern the patterns used to match - * @return the generated {@link MvcRequestMatcher} - */ - public MvcRequestMatcher pattern(HttpMethod method, String pattern) { - MvcRequestMatcher mvcRequestMatcher = new MvcRequestMatcher(this.introspector, pattern); - mvcRequestMatcher.setServletPath(this.servletPath); - mvcRequestMatcher.setMethod(method); - return mvcRequestMatcher; - } - - } - -} diff --git a/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java b/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java deleted file mode 100644 index 0df94264f6..0000000000 --- a/web/src/main/java/org/springframework/security/web/util/matcher/AntPathRequestMatcher.java +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright 2002-2025 the original author or authors. - * - * Licensed 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 - * - * https://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.springframework.security.web.util.matcher; - -import java.util.Collections; -import java.util.Locale; -import java.util.Map; - -import jakarta.servlet.http.HttpServletRequest; - -import org.springframework.http.HttpMethod; -import org.springframework.security.web.servlet.util.matcher.PathPatternRequestMatcher; -import org.springframework.util.AntPathMatcher; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; -import org.springframework.web.util.UrlPathHelper; - -/** - * Matcher which compares a pre-defined ant-style pattern against the URL ( - * {@code servletPath + pathInfo}) of an {@code HttpServletRequest}. The query string of - * the URL is ignored and matching is case-insensitive or case-sensitive depending on the - * arguments passed into the constructor. - *

- * Using a pattern value of {@code /**} or {@code **} is treated as a universal match, - * which will match any request. Patterns which end with {@code /**} (and have no other - * wildcards) are optimized by using a substring match — a pattern of - * {@code /aaa/**} will match {@code /aaa}, {@code /aaa/} and any sub-directories, such as - * {@code /aaa/bbb/ccc}. - *

- *

- * For all other cases, Spring's {@link AntPathMatcher} is used to perform the match. See - * the Spring documentation for this class for comprehensive information on the syntax - * used. - *

- * - * @author Luke Taylor - * @author Rob Winch - * @author Eddú Meléndez - * @author Evgeniy Cheban - * @author Manuel Jordan - * @since 3.1 - * @see org.springframework.util.AntPathMatcher - * @deprecated please use {@link PathPatternRequestMatcher} instead - */ -@Deprecated(forRemoval = true) -public final class AntPathRequestMatcher implements RequestMatcher, RequestVariablesExtractor { - - private static final String MATCH_ALL = "/**"; - - private final Matcher matcher; - - private final String pattern; - - private final HttpMethod httpMethod; - - private final boolean caseSensitive; - - private final UrlPathHelper urlPathHelper; - - /** - * Creates a matcher with the specific pattern which will match all HTTP methods in a - * case-sensitive manner. - * @param pattern the ant pattern to use for matching - * @since 5.8 - */ - public static AntPathRequestMatcher antMatcher(String pattern) { - Assert.hasText(pattern, "pattern cannot be empty"); - return new AntPathRequestMatcher(pattern); - } - - /** - * Creates a matcher that will match all request with the supplied HTTP method in a - * case-sensitive manner. - * @param method the HTTP method. The {@code matches} method will return false if the - * incoming request doesn't have the same method. - * @since 5.8 - */ - public static AntPathRequestMatcher antMatcher(HttpMethod method) { - Assert.notNull(method, "method cannot be null"); - return new AntPathRequestMatcher(MATCH_ALL, method.name()); - } - - /** - * Creates a matcher with the supplied pattern and HTTP method in a case-sensitive - * manner. - * @param method the HTTP method. The {@code matches} method will return false if the - * incoming request doesn't have the same method. - * @param pattern the ant pattern to use for matching - * @since 5.8 - */ - public static AntPathRequestMatcher antMatcher(HttpMethod method, String pattern) { - Assert.notNull(method, "method cannot be null"); - Assert.hasText(pattern, "pattern cannot be empty"); - return new AntPathRequestMatcher(pattern, method.name()); - } - - /** - * Creates a matcher with the specific pattern which will match all HTTP methods in a - * case sensitive manner. - * @param pattern the ant pattern to use for matching - */ - public AntPathRequestMatcher(String pattern) { - this(pattern, null); - } - - /** - * Creates a matcher with the supplied pattern and HTTP method in a case sensitive - * manner. - * @param pattern the ant pattern to use for matching - * @param httpMethod the HTTP method. The {@code matches} method will return false if - * the incoming request doesn't have the same method. - */ - public AntPathRequestMatcher(String pattern, String httpMethod) { - this(pattern, httpMethod, true); - } - - /** - * Creates a matcher with the supplied pattern which will match the specified Http - * method - * @param pattern the ant pattern to use for matching - * @param httpMethod the HTTP method. The {@code matches} method will return false if - * the incoming request doesn't doesn't have the same method. - * @param caseSensitive true if the matcher should consider case, else false - */ - public AntPathRequestMatcher(String pattern, String httpMethod, boolean caseSensitive) { - this(pattern, httpMethod, caseSensitive, null); - } - - /** - * Creates a matcher with the supplied pattern which will match the specified Http - * method - * @param pattern the ant pattern to use for matching - * @param httpMethod the HTTP method. The {@code matches} method will return false if - * the incoming request doesn't have the same method. - * @param caseSensitive true if the matcher should consider case, else false - * @param urlPathHelper if non-null, will be used for extracting the path from the - * HttpServletRequest - */ - public AntPathRequestMatcher(String pattern, String httpMethod, boolean caseSensitive, - UrlPathHelper urlPathHelper) { - Assert.hasText(pattern, "Pattern cannot be null or empty"); - this.caseSensitive = caseSensitive; - if (pattern.equals(MATCH_ALL) || pattern.equals("**")) { - pattern = MATCH_ALL; - this.matcher = null; - } - else { - // If the pattern ends with {@code /**} and has no other wildcards or path - // variables, then optimize to a sub-path match - if (pattern.endsWith(MATCH_ALL) - && (pattern.indexOf('?') == -1 && pattern.indexOf('{') == -1 && pattern.indexOf('}') == -1) - && pattern.indexOf("*") == pattern.length() - 2) { - this.matcher = new SubpathMatcher(pattern.substring(0, pattern.length() - 3), caseSensitive); - } - else { - this.matcher = new SpringAntMatcher(pattern, caseSensitive); - } - } - this.pattern = pattern; - this.httpMethod = StringUtils.hasText(httpMethod) ? HttpMethod.valueOf(httpMethod) : null; - this.urlPathHelper = urlPathHelper; - } - - /** - * Returns true if the configured pattern (and HTTP-Method) match those of the - * supplied request. - * @param request the request to match against. The ant pattern will be matched - * against the {@code servletPath} + {@code pathInfo} of the request. - */ - @Override - public boolean matches(HttpServletRequest request) { - if (this.httpMethod != null && StringUtils.hasText(request.getMethod()) - && this.httpMethod != HttpMethod.valueOf(request.getMethod())) { - return false; - } - if (this.pattern.equals(MATCH_ALL)) { - return true; - } - String url = getRequestPath(request); - return this.matcher.matches(url); - } - - @Override - @Deprecated - public Map extractUriTemplateVariables(HttpServletRequest request) { - return matcher(request).getVariables(); - } - - @Override - public MatchResult matcher(HttpServletRequest request) { - if (!matches(request)) { - return MatchResult.notMatch(); - } - if (this.matcher == null) { - return MatchResult.match(); - } - String url = getRequestPath(request); - return MatchResult.match(this.matcher.extractUriTemplateVariables(url)); - } - - private String getRequestPath(HttpServletRequest request) { - if (this.urlPathHelper != null) { - return this.urlPathHelper.getPathWithinApplication(request); - } - String url = request.getServletPath(); - String pathInfo = request.getPathInfo(); - if (pathInfo != null) { - url = StringUtils.hasLength(url) ? url + pathInfo : pathInfo; - } - return url; - } - - public String getPattern() { - return this.pattern; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof AntPathRequestMatcher other)) { - return false; - } - return this.pattern.equals(other.pattern) && this.httpMethod == other.httpMethod - && this.caseSensitive == other.caseSensitive; - } - - @Override - public int hashCode() { - int result = (this.pattern != null) ? this.pattern.hashCode() : 0; - result = 31 * result + ((this.httpMethod != null) ? this.httpMethod.hashCode() : 0); - result = 31 * result + (this.caseSensitive ? 1231 : 1237); - return result; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("Ant [pattern='").append(this.pattern).append("'"); - if (this.httpMethod != null) { - sb.append(", ").append(this.httpMethod); - } - sb.append("]"); - return sb.toString(); - } - - private interface Matcher { - - boolean matches(String path); - - Map extractUriTemplateVariables(String path); - - } - - private static final class SpringAntMatcher implements Matcher { - - private final AntPathMatcher antMatcher; - - private final String pattern; - - private SpringAntMatcher(String pattern, boolean caseSensitive) { - this.pattern = pattern; - this.antMatcher = createMatcher(caseSensitive); - } - - @Override - public boolean matches(String path) { - return this.antMatcher.match(this.pattern, path); - } - - @Override - public Map extractUriTemplateVariables(String path) { - return this.antMatcher.extractUriTemplateVariables(this.pattern, path); - } - - private static AntPathMatcher createMatcher(boolean caseSensitive) { - AntPathMatcher matcher = new AntPathMatcher(); - matcher.setTrimTokens(false); - matcher.setCaseSensitive(caseSensitive); - return matcher; - } - - } - - /** - * Optimized matcher for trailing wildcards - */ - private static final class SubpathMatcher implements Matcher { - - private final String subpath; - - private final int length; - - private final boolean caseSensitive; - - private SubpathMatcher(String subpath, boolean caseSensitive) { - Assert.isTrue(!subpath.contains("*"), "subpath cannot contain \"*\""); - this.subpath = caseSensitive ? subpath : subpath.toLowerCase(Locale.ROOT); - this.length = subpath.length(); - this.caseSensitive = caseSensitive; - } - - @Override - public boolean matches(String path) { - if (!this.caseSensitive) { - path = path.toLowerCase(Locale.ROOT); - } - return path.startsWith(this.subpath) && (path.length() == this.length || path.charAt(this.length) == '/'); - } - - @Override - public Map extractUriTemplateVariables(String path) { - return Collections.emptyMap(); - } - - } - -} diff --git a/web/src/test/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcherTests.java b/web/src/test/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcherTests.java deleted file mode 100644 index 047f42604c..0000000000 --- a/web/src/test/java/org/springframework/security/web/servlet/util/matcher/MvcRequestMatcherTests.java +++ /dev/null @@ -1,275 +0,0 @@ -/* - * Copyright 2012-2022 the original author or authors. - * - * Licensed 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 - * - * https://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.springframework.security.web.servlet.util.matcher; - -import java.util.Collections; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.http.HttpMethod; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.web.HttpRequestMethodNotSupportedException; -import org.springframework.web.servlet.handler.HandlerMappingIntrospector; -import org.springframework.web.servlet.handler.MatchableHandlerMapping; -import org.springframework.web.servlet.handler.RequestMatchResult; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.verifyNoMoreInteractions; - -/** - * @author Rob Winch - * @author Eddú Meléndez - * @author Evgeniy Cheban - */ -@ExtendWith(MockitoExtension.class) -public class MvcRequestMatcherTests { - - @Mock - HandlerMappingIntrospector introspector; - - @Mock - MatchableHandlerMapping mapping; - - @Mock - RequestMatchResult result; - - @Captor - ArgumentCaptor pattern; - - MockHttpServletRequest request; - - MvcRequestMatcher matcher; - - @BeforeEach - public void setup() { - this.request = new MockHttpServletRequest(); - this.request.setMethod("GET"); - this.request.setServletPath("/path"); - this.matcher = new MvcRequestMatcher(this.introspector, "/path"); - } - - @Test - public void extractUriTemplateVariablesSuccess() throws Exception { - this.matcher = new MvcRequestMatcher(this.introspector, "/{p}"); - given(this.introspector.getMatchableHandlerMapping(this.request)).willReturn(null); - assertThat(this.matcher.extractUriTemplateVariables(this.request)).containsEntry("p", "path"); - assertThat(this.matcher.matcher(this.request).getVariables()).containsEntry("p", "path"); - } - - @Test - public void extractUriTemplateVariablesFail() throws Exception { - given(this.result.extractUriTemplateVariables()).willReturn(Collections.emptyMap()); - given(this.introspector.getMatchableHandlerMapping(this.request)).willReturn(this.mapping); - given(this.mapping.match(eq(this.request), this.pattern.capture())).willReturn(this.result); - assertThat(this.matcher.extractUriTemplateVariables(this.request)).isEmpty(); - assertThat(this.matcher.matcher(this.request).getVariables()).isEmpty(); - } - - @Test - public void extractUriTemplateVariablesDefaultSuccess() throws Exception { - this.matcher = new MvcRequestMatcher(this.introspector, "/{p}"); - given(this.introspector.getMatchableHandlerMapping(this.request)).willReturn(null); - assertThat(this.matcher.extractUriTemplateVariables(this.request)).containsEntry("p", "path"); - assertThat(this.matcher.matcher(this.request).getVariables()).containsEntry("p", "path"); - } - - @Test - public void extractUriTemplateVariablesDefaultFail() throws Exception { - this.matcher = new MvcRequestMatcher(this.introspector, "/nomatch/{p}"); - given(this.introspector.getMatchableHandlerMapping(this.request)).willReturn(null); - assertThat(this.matcher.extractUriTemplateVariables(this.request)).isEmpty(); - assertThat(this.matcher.matcher(this.request).getVariables()).isEmpty(); - } - - @Test - public void matchesServletPathTrue() throws Exception { - given(this.introspector.getMatchableHandlerMapping(this.request)).willReturn(this.mapping); - given(this.mapping.match(eq(this.request), this.pattern.capture())).willReturn(this.result); - this.matcher.setServletPath("/spring"); - this.request.setServletPath("/spring"); - assertThat(this.matcher.matches(this.request)).isTrue(); - assertThat(this.pattern.getValue()).isEqualTo("/path"); - } - - @Test - public void matchesServletPathFalse() { - this.matcher.setServletPath("/spring"); - this.request.setServletPath("/"); - assertThat(this.matcher.matches(this.request)).isFalse(); - } - - @Test - public void matchesPathOnlyTrue() throws Exception { - given(this.introspector.getMatchableHandlerMapping(this.request)).willReturn(this.mapping); - given(this.mapping.match(eq(this.request), this.pattern.capture())).willReturn(this.result); - assertThat(this.matcher.matches(this.request)).isTrue(); - assertThat(this.pattern.getValue()).isEqualTo("/path"); - } - - @Test - public void matchesDefaultMatches() throws Exception { - given(this.introspector.getMatchableHandlerMapping(this.request)).willReturn(null); - assertThat(this.matcher.matches(this.request)).isTrue(); - } - - @Test - public void matchesDefaultDoesNotMatch() throws Exception { - this.request.setServletPath("/other"); - given(this.introspector.getMatchableHandlerMapping(this.request)).willReturn(null); - assertThat(this.matcher.matches(this.request)).isFalse(); - } - - @Test - public void matchesPathOnlyFalse() throws Exception { - given(this.introspector.getMatchableHandlerMapping(this.request)).willReturn(this.mapping); - assertThat(this.matcher.matches(this.request)).isFalse(); - } - - @Test - public void matchesMethodAndPathTrue() throws Exception { - this.matcher.setMethod(HttpMethod.GET); - given(this.introspector.getMatchableHandlerMapping(this.request)).willReturn(this.mapping); - given(this.mapping.match(eq(this.request), this.pattern.capture())).willReturn(this.result); - assertThat(this.matcher.matches(this.request)).isTrue(); - assertThat(this.pattern.getValue()).isEqualTo("/path"); - } - - @Test - public void matchesMethodAndPathFalseMethod() { - this.matcher.setMethod(HttpMethod.POST); - assertThat(this.matcher.matches(this.request)).isFalse(); - // method compare should be done first since faster - verifyNoMoreInteractions(this.introspector); - } - - /** - * Malicious users can specify any HTTP Method to create a stacktrace and try to - * expose useful information about the system. We should ensure we ignore invalid HTTP - * methods. - */ - @Test - public void matchesInvalidMethodOnRequest() { - this.matcher.setMethod(HttpMethod.GET); - this.request.setMethod("invalid"); - assertThat(this.matcher.matches(this.request)).isFalse(); - // method compare should be done first since faster - verifyNoMoreInteractions(this.introspector); - } - - @Test - public void matchesMethodAndPathFalsePath() throws Exception { - this.matcher.setMethod(HttpMethod.GET); - given(this.introspector.getMatchableHandlerMapping(this.request)).willReturn(this.mapping); - assertThat(this.matcher.matches(this.request)).isFalse(); - } - - @Test - public void matchesGetMatchableHandlerMappingNull() { - assertThat(this.matcher.matches(this.request)).isTrue(); - } - - @Test - public void matchesGetMatchableHandlerMappingThrows() throws Exception { - given(this.introspector.getMatchableHandlerMapping(this.request)) - .willThrow(new HttpRequestMethodNotSupportedException(this.request.getMethod())); - assertThat(this.matcher.matches(this.request)).isTrue(); - } - - @Test - public void toStringWhenAll() { - this.matcher.setMethod(HttpMethod.GET); - this.matcher.setServletPath("/spring"); - assertThat(this.matcher.toString()).isEqualTo("Mvc [pattern='/path', servletPath='/spring', GET]"); - } - - @Test - public void toStringWhenHttpMethod() { - this.matcher.setMethod(HttpMethod.GET); - assertThat(this.matcher.toString()).isEqualTo("Mvc [pattern='/path', GET]"); - } - - @Test - public void toStringWhenServletPath() { - this.matcher.setServletPath("/spring"); - assertThat(this.matcher.toString()).isEqualTo("Mvc [pattern='/path', servletPath='/spring']"); - } - - @Test - public void toStringWhenOnlyPattern() { - assertThat(this.matcher.toString()).isEqualTo("Mvc [pattern='/path']"); - } - - @Test - public void matcherWhenMethodNotMatchesThenNotMatchResult() { - this.matcher.setMethod(HttpMethod.POST); - assertThat(this.matcher.matcher(this.request).isMatch()).isFalse(); - } - - @Test - public void matcherWhenMethodMatchesThenMatchResult() { - this.matcher.setMethod(HttpMethod.GET); - assertThat(this.matcher.matcher(this.request).isMatch()).isTrue(); - } - - @Test - public void matcherWhenServletPathNotMatchesThenNotMatchResult() { - this.matcher.setServletPath("/spring"); - assertThat(this.matcher.matcher(this.request).isMatch()).isFalse(); - } - - @Test - public void matcherWhenServletPathMatchesThenMatchResult() { - this.matcher.setServletPath("/path"); - assertThat(this.matcher.matcher(this.request).isMatch()).isTrue(); - } - - @Test - public void builderWhenServletPathThenServletPathPresent() { - MvcRequestMatcher matcher = new MvcRequestMatcher.Builder(this.introspector).servletPath("/path") - .pattern("/endpoint"); - assertThat(matcher.getServletPath()).isEqualTo("/path"); - assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/endpoint"); - assertThat(ReflectionTestUtils.getField(matcher, "method")).isNull(); - } - - @Test - public void builderWhenPatternThenPatternPresent() { - MvcRequestMatcher matcher = new MvcRequestMatcher.Builder(this.introspector).pattern("/endpoint"); - assertThat(matcher.getServletPath()).isNull(); - assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/endpoint"); - assertThat(ReflectionTestUtils.getField(matcher, "method")).isNull(); - } - - @Test - public void builderWhenMethodAndPatternThenMethodAndPatternPresent() { - MvcRequestMatcher matcher = new MvcRequestMatcher.Builder(this.introspector).pattern(HttpMethod.GET, - "/endpoint"); - assertThat(matcher.getServletPath()).isNull(); - assertThat(ReflectionTestUtils.getField(matcher, "pattern")).isEqualTo("/endpoint"); - assertThat(ReflectionTestUtils.getField(matcher, "method")).isEqualTo(HttpMethod.GET); - } - -} diff --git a/web/src/test/java/org/springframework/security/web/util/matcher/AntPathRequestMatcherTests.java b/web/src/test/java/org/springframework/security/web/util/matcher/AntPathRequestMatcherTests.java deleted file mode 100644 index 09c4d194a7..0000000000 --- a/web/src/test/java/org/springframework/security/web/util/matcher/AntPathRequestMatcherTests.java +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright 2002-2022 the original author or authors. - * - * Licensed 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 - * - * https://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.springframework.security.web.util.matcher; - -import jakarta.servlet.http.HttpServletRequest; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - -import org.springframework.http.HttpMethod; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.util.ReflectionTestUtils; -import org.springframework.web.util.UrlPathHelper; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.mockito.BDDMockito.given; -import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; - -/** - * @author Luke Taylor - * @author Rob Winch - * @author Evgeniy Cheban - */ -@ExtendWith(MockitoExtension.class) -public class AntPathRequestMatcherTests { - - @Mock - private HttpServletRequest request; - - @Test - public void matchesWhenUrlPathHelperThenMatchesOnRequestUri() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/foo/bar", null, true, new UrlPathHelper()); - MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo/bar"); - assertThat(matcher.matches(request)).isTrue(); - } - - @Test - public void singleWildcardMatchesAnyPath() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/**"); - assertThat(matcher.getPattern()).isEqualTo("/**"); - assertThat(matcher.matches(createRequest("/blah"))).isTrue(); - matcher = new AntPathRequestMatcher("**"); - assertThat(matcher.matches(createRequest("/blah"))).isTrue(); - assertThat(matcher.matches(createRequest(""))).isTrue(); - } - - @Test - public void trailingWildcardMatchesCorrectly() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/blah/blAh/**", null, false); - assertThat(matcher.matches(createRequest("/BLAH/blah"))).isTrue(); - assertThat(matcher.matches(createRequest("/blah/bleh"))).isFalse(); - assertThat(matcher.matches(createRequest("/blah/blah/"))).isTrue(); - assertThat(matcher.matches(createRequest("/blah/blah/xxx"))).isTrue(); - assertThat(matcher.matches(createRequest("/blah/blaha"))).isFalse(); - assertThat(matcher.matches(createRequest("/blah/bleh/"))).isFalse(); - MockHttpServletRequest request = createRequest("/blah/"); - request.setPathInfo("blah/bleh"); - assertThat(matcher.matches(request)).isTrue(); - matcher = new AntPathRequestMatcher("/bl?h/blAh/**", null, false); - assertThat(matcher.matches(createRequest("/BLAH/Blah/aaa/"))).isTrue(); - assertThat(matcher.matches(createRequest("/bleh/Blah"))).isTrue(); - matcher = new AntPathRequestMatcher("/blAh/**/blah/**", null, false); - assertThat(matcher.matches(createRequest("/blah/blah"))).isTrue(); - assertThat(matcher.matches(createRequest("/blah/bleh"))).isFalse(); - assertThat(matcher.matches(createRequest("/blah/aaa/blah/bbb"))).isTrue(); - } - - @Test - public void trailingWildcardWithVariableMatchesCorrectly() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/{id}/blAh/**", null, false); - assertThat(matcher.matches(createRequest("/1234/blah"))).isTrue(); - assertThat(matcher.matches(createRequest("/4567/bleh"))).isFalse(); - assertThat(matcher.matches(createRequest("/paskos/blah/"))).isTrue(); - assertThat(matcher.matches(createRequest("/12345/blah/xxx"))).isTrue(); - assertThat(matcher.matches(createRequest("/12345/blaha"))).isFalse(); - assertThat(matcher.matches(createRequest("/paskos/bleh/"))).isFalse(); - } - - @Test - public void nontrailingWildcardWithVariableMatchesCorrectly() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/**/{id}"); - assertThat(matcher.matches(createRequest("/blah/1234"))).isTrue(); - assertThat(matcher.matches(createRequest("/bleh/4567"))).isTrue(); - assertThat(matcher.matches(createRequest("/paskos/blah/"))).isFalse(); - assertThat(matcher.matches(createRequest("/12345/blah/xxx"))).isTrue(); - assertThat(matcher.matches(createRequest("/12345/blaha"))).isTrue(); - assertThat(matcher.matches(createRequest("/paskos/bleh/"))).isFalse(); - } - - @Test - public void requestHasNullMethodMatches() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/something/*", "GET"); - HttpServletRequest request = createRequestWithNullMethod("/something/here"); - assertThat(matcher.matches(request)).isTrue(); - } - - // SEC-2084 - @Test - public void requestHasNullMethodNoMatch() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/something/*", "GET"); - HttpServletRequest request = createRequestWithNullMethod("/nomatch"); - assertThat(matcher.matches(request)).isFalse(); - } - - @Test - public void requestHasNullMethodAndNullMatcherMatches() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/something/*"); - MockHttpServletRequest request = createRequest("/something/here"); - request.setMethod(null); - assertThat(matcher.matches(request)).isTrue(); - } - - @Test - public void requestHasNullMethodAndNullMatcherNoMatch() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/something/*"); - MockHttpServletRequest request = createRequest("/nomatch"); - request.setMethod(null); - assertThat(matcher.matches(request)).isFalse(); - } - - @Test - public void exactMatchOnlyMatchesIdenticalPath() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/login.html"); - assertThat(matcher.matches(createRequest("/login.html"))).isTrue(); - assertThat(matcher.matches(createRequest("/login.html/"))).isFalse(); - assertThat(matcher.matches(createRequest("/login.html/blah"))).isFalse(); - } - - @Test - public void httpMethodSpecificMatchOnlyMatchesRequestsWithCorrectMethod() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/blah", "GET"); - MockHttpServletRequest request = createRequest("/blah"); - request.setMethod("GET"); - assertThat(matcher.matches(request)).isTrue(); - request.setMethod("POST"); - assertThat(matcher.matches(request)).isFalse(); - } - - @Test - public void caseSensitive() { - MockHttpServletRequest request = createRequest("/UPPER"); - assertThat(new AntPathRequestMatcher("/upper", null, true).matches(request)).isFalse(); - assertThat(new AntPathRequestMatcher("/upper", "POST", true).matches(request)).isFalse(); - assertThat(new AntPathRequestMatcher("/upper", "GET", true).matches(request)).isFalse(); - assertThat(new AntPathRequestMatcher("/upper", null, false).matches(request)).isTrue(); - assertThat(new AntPathRequestMatcher("/upper", "POST", false).matches(request)).isTrue(); - } - - @Test - public void spacesInPathSegmentsAreNotIgnored() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/path/*/bar"); - MockHttpServletRequest request = createRequest("/path /foo/bar"); - assertThat(matcher.matches(request)).isFalse(); - matcher = new AntPathRequestMatcher("/path/foo"); - request = createRequest("/path /foo"); - assertThat(matcher.matches(request)).isFalse(); - } - - @Test - public void equalsBehavesCorrectly() { - // Both universal wildcard options should be equal - assertThat(new AntPathRequestMatcher("**")).isEqualTo(new AntPathRequestMatcher("/**")); - assertThat(new AntPathRequestMatcher("/xyz")).isEqualTo(new AntPathRequestMatcher("/xyz")); - assertThat(new AntPathRequestMatcher("/xyz", "POST")).isEqualTo(new AntPathRequestMatcher("/xyz", "POST")); - assertThat(new AntPathRequestMatcher("/xyz", "POST")).isNotEqualTo(new AntPathRequestMatcher("/xyz", "GET")); - assertThat(new AntPathRequestMatcher("/xyz")).isNotEqualTo(new AntPathRequestMatcher("/xxx")); - assertThat(new AntPathRequestMatcher("/xyz").equals(AnyRequestMatcher.INSTANCE)).isFalse(); - assertThat(new AntPathRequestMatcher("/xyz", "GET", false)) - .isNotEqualTo(new AntPathRequestMatcher("/xyz", "GET", true)); - } - - @Test - public void toStringIsOk() { - new AntPathRequestMatcher("/blah").toString(); - new AntPathRequestMatcher("/blah", "GET").toString(); - } - - // SEC-2831 - @Test - public void matchesWithInvalidMethod() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/blah", "GET"); - MockHttpServletRequest request = createRequest("/blah"); - request.setMethod("INVALID"); - assertThat(matcher.matches(request)).isFalse(); - } - - // gh-9285 - @Test - public void matcherWhenMatchAllPatternThenMatchResult() { - AntPathRequestMatcher matcher = new AntPathRequestMatcher("/**"); - MockHttpServletRequest request = createRequest("/blah"); - assertThat(matcher.matcher(request).isMatch()).isTrue(); - } - - @Test - public void staticAntMatcherWhenPatternProvidedThenPattern() { - AntPathRequestMatcher matcher = antMatcher("/path"); - assertThat(matcher.getPattern()).isEqualTo("/path"); - } - - @Test - public void staticAntMatcherWhenMethodProvidedThenMatchAll() { - AntPathRequestMatcher matcher = antMatcher(HttpMethod.GET); - assertThat(ReflectionTestUtils.getField(matcher, "httpMethod")).isEqualTo(HttpMethod.GET); - } - - @Test - public void staticAntMatcherWhenMethodAndPatternProvidedThenMatchAll() { - AntPathRequestMatcher matcher = antMatcher(HttpMethod.POST, "/path"); - assertThat(matcher.getPattern()).isEqualTo("/path"); - assertThat(ReflectionTestUtils.getField(matcher, "httpMethod")).isEqualTo(HttpMethod.POST); - } - - @Test - public void staticAntMatcherWhenMethodNullThenException() { - assertThatIllegalArgumentException().isThrownBy(() -> antMatcher((HttpMethod) null)) - .withMessage("method cannot be null"); - } - - @Test - public void staticAntMatcherWhenPatternNullThenException() { - assertThatIllegalArgumentException().isThrownBy(() -> antMatcher((String) null)) - .withMessage("pattern cannot be empty"); - } - - @Test - public void forMethodWhenMethodThenMatches() { - AntPathRequestMatcher matcher = antMatcher(HttpMethod.POST); - MockHttpServletRequest request = createRequest("/path"); - assertThat(matcher.matches(request)).isTrue(); - request.setServletPath("/another-path/second"); - assertThat(matcher.matches(request)).isTrue(); - request.setMethod("GET"); - assertThat(matcher.matches(request)).isFalse(); - } - - private HttpServletRequest createRequestWithNullMethod(String path) { - given(this.request.getServletPath()).willReturn(path); - return this.request; - } - - private MockHttpServletRequest createRequest(String path) { - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setQueryString("doesntMatter"); - request.setServletPath(path); - request.setMethod("POST"); - return request; - } - -}