Pārlūkot izejas kodu

Use UserWebTestClientConfigurer

Closes gh-17496
Rob Winch 1 mēnesi atpakaļ
vecāks
revīzija
e48fdd5ed4

+ 7 - 57
config/src/test/kotlin/org/springframework/security/config/web/server/ServerX509DslTests.kt

@@ -16,11 +16,6 @@
 
 package org.springframework.security.config.web.server
 
-import io.mockk.every
-import io.mockk.mockk
-import java.security.cert.Certificate
-import java.security.cert.CertificateFactory
-import java.security.cert.X509Certificate
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.extension.ExtendWith
 import org.springframework.beans.factory.annotation.Autowired
@@ -28,10 +23,6 @@ import org.springframework.context.ApplicationContext
 import org.springframework.context.annotation.Bean
 import org.springframework.context.annotation.Configuration
 import org.springframework.core.io.ClassPathResource
-import org.springframework.http.client.reactive.ClientHttpConnector
-import org.springframework.http.server.reactive.ServerHttpRequestDecorator
-import org.springframework.http.server.reactive.SslInfo
-import org.springframework.lang.Nullable
 import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity
 import org.springframework.security.config.test.SpringTestContext
 import org.springframework.security.config.test.SpringTestContextExtension
@@ -41,19 +32,15 @@ import org.springframework.security.core.userdetails.User
 import org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor
 import org.springframework.security.web.server.SecurityWebFilterChain
 import org.springframework.security.web.server.authentication.ReactivePreAuthenticatedAuthenticationManager
-import org.springframework.test.web.reactive.server.MockServerConfigurer
+import org.springframework.test.web.reactive.server.UserWebTestClientConfigurer.x509
 import org.springframework.test.web.reactive.server.WebTestClient
-import org.springframework.test.web.reactive.server.WebTestClientConfigurer
 import org.springframework.test.web.reactive.server.expectBody
 import org.springframework.web.bind.annotation.GetMapping
 import org.springframework.web.bind.annotation.RestController
 import org.springframework.web.reactive.config.EnableWebFlux
