From ae9eb6f56b23962d01df9be4d4e3d11d74f27caa Mon Sep 17 00:00:00 2001 From: Eleftheria Stein Date: Tue, 2 Jul 2019 14:27:02 -0400 Subject: [PATCH] Allow configuration of x509 through nested builder Issue: gh-5557 --- .../annotation/web/builders/HttpSecurity.java | 36 +++++++++++ .../web/configurers/X509ConfigurerTests.java | 64 +++++++++++++++++++ 2 files changed, 100 insertions(+) diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java index 7d8654687d..3bd002b5e8 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/builders/HttpSecurity.java @@ -861,6 +861,42 @@ public final class HttpSecurity extends return getOrApply(new X509Configurer<>()); } + /** + * Configures X509 based pre authentication. + * + *

Example Configuration

+ * + * The following configuration will attempt to extract the username from the X509 + * certificate. Remember that the Servlet Container will need to be configured to + * request client certificates in order for this to work. + * + *
+	 * @Configuration
+	 * @EnableWebSecurity
+	 * public class X509SecurityConfig extends WebSecurityConfigurerAdapter {
+	 *
+	 * 	@Override
+	 * 	protected void configure(HttpSecurity http) throws Exception {
+	 * 		http
+	 * 			.authorizeRequests()
+	 * 				.antMatchers("/**")
+	 * 				.hasRole("USER")
+	 * 				.and()
+	 * 			.x509(withDefaults());
+	 * 	}
+	 * }
+	 * 
+ * + * @param x509Customizer the {@link Customizer} to provide more options for + * the {@link X509Configurer} + * @return the {@link HttpSecurity} for further customizations + * @throws Exception + */ + public HttpSecurity x509(Customizer> x509Customizer) throws Exception { + x509Customizer.customize(getOrApply(new X509Configurer<>())); + return HttpSecurity.this; + } + /** * Allows configuring of Remember Me authentication. * diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/X509ConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/X509ConfigurerTests.java index 49ca4e431b..f7c24c0013 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/X509ConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/X509ConfigurerTests.java @@ -38,6 +38,7 @@ import java.security.cert.X509Certificate; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; +import static org.springframework.security.config.Customizer.withDefaults; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.x509; import static org.springframework.security.test.web.servlet.response.SecurityMockMvcResultMatchers.authenticated; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; @@ -122,6 +123,69 @@ public class X509ConfigurerTests { } } + @Test + public void x509WhenConfiguredInLambdaThenUsesDefaults() throws Exception { + this.spring.register(DefaultsInLambdaConfig.class).autowire(); + X509Certificate certificate = loadCert("rod.cer"); + + this.mvc.perform(get("/") + .with(x509(certificate))) + .andExpect(authenticated().withUsername("rod")); + } + + @EnableWebSecurity + static class DefaultsInLambdaConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .x509(withDefaults()); + // @formatter:on + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth + .inMemoryAuthentication() + .withUser("rod").password("password").roles("USER", "ADMIN"); + // @formatter:on + } + } + + @Test + public void x509WhenSubjectPrincipalRegexInLambdaThenUsesRegexToExtractPrincipal() throws Exception { + this.spring.register(SubjectPrincipalRegexInLambdaConfig.class).autowire(); + X509Certificate certificate = loadCert("rodatexampledotcom.cer"); + + this.mvc.perform(get("/") + .with(x509(certificate))) + .andExpect(authenticated().withUsername("rod")); + } + + @EnableWebSecurity + static class SubjectPrincipalRegexInLambdaConfig extends WebSecurityConfigurerAdapter { + @Override + protected void configure(HttpSecurity http) throws Exception { + // @formatter:off + http + .x509(x509 -> + x509 + .subjectPrincipalRegex("CN=(.*?)@example.com(?:,|$)") + ); + // @formatter:on + } + + @Override + protected void configure(AuthenticationManagerBuilder auth) throws Exception { + // @formatter:off + auth + .inMemoryAuthentication() + .withUser("rod").password("password").roles("USER", "ADMIN"); + // @formatter:on + } + } + private T loadCert(String location) { try (InputStream is = new ClassPathResource(location).getInputStream()) { CertificateFactory certFactory = CertificateFactory.getInstance("X.509");