2
0
Эх сурвалжийг харах

Add ParameterRequestMatcher

Closes gh-15342
Josh Cummings 1 жил өмнө
parent
commit
773e86701e

+ 2 - 20
config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurer.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2023 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -18,8 +18,6 @@ package org.springframework.security.config.annotation.web.configurers.saml2;
 
 import java.util.ArrayList;
 import java.util.List;
-import java.util.Objects;
-import java.util.function.Predicate;
 
 import jakarta.servlet.http.HttpServletRequest;
 
@@ -60,6 +58,7 @@ import org.springframework.security.web.csrf.CsrfLogoutHandler;
 import org.springframework.security.web.csrf.CsrfTokenRepository;
 import org.springframework.security.web.util.matcher.AndRequestMatcher;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.ParameterRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 
 /**
@@ -508,23 +507,6 @@ public final class Saml2LogoutConfigurer<H extends HttpSecurityBuilder<H>>
 
 	}
 
-	private static class ParameterRequestMatcher implements RequestMatcher {
-
-		Predicate<String> test = Objects::nonNull;
-
-		String name;
-
-		ParameterRequestMatcher(String name) {
-			this.name = name;
-		}
-
-		@Override
-		public boolean matches(HttpServletRequest request) {
-			return this.test.test(request.getParameter(this.name));
-		}
-
-	}
-
 	private static class Saml2RelyingPartyInitiatedLogoutFilter extends LogoutFilter {
 
 		Saml2RelyingPartyInitiatedLogoutFilter(LogoutSuccessHandler logoutSuccessHandler, LogoutHandler... handlers) {

+ 2 - 20
config/src/main/java/org/springframework/security/config/http/Saml2LogoutBeanDefinitionParser.java

@@ -1,5 +1,5 @@
 /*
- * Copyright 2002-2022 the original author or authors.
+ * Copyright 2002-2024 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.
@@ -18,8 +18,6 @@ package org.springframework.security.config.http;
 
 import java.util.Arrays;
 import java.util.List;
-import java.util.Objects;
-import java.util.function.Predicate;
 
 import jakarta.servlet.http.HttpServletRequest;
 import org.w3c.dom.Element;
@@ -44,6 +42,7 @@ import org.springframework.security.web.authentication.logout.SecurityContextLog
 import org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler;
 import org.springframework.security.web.util.matcher.AndRequestMatcher;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.ParameterRequestMatcher;
 import org.springframework.security.web.util.matcher.RequestMatcher;
 import org.springframework.util.CollectionUtils;
 import org.springframework.util.StringUtils;
@@ -228,23 +227,6 @@ final class Saml2LogoutBeanDefinitionParser implements BeanDefinitionParser {
 		return this.logoutFilter;
 	}
 
-	private static class ParameterRequestMatcher implements RequestMatcher {
-
-		Predicate<String> test = Objects::nonNull;
-
-		String name;
-
-		ParameterRequestMatcher(String name) {
-			this.name = name;
-		}
-
-		@Override
-		public boolean matches(HttpServletRequest request) {
-			return this.test.test(request.getParameter(this.name));
-		}
-
-	}
-
 	public static class Saml2RequestMatcher implements RequestMatcher {
 
 		private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder

+ 91 - 0
web/src/main/java/org/springframework/security/web/util/matcher/ParameterRequestMatcher.java

@@ -0,0 +1,91 @@
+/*
+ * Copyright 2002-2024 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.Map;
+import java.util.Objects;
+
+import jakarta.servlet.http.HttpServletRequest;
+
+/**
+ * A {@link RequestMatcher} for matching on a request parameter and its value.
+ *
+ * <p>
+ * The value may also be specified as a placeholder in order to match on any value,
+ * returning the value as part of the {@link MatchResult}.
+ *
+ * @author Josh Cummings
+ * @since 6.4
+ */
+public final class ParameterRequestMatcher implements RequestMatcher {
+
+	private static final MatchesValueMatcher NON_NULL = Objects::nonNull;
+
+	private final String name;
+
+	private final ValueMatcher matcher;
+
+	public ParameterRequestMatcher(String name) {
+		this.name = name;
+		this.matcher = NON_NULL;
+	}
+
+	public ParameterRequestMatcher(String name, String value) {
+		this.name = name;
+		MatchesValueMatcher matcher = value::equals;
+		if (value.startsWith("{") && value.endsWith("}")) {
+			String key = value.substring(1, value.length() - 1);
+			this.matcher = (v) -> (v != null) ? MatchResult.match(Map.of(key, v)) : MatchResult.notMatch();
+		}
+		else {
+			this.matcher = matcher;
+		}
+	}
+
+	@Override
+	public boolean matches(HttpServletRequest request) {
+		return matcher(request).isMatch();
+	}
+
+	@Override
+	public MatchResult matcher(HttpServletRequest request) {
+		String parameterValue = request.getParameter(this.name);
+		return this.matcher.matcher(parameterValue);
+	}
+
+	private interface ValueMatcher {
+
+		MatchResult matcher(String value);
+
+	}
+
+	private interface MatchesValueMatcher extends ValueMatcher {
+
+		default MatchResult matcher(String value) {
+			if (matches(value)) {
+				return MatchResult.match();
+			}
+			else {
+				return MatchResult.notMatch();
+			}
+		}
+
+		boolean matches(String value);
+
+	}
+
+}

+ 64 - 0
web/src/test/java/org/springframework/security/web/util/matcher/ParameterRequestMatcherTests.java

@@ -0,0 +1,64 @@
+/*
+ * Copyright 2002-2024 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 org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import org.springframework.mock.web.MockHttpServletRequest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests for {@link ParameterRequestMatcher}
+ *
+ * @author Josh Cummings
+ */
+@ExtendWith(MockitoExtension.class)
+public class ParameterRequestMatcherTests {
+
+	@Test
+	public void matchesWhenNameThenMatchesOnParameterName() {
+		ParameterRequestMatcher matcher = new ParameterRequestMatcher("name");
+		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo/bar");
+		assertThat(matcher.matches(request)).isFalse();
+		request.setParameter("name", "value");
+		assertThat(matcher.matches(request)).isTrue();
+	}
+
+	@Test
+	public void matchesWhenNameAndValueThenMatchesOnBoth() {
+		ParameterRequestMatcher matcher = new ParameterRequestMatcher("name", "value");
+		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo/bar");
+		request.setParameter("name", "value");
+		assertThat(matcher.matches(request)).isTrue();
+		request.setParameter("name", "wrong");
+		assertThat(matcher.matches(request)).isFalse();
+	}
+
+	@Test
+	public void matchesWhenValuePlaceholderThenMatchesOnName() {
+		ParameterRequestMatcher matcher = new ParameterRequestMatcher("name", "{placeholder}");
+		MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo/bar");
+		request.setParameter("name", "value");
+		RequestMatcher.MatchResult result = matcher.matcher(request);
+		assertThat(result.isMatch()).isTrue();
+		assertThat(result.getVariables().get("placeholder")).isEqualTo("value");
+	}
+
+}