-import org.springframework.web.server.ServerWebExchange
-import org.springframework.web.server.ServerWebExchangeDecorator
-import org.springframework.web.server.WebFilter
-import org.springframework.web.server.WebFilterChain
-import org.springframework.web.server.adapter.WebHttpHandlerBuilder
-import reactor.core.publisher.Mono
+import java.security.cert.Certificate
+import java.security.cert.CertificateFactory
+import java.security.cert.X509Certificate
 
 /**
  * Tests for [ServerX509Dsl]
@@ -83,7 +70,7 @@ class ServerX509DslTests {
         val certificate = loadCert<X509Certificate>("rod.cer")
 
         this.client
-                .mutateWith(mockX509(certificate))
+                .mutateWith(x509(certificate))
                 .get()
                 .uri("/username")
                 .exchange()
@@ -111,7 +98,7 @@ class ServerX509DslTests {
         val certificate = loadCert<X509Certificate>("rodatexampledotcom.cer")
 
         this.client
-                .mutateWith(mockX509(certificate))
+                .mutateWith(x509(certificate))
                 .get()
                 .uri("/username")
                 .exchange()
@@ -143,7 +130,7 @@ class ServerX509DslTests {
         val certificate = loadCert<X509Certificate>("rod.cer")
 
         this.client
-                .mutateWith(mockX509(certificate))
+                .mutateWith(x509(certificate))
                 .get()
                 .uri("/username")
                 .exchange()
@@ -195,43 +182,6 @@ class ServerX509DslTests {
         }
     }
 
-    private fun mockX509(certificate: X509Certificate): X509Mutator {
-        return X509Mutator(certificate)
-    }
-
-    private class X509Mutator internal constructor(private var certificate: X509Certificate) : WebTestClientConfigurer, MockServerConfigurer {
-
-        override fun afterConfigurerAdded(builder: WebTestClient.Builder,
-                                          @Nullable httpHandlerBuilder: WebHttpHandlerBuilder?,
-                                          @Nullable connector: ClientHttpConnector?) {
-            val filter = SetSslInfoWebFilter(certificate)
-            httpHandlerBuilder!!.filters { filters: MutableList<WebFilter> -> filters.add(0, filter) }
-        }
-    }
-
-    private class SetSslInfoWebFilter(var certificate: X509Certificate) : WebFilter {
-
-        override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
-            return chain.filter(decorate(exchange))
-        }
-
-        private fun decorate(exchange: ServerWebExchange): ServerWebExchange {
-            val decorated: ServerHttpRequestDecorator = object : ServerHttpRequestDecorator(exchange.request) {
-                override fun getSslInfo(): SslInfo {
-                    val sslInfo: SslInfo = mockk()
-                    every { sslInfo.sessionId } returns "sessionId"
-                    every { sslInfo.peerCertificates } returns arrayOf(certificate)
-                    return sslInfo
-                }
-            }
-            return object : ServerWebExchangeDecorator(exchange) {
-                override fun getRequest(): org.springframework.http.server.reactive.ServerHttpRequest {
-                    return decorated
-                }
-            }
-        }
-    }
-
     private fun <T : Certificate> loadCert(location: String): T {
         ClassPathResource(location).inputStream.use { inputStream ->
             val certFactory = CertificateFactory.getInstance("X.509")

+ 1 - 0
docs/modules/ROOT/nav.adoc

@@ -176,6 +176,7 @@
 **** xref:reactive/test/web/authentication.adoc[Testing Authentication]
 **** xref:reactive/test/web/csrf.adoc[Testing CSRF]
 **** xref:reactive/test/web/oauth2.adoc[Testing OAuth 2.0]
+**** xref:reactive/test/web/x509.adoc[Testing X509]
 ** xref:reactive/configuration/webflux.adoc[WebFlux Security]
 * xref:native-image/index.adoc[GraalVM Native Image Support]
 ** xref:native-image/method-security.adoc[Method Security]

+ 4 - 0
docs/modules/ROOT/pages/reactive/test/web/x509.adoc

@@ -0,0 +1,4 @@
+= X509
+
+Spring Framework provides first class support for testing X509 with `WebTestClient`.
+For details refer to javadoc:{spring-framework-api-url}org.springframework.test.web.reactive.server.UserWebTestClientConfigurer[].

+ 1 - 52
docs/src/test/java/org/springframework/security/docs/reactive/authentication/reactivex509/X509ConfigurationTests.java

@@ -21,31 +21,20 @@ import java.security.cert.Certificate;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 
-import org.jetbrains.annotations.NotNull;
-import org.jspecify.annotations.Nullable;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
-import reactor.core.publisher.Mono;
 
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.core.io.ClassPathResource;
-import org.springframework.http.client.reactive.ClientHttpConnector;
-import org.springframework.http.server.reactive.ServerHttpRequest;
-import org.springframework.http.server.reactive.SslInfo;
 import org.springframework.security.config.test.SpringTestContext;
 import org.springframework.security.config.test.SpringTestContextExtension;
 import org.springframework.security.test.web.reactive.server.WebTestClientBuilder;
 import org.springframework.security.web.authentication.preauth.x509.X509TestUtils;
 import org.springframework.test.web.reactive.server.WebTestClient;
-import org.springframework.test.web.reactive.server.WebTestClientConfigurer;
-import org.springframework.web.server.ServerWebExchange;
 import org.springframework.web.server.WebFilter;
-import org.springframework.web.server.WebFilterChain;
-import org.springframework.web.server.adapter.WebHttpHandlerBuilder;
 
 import static org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers.springSecurity;
-import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.x509;
-import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.reactive.server.UserWebTestClientConfigurer.x509;
 
 /**
  * Tests {@link CustomX509Configuration}.
@@ -96,46 +85,6 @@ public class X509ConfigurationTests {
 		// @formatter:on
 	}
 
-	private static @NotNull WebTestClientConfigurer x509(X509Certificate certificate) {
-		return (builder, httpHandlerBuilder, connector) -> {
-			builder.apply(new WebTestClientConfigurer() {
-				@Override
-				public void afterConfigurerAdded(WebTestClient.Builder builder,
-						@Nullable WebHttpHandlerBuilder httpHandlerBuilder,
-						@Nullable ClientHttpConnector connector) {
-					SslInfo sslInfo = new SslInfo() {
-						@Override
-						public @Nullable String getSessionId() {
-							return "sessionId";
-						}
-
-						@Override
-						public X509Certificate @Nullable [] getPeerCertificates() {
-							return new X509Certificate[] {  certificate };
-						}
-					};
-					httpHandlerBuilder.filters((filters) -> filters.add(0, new SslInfoOverrideWebFilter(sslInfo)));
-				}
-			});
-		};
-	}
-
-	private static class SslInfoOverrideWebFilter implements WebFilter {
-		private final SslInfo sslInfo;
-
-		private SslInfoOverrideWebFilter(SslInfo sslInfo) {
-			this.sslInfo = sslInfo;
-		}
-
-		@Override
-		public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
-			ServerHttpRequest sslInfoRequest = exchange.getRequest().mutate().sslInfo(sslInfo)
-					.build();
-			ServerWebExchange sslInfoExchange = exchange.mutate().request(sslInfoRequest).build();
-			return chain.filter(sslInfoExchange);
-		}
-	}
-
 	private <T extends Certificate> T loadCert(String location) {
 		try (InputStream is = new ClassPathResource(location).getInputStream()) {
 			CertificateFactory certFactory = CertificateFactory.getInstance("X.509");

+ 1 - 33
docs/src/test/kotlin/org/springframework/security/kt/docs/reactive/authentication/reactivex509/X509ConfigurationTests.kt

@@ -26,6 +26,7 @@ import org.springframework.security.config.test.SpringTestContextExtension
 import org.springframework.security.test.web.reactive.server.SecurityMockServerConfigurers
 import org.springframework.security.test.web.reactive.server.WebTestClientBuilder.Http200RestController
 import org.springframework.security.web.authentication.preauth.x509.X509TestUtils
+import org.springframework.test.web.reactive.server.UserWebTestClientConfigurer.x509
 import org.springframework.test.web.reactive.server.WebTestClient
 import org.springframework.test.web.reactive.server.WebTestClientConfigurer
 import org.springframework.util.Assert
@@ -87,15 +88,6 @@ class X509ConfigurationTests {
         // @formatter:on
     }
 
-    private class SslInfoOverrideWebFilter(private val sslInfo: SslInfo) : WebFilter {
-        override fun filter(exchange: ServerWebExchange, chain: WebFilterChain): Mono<Void> {
-            val sslInfoRequest = exchange.getRequest().mutate().sslInfo(sslInfo)
-                .build()
-            val sslInfoExchange = exchange.mutate().request(sslInfoRequest).build()
-            return chain.filter(sslInfoExchange)
-        }
-    }
-
     private fun <T : Certificate?> loadCert(location: String): T {
         try {
             ClassPathResource(location).getInputStream().use { `is` ->
@@ -106,28 +98,4 @@ class X509ConfigurationTests {
             throw IllegalArgumentException(ex)
         }
     }
-
-    companion object {
-        private fun x509(certificate: X509Certificate): WebTestClientConfigurer {
-            return WebTestClientConfigurer { builder: WebTestClient.Builder, httpHandlerBuilder: WebHttpHandlerBuilder?, connector: ClientHttpConnector? ->
-
-                val sslInfo: SslInfo = object : SslInfo {
-                    override fun getSessionId(): String {
-                        return "sessionId"
-                    }
-
-                    override fun getPeerCertificates(): Array<X509Certificate> {
-                        return arrayOf(certificate)
-                    }
-                }
-                Assert.notNull(httpHandlerBuilder, "httpHandlerBuilder should not be null")
-                httpHandlerBuilder!!.filters(Consumer { filters: MutableList<WebFilter> ->
-                    filters.add(
-                        0,
-                        SslInfoOverrideWebFilter(sslInfo)
-                    )
-                })
-            }
-        }
-    }
 }

+ 1 - 21
web/src/test/java/org/springframework/security/web/server/authentication/ServerX509AuthenticationConverterTests.java

@@ -69,31 +69,11 @@ public class ServerX509AuthenticationConverterTests {
 	@Test
 	public void shouldReturnAuthenticationForValidCertificate() {
 		givenExtractPrincipalWillReturn();
-		this.request.sslInfo(new MockSslInfo(this.certificate));
+		this.request.sslInfo(SslInfo.from("123", this.certificate));
 		Authentication authentication = this.converter.convert(MockServerWebExchange.from(this.request.build()))
 			.block();
 		assertThat(authentication.getName()).isEqualTo("Luke Taylor");
 		assertThat(authentication.getCredentials()).isEqualTo(this.certificate);
 	}
 
-	class MockSslInfo implements SslInfo {
-
-		private final X509Certificate[] peerCertificates;
-
-		MockSslInfo(X509Certificate... peerCertificates) {
-			this.peerCertificates = peerCertificates;
-		}
-
-		@Override
-		public String getSessionId() {
-			return "mock-session-id";
-		}
-
-		@Override
-		public X509Certificate[] getPeerCertificates() {
-			return this.peerCertificates;
-		}
-
-	}
-
 }

+ 3 - 18
web/src/test/java/org/springframework/security/web/server/csrf/CookieServerCsrfTokenRepositoryTests.java

@@ -16,7 +16,6 @@
 
 package org.springframework.security.web.server.csrf;
 
-import java.security.cert.X509Certificate;
 import java.time.Duration;
 import java.time.temporal.ChronoUnit;
 
@@ -179,7 +178,7 @@ class CookieServerCsrfTokenRepositoryTests {
 
 	@Test
 	void saveTokenWhenSslInfoPresentThenSecure() {
-		this.request.sslInfo(new MockSslInfo());
+		this.request.sslInfo(SslInfo.from("sessionId"));
 		MockServerWebExchange exchange = MockServerWebExchange.from(this.request);
 		this.csrfTokenRepository.saveToken(exchange, createToken()).block();
 		ResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName);
@@ -239,7 +238,7 @@ class CookieServerCsrfTokenRepositoryTests {
 	@Test
 	void saveTokenWhenSecureFlagFalseAndSslInfoThenNotSecure() {
 		MockServerWebExchange exchange = MockServerWebExchange.from(this.request);
-		this.request.sslInfo(new MockSslInfo());
+		this.request.sslInfo(SslInfo.from("sessionId"));
 		this.csrfTokenRepository.setSecure(false);
 		this.csrfTokenRepository.saveToken(exchange, createToken()).block();
 		ResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName);
@@ -250,7 +249,7 @@ class CookieServerCsrfTokenRepositoryTests {
 	@Test
 	void saveTokenWhenSecureFlagFalseAndSslInfoThenNotSecureUsingCustomizer() {
 		MockServerWebExchange exchange = MockServerWebExchange.from(this.request);
-		this.request.sslInfo(new MockSslInfo());
+		this.request.sslInfo(SslInfo.from("sessionId"));
 		this.csrfTokenRepository.setCookieCustomizer((customizer) -> customizer.secure(false));
 		this.csrfTokenRepository.saveToken(exchange, createToken()).block();
 		ResponseCookie cookie = exchange.getResponse().getCookies().getFirst(this.expectedCookieName);
@@ -401,18 +400,4 @@ class CookieServerCsrfTokenRepositoryTests {
 		return new DefaultCsrfToken(headerName, parameterName, tokenValue);
 	}
 
-	static class MockSslInfo implements SslInfo {
-
-		@Override
-		public String getSessionId() {
-			return "sessionId";
-		}
-
-		@Override
-		public X509Certificate[] getPeerCertificates() {
-			return new X509Certificate[] {};
-		}
-
-	}
-
 }