From 47f505f7f7e262b71f4ef81a669164dd965e1137 Mon Sep 17 00:00:00 2001 From: Rob Winch Date: Wed, 7 Sep 2022 13:39:26 -0500 Subject: [PATCH] Remove Deprecated OpenSAML 3 Support Closes gh-10556 --- build.gradle | 8 - config/spring-security-config.gradle | 2 +- .../saml2/Saml2LoginConfigurer.java | 37 +- .../saml2/Saml2LogoutConfigurer.java | 31 +- .../saml2/Saml2LoginConfigurerTests.java | 67 -- .../pages/servlet/saml2/login/overview.adoc | 8 - docs/modules/ROOT/pages/whats-new.adoc | 2 + gradle.properties | 2 +- ...ing-security-saml2-service-provider.gradle | 79 -- .../OpenSaml4AuthenticationProvider.java | 2 +- ...penSaml4AuthenticationRequestResolver.java | 0 .../OpenSaml4LogoutRequestResolver.java | 0 .../OpenSaml4LogoutResponseResolver.java | 0 .../OpenSamlAuthenticationProvider.java | 864 ------------------ ...penSaml3AuthenticationRequestResolver.java | 112 --- .../OpenSaml3LogoutRequestResolver.java | 124 --- .../OpenSaml3LogoutResponseResolver.java | 120 --- .../OpenSamlAuthenticationProviderTests.java | 682 -------------- .../OpenSaml3LogoutRequestResolverTests.java | 66 -- .../OpenSaml3LogoutResponseResolverTests.java | 78 -- ...faultSaml2AuthenticatedPrincipalTests.java | 3 +- .../OpenSaml4AuthenticationProviderTests.java | 0 ...ml4AuthenticationRequestResolverTests.java | 0 .../OpenSaml4LogoutRequestResolverTests.java | 0 .../OpenSaml4LogoutResponseResolverTests.java | 0 25 files changed, 14 insertions(+), 2273 deletions(-) rename saml2/saml2-service-provider/src/{opensaml4Main => main}/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java (99%) rename saml2/saml2-service-provider/src/{opensaml4Main => main}/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml4AuthenticationRequestResolver.java (100%) rename saml2/saml2-service-provider/src/{opensaml4Main => main}/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutRequestResolver.java (100%) rename saml2/saml2-service-provider/src/{opensaml4Main => main}/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolver.java (100%) delete mode 100644 saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProvider.java delete mode 100644 saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml3AuthenticationRequestResolver.java delete mode 100644 saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutRequestResolver.java delete mode 100644 saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutResponseResolver.java delete mode 100644 saml2/saml2-service-provider/src/opensaml3Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java delete mode 100644 saml2/saml2-service-provider/src/opensaml3Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutRequestResolverTests.java delete mode 100644 saml2/saml2-service-provider/src/opensaml3Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutResponseResolverTests.java rename saml2/saml2-service-provider/src/{opensaml4Test => test}/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java (100%) rename saml2/saml2-service-provider/src/{opensaml4Test => test}/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml4AuthenticationRequestResolverTests.java (100%) rename saml2/saml2-service-provider/src/{opensaml4Test => test}/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutRequestResolverTests.java (100%) rename saml2/saml2-service-provider/src/{opensaml4Test => test}/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolverTests.java (100%) diff --git a/build.gradle b/build.gradle index 21893a72f83..c1a466ee6f4 100644 --- a/build.gradle +++ b/build.gradle @@ -118,14 +118,6 @@ updateDependenciesSettings { selection.reject("nimbus-jose-jwt gets updated when oauth2-oidc-sdk is updated to ensure consistency"); } } - components.all { selection -> - ModuleComponentIdentifier candidate = selection.getCandidate(); - // Do not compare version due to multiple versions existing - // will cause opensaml 3.x to be updated to 4.x - if (candidate.getGroup().equals("org.opensaml")) { - selection.reject("org.opensaml maintains two different versions, so it must be updated manually"); - } - } } } } diff --git a/config/spring-security-config.gradle b/config/spring-security-config.gradle index 7818f34e6ac..2388f51ba1c 100644 --- a/config/spring-security-config.gradle +++ b/config/spring-security-config.gradle @@ -46,8 +46,8 @@ dependencies { testImplementation project(path : ':spring-security-ldap', configuration : 'tests') testImplementation project(path : ':spring-security-oauth2-client', configuration : 'tests') testImplementation project(path : ':spring-security-oauth2-resource-server', configuration : 'tests') + testImplementation project(':spring-security-saml2-service-provider') testImplementation project(path : ':spring-security-saml2-service-provider', configuration : 'tests') - testImplementation project(path : ':spring-security-saml2-service-provider', configuration : 'opensaml4MainImplementation') testImplementation project(path : ':spring-security-web', configuration : 'tests') testImplementation "jakarta.inject:jakarta.inject-api" testImplementation "org.assertj:assertj-core" diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java index c980777e887..642638ad9c4 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurer.java @@ -19,8 +19,6 @@ import java.util.LinkedHashMap; import java.util.Map; -import org.opensaml.core.Version; - import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.context.ApplicationContext; import org.springframework.security.authentication.AuthenticationManager; @@ -33,7 +31,6 @@ import org.springframework.security.core.Authentication; import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest; import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider; -import org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter; @@ -43,7 +40,6 @@ import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository; import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationTokenConverter; -import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml3AuthenticationRequestResolver; import org.springframework.security.saml2.provider.service.web.authentication.OpenSaml4AuthenticationRequestResolver; import org.springframework.security.saml2.provider.service.web.authentication.Saml2AuthenticationRequestResolver; import org.springframework.security.web.AuthenticationEntryPoint; @@ -200,10 +196,6 @@ public Saml2LoginConfigurer authenticationRequestResolver( * @since 6.0 */ public Saml2LoginConfigurer authenticationRequestUri(String authenticationRequestUri) { - // OpenSAML 3 is no longer supported by spring security - if (version().startsWith("3")) { - return this; - } Assert.state(authenticationRequestUri.contains("{registrationId}"), "authenticationRequestUri must contain {registrationId} path variable"); this.authenticationRequestUri = authenticationRequestUri; @@ -345,14 +337,11 @@ private Saml2AuthenticationRequestResolver getAuthenticationRequestResolver(B ht if (bean != null) { return bean; } - if (version().startsWith("4")) { - OpenSaml4AuthenticationRequestResolver openSaml4AuthenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver( - relyingPartyRegistrationResolver(http)); - openSaml4AuthenticationRequestResolver - .setRequestMatcher(new AntPathRequestMatcher(this.authenticationRequestUri)); - return openSaml4AuthenticationRequestResolver; - } - return new OpenSaml3AuthenticationRequestResolver(relyingPartyRegistrationResolver(http)); + OpenSaml4AuthenticationRequestResolver openSaml4AuthenticationRequestResolver = new OpenSaml4AuthenticationRequestResolver( + relyingPartyRegistrationResolver(http)); + openSaml4AuthenticationRequestResolver + .setRequestMatcher(new AntPathRequestMatcher(this.authenticationRequestUri)); + return openSaml4AuthenticationRequestResolver; } private AuthenticationConverter getAuthenticationConverter(B http) { @@ -370,22 +359,8 @@ private AuthenticationConverter getAuthenticationConverter(B http) { return authenticationConverterBean; } - private String version() { - String version = Version.getVersion(); - if (version != null) { - return version; - } - return Version.class.getModule().getDescriptor().version().map(Object::toString) - .orElseThrow(() -> new IllegalStateException("cannot determine OpenSAML version")); - } - private void registerDefaultAuthenticationProvider(B http) { - if (version().startsWith("4")) { - http.authenticationProvider(postProcess(new OpenSaml4AuthenticationProvider())); - } - else { - http.authenticationProvider(postProcess(new OpenSamlAuthenticationProvider())); - } + http.authenticationProvider(postProcess(new OpenSaml4AuthenticationProvider())); } private void registerDefaultCsrfOverride(B http) { diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurer.java index 87d486d8a74..58e17716e93 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LogoutConfigurer.java @@ -22,8 +22,6 @@ import java.util.function.Predicate; import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; -import org.opensaml.core.Version; import org.springframework.context.ApplicationContext; import org.springframework.security.authentication.AuthenticationManager; @@ -44,8 +42,6 @@ import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.authentication.logout.HttpSessionLogoutRequestRepository; -import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml3LogoutRequestResolver; -import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml3LogoutResponseResolver; import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutRequestResolver; import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml4LogoutResponseResolver; import org.springframework.security.saml2.provider.service.web.authentication.logout.Saml2LogoutRequestFilter; @@ -313,15 +309,6 @@ private C getBeanOrNull(Class clazz) { return this.context.getBean(clazz); } - private String version() { - String version = Version.getVersion(); - if (version != null) { - return version; - } - return Version.class.getModule().getDescriptor().version().map(Object::toString) - .orElseThrow(() -> new IllegalStateException("cannot determine OpenSAML version")); - } - /** * A configurer for SAML 2.0 LogoutRequest components */ @@ -401,10 +388,7 @@ private Saml2LogoutRequestResolver logoutRequestResolver( if (this.logoutRequestResolver != null) { return this.logoutRequestResolver; } - if (version().startsWith("4")) { - return new OpenSaml4LogoutRequestResolver(relyingPartyRegistrationResolver); - } - return new OpenSaml3LogoutRequestResolver(relyingPartyRegistrationResolver); + return new OpenSaml4LogoutRequestResolver(relyingPartyRegistrationResolver); } } @@ -471,10 +455,7 @@ private Saml2LogoutResponseValidator logoutResponseValidator() { private Saml2LogoutResponseResolver logoutResponseResolver( RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) { if (this.logoutResponseResolver == null) { - if (version().startsWith("4")) { - return new OpenSaml4LogoutResponseResolver(relyingPartyRegistrationResolver); - } - return new OpenSaml3LogoutResponseResolver(relyingPartyRegistrationResolver); + return new OpenSaml4LogoutResponseResolver(relyingPartyRegistrationResolver); } return this.logoutResponseResolver; } @@ -511,12 +492,4 @@ public boolean matches(HttpServletRequest request) { } - private static class NoopLogoutHandler implements LogoutHandler { - - @Override - public void logout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { - } - - } - } diff --git a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java index a39473c1ec2..d49ac1bd0e1 100644 --- a/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java +++ b/config/src/test/java/org/springframework/security/config/annotation/web/configurers/saml2/Saml2LoginConfigurerTests.java @@ -18,9 +18,7 @@ import java.io.IOException; import java.net.URLDecoder; -import java.time.Duration; import java.util.Base64; -import java.util.Collection; import java.util.Collections; import jakarta.servlet.ServletException; @@ -32,7 +30,6 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; -import org.opensaml.saml.saml2.core.Assertion; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; @@ -40,18 +37,14 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.core.convert.converter.Converter; import org.springframework.http.MediaType; import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockHttpSession; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.authentication.AuthenticationServiceException; -import org.springframework.security.authentication.ProviderManager; import org.springframework.security.config.Customizer; -import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @@ -59,16 +52,12 @@ import org.springframework.security.config.test.SpringTestContextExtension; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; import org.springframework.security.saml2.core.Saml2ErrorCodes; import org.springframework.security.saml2.core.Saml2Utils; import org.springframework.security.saml2.core.TestSaml2X509Credentials; import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest; -import org.springframework.security.saml2.provider.service.authentication.OpenSaml4AuthenticationProvider; -import org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticatedPrincipal; import org.springframework.security.saml2.provider.service.authentication.Saml2Authentication; import org.springframework.security.saml2.provider.service.authentication.Saml2AuthenticationException; @@ -77,7 +66,6 @@ import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistrationRepository; import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations; -import org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter; import org.springframework.security.saml2.provider.service.web.DefaultRelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; import org.springframework.security.saml2.provider.service.web.Saml2AuthenticationRequestRepository; @@ -91,7 +79,6 @@ import org.springframework.security.web.context.HttpRequestResponseHolder; import org.springframework.security.web.context.HttpSessionSecurityContextRepository; import org.springframework.security.web.context.SecurityContextRepository; -import org.springframework.test.util.ReflectionTestUtils; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; @@ -121,14 +108,6 @@ @ExtendWith(SpringTestContextExtension.class) public class Saml2LoginConfigurerTests { - private static final Converter> AUTHORITIES_EXTRACTOR = ( - a) -> Collections.singletonList(new SimpleGrantedAuthority("TEST")); - - private static final GrantedAuthoritiesMapper AUTHORITIES_MAPPER = (authorities) -> Collections - .singletonList(new SimpleGrantedAuthority("TEST CONVERTED")); - - private static final Duration RESPONSE_TIME_VALIDATION_SKEW = Duration.ZERO; - private static final String SIGNED_RESPONSE = "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c2FtbDJwOlJlc3BvbnNlIHhtbG5zOnNhbWwycD0idXJuOm9hc2lzOm5hbWVzOnRjOlNBTUw6Mi4wOnByb3RvY29sIiBEZXN0aW5hdGlvbj0iaHR0cHM6Ly9ycC5leGFtcGxlLm9yZy9hY3MiIElEPSJfYzE3MzM2YTAtNTM1My00MTQ5LWI3MmMtMDNkOWY5YWYzMDdlIiBJc3N1ZUluc3RhbnQ9IjIwMjAtMDgtMDRUMjI6MDQ6NDUuMDE2WiIgVmVyc2lvbj0iMi4wIj48c2FtbDI6SXNzdWVyIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIj5hcC1lbnRpdHktaWQ8L3NhbWwyOklzc3Vlcj48ZHM6U2lnbmF0dXJlIHhtbG5zOmRzPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwLzA5L3htbGRzaWcjIj4KPGRzOlNpZ25lZEluZm8+CjxkczpDYW5vbmljYWxpemF0aW9uTWV0aG9kIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMS8xMC94bWwtZXhjLWMxNG4jIi8+CjxkczpTaWduYXR1cmVNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGRzaWctbW9yZSNyc2Etc2hhMjU2Ii8+CjxkczpSZWZlcmVuY2UgVVJJPSIjX2MxNzMzNmEwLTUzNTMtNDE0OS1iNzJjLTAzZDlmOWFmMzA3ZSI+CjxkczpUcmFuc2Zvcm1zPgo8ZHM6VHJhbnNmb3JtIEFsZ29yaXRobT0iaHR0cDovL3d3dy53My5vcmcvMjAwMC8wOS94bWxkc2lnI2VudmVsb3BlZC1zaWduYXR1cmUiLz4KPGRzOlRyYW5zZm9ybSBBbGdvcml0aG09Imh0dHA6Ly93d3cudzMub3JnLzIwMDEvMTAveG1sLWV4Yy1jMTRuIyIvPgo8L2RzOlRyYW5zZm9ybXM+CjxkczpEaWdlc3RNZXRob2QgQWxnb3JpdGhtPSJodHRwOi8vd3d3LnczLm9yZy8yMDAxLzA0L3htbGVuYyNzaGEyNTYiLz4KPGRzOkRpZ2VzdFZhbHVlPjYzTmlyenFzaDVVa0h1a3NuRWUrM0hWWU5aYWFsQW1OQXFMc1lGMlRuRDA9PC9kczpEaWdlc3RWYWx1ZT4KPC9kczpSZWZlcmVuY2U+CjwvZHM6U2lnbmVkSW5mbz4KPGRzOlNpZ25hdHVyZVZhbHVlPgpLMVlvWWJVUjBTclY4RTdVMkhxTTIvZUNTOTNoV25mOExnNnozeGZWMUlyalgzSXhWYkNvMVlYcnRBSGRwRVdvYTJKKzVOMmFNbFBHJiMxMzsKN2VpbDBZRC9xdUVRamRYbTNwQTBjZmEvY25pa2RuKzVhbnM0ZWQwanU1amo2dkpvZ2w2Smt4Q25LWUpwTU9HNzhtampmb0phengrWCYjMTM7CkM2NktQVStBYUdxeGVwUEQ1ZlhRdTFKSy9Jb3lBaitaa3k4Z2Jwc3VyZHFCSEJLRWxjdnVOWS92UGY0OGtBeFZBKzdtRGhNNUMvL1AmIzEzOwp0L084Y3NZYXB2UjZjdjZrdk45QXZ1N3FRdm9qVk1McHVxZWNJZDJwTUVYb0NSSnE2Nkd4MStNTUVPeHVpMWZZQlRoMEhhYjRmK3JyJiMxMzsKOEY2V1NFRC8xZllVeHliRkJqZ1Q4d2lEWHFBRU8wSVY4ZWRQeEE9PQo8L2RzOlNpZ25hdHVyZVZhbHVlPgo8L2RzOlNpZ25hdHVyZT48c2FtbDI6QXNzZXJ0aW9uIHhtbG5zOnNhbWwyPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6YXNzZXJ0aW9uIiBJRD0iQWUzZjQ5OGI4LTliMTctNDA3OC05ZDM1LTg2YTA4NDA4NDk5NSIgSXNzdWVJbnN0YW50PSIyMDIwLTA4LTA0VDIyOjA0OjQ1LjA3N1oiIFZlcnNpb249IjIuMCI+PHNhbWwyOklzc3Vlcj5hcC1lbnRpdHktaWQ8L3NhbWwyOklzc3Vlcj48c2FtbDI6U3ViamVjdD48c2FtbDI6TmFtZUlEPnRlc3RAc2FtbC51c2VyPC9zYW1sMjpOYW1lSUQ+PHNhbWwyOlN1YmplY3RDb25maXJtYXRpb24gTWV0aG9kPSJ1cm46b2FzaXM6bmFtZXM6dGM6U0FNTDoyLjA6Y206YmVhcmVyIj48c2FtbDI6U3ViamVjdENvbmZpcm1hdGlvbkRhdGEgTm90QmVmb3JlPSIyMDIwLTA4LTA0VDIxOjU5OjQ1LjA5MFoiIE5vdE9uT3JBZnRlcj0iMjA0MC0wNy0zMFQyMjowNTowNi4wODhaIiBSZWNpcGllbnQ9Imh0dHBzOi8vcnAuZXhhbXBsZS5vcmcvYWNzIi8+PC9zYW1sMjpTdWJqZWN0Q29uZmlybWF0aW9uPjwvc2FtbDI6U3ViamVjdD48c2FtbDI6Q29uZGl0aW9ucyBOb3RCZWZvcmU9IjIwMjAtMDgtMDRUMjE6NTk6NDUuMDgwWiIgTm90T25PckFmdGVyPSIyMDQwLTA3LTMwVDIyOjA1OjA2LjA4N1oiLz48L3NhbWwyOkFzc2VydGlvbj48L3NhbWwycDpSZXNwb25zZT4="; private static final AuthenticationConverter AUTHENTICATION_CONVERTER = mock(AuthenticationConverter.class); @@ -197,14 +176,6 @@ public void saml2LoginWhenDefaultAndSamlAuthenticationManagerThenSamlManagerIsUs performSaml2Login("ROLE_AUTH_MANAGER"); } - @Test - public void saml2LoginWhenConfiguringAuthenticationDefaultsUsingCustomizerThenTheProviderIsConfigured() - throws Exception { - // setup application context - this.spring.register(Saml2LoginConfigWithAuthenticationDefaultsWithPostProcessor.class).autowire(); - validateSaml2WebSsoAuthenticationFilterConfiguration(); - } - @Test public void authenticationRequestWhenAuthenticationRequestResolverBeanThenUses() throws Exception { this.spring.register(CustomAuthenticationRequestResolverBean.class).autowire(); @@ -362,22 +333,6 @@ public void getFaviconWhenDefaultConfigurationThenDoesNotSaveAuthnRequest() thro .andExpect(redirectedUrl("http://localhost/saml2/authenticate/registration-id")); } - private void validateSaml2WebSsoAuthenticationFilterConfiguration() { - // get the OpenSamlAuthenticationProvider - Saml2WebSsoAuthenticationFilter filter = getSaml2SsoFilter(this.springSecurityFilterChain); - AuthenticationManager manager = (AuthenticationManager) ReflectionTestUtils.getField(filter, - "authenticationManager"); - ProviderManager pm = (ProviderManager) manager; - AuthenticationProvider provider = pm.getProviders().stream() - .filter((p) -> p instanceof OpenSaml4AuthenticationProvider).findFirst().get(); - assertThat(provider).isNotNull(); - } - - private Saml2WebSsoAuthenticationFilter getSaml2SsoFilter(FilterChainProxy chain) { - return (Saml2WebSsoAuthenticationFilter) chain.getFilters("/login/saml2/sso/test").stream() - .filter((f) -> f instanceof Saml2WebSsoAuthenticationFilter).findFirst().get(); - } - private void performSaml2Login(String expected) throws IOException, ServletException { // setup authentication parameters this.request.setRequestURI("/login/saml2/sso/registration-id"); @@ -460,28 +415,6 @@ protected void configure(HttpSecurity http) throws Exception { } - @Configuration - @EnableWebSecurity - @Import(Saml2LoginConfigBeans.class) - static class Saml2LoginConfigWithAuthenticationDefaultsWithPostProcessor extends WebSecurityConfigurerAdapter { - - @Override - protected void configure(HttpSecurity http) throws Exception { - ObjectPostProcessor processor = new ObjectPostProcessor() { - @Override - public O postProcess(O provider) { - provider.setResponseTimeValidationSkew(RESPONSE_TIME_VALIDATION_SKEW); - provider.setAuthoritiesMapper(AUTHORITIES_MAPPER); - provider.setAuthoritiesExtractor(AUTHORITIES_EXTRACTOR); - return provider; - } - }; - http.saml2Login().addObjectPostProcessor(processor); - super.configure(http); - } - - } - @Configuration @EnableWebSecurity @Import(Saml2LoginConfigBeans.class) diff --git a/docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc b/docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc index 96cfa140756..0b5b0999d36 100644 --- a/docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc +++ b/docs/modules/ROOT/pages/servlet/saml2/login/overview.adoc @@ -154,14 +154,6 @@ Instead, such classes as `OpenSamlAuthenticationRequestFactory` and `OpenSamlAut For example, once your application receives a `SAMLResponse` and delegates to `Saml2WebSsoAuthenticationFilter`, the filter delegates to `OpenSamlAuthenticationProvider`: -[NOTE] -==== -For backward compatibility, Spring Security will use the latest OpenSAML 3 by default. -Note, though that OpenSAML 3 has reached it's end-of-life and updating to OpenSAML 4.x is recommended. -For that reason, Spring Security supports both OpenSAML 3.x and 4.x. -If you manage your OpenSAML dependency to 4.x, then Spring Security will select its OpenSAML 4.x implementations. -==== - .Authenticating an OpenSAML `Response` image:{figures}/opensamlauthenticationprovider.png[] diff --git a/docs/modules/ROOT/pages/whats-new.adoc b/docs/modules/ROOT/pages/whats-new.adoc index 5523c8bd8ba..ceff3efb9fc 100644 --- a/docs/modules/ROOT/pages/whats-new.adoc +++ b/docs/modules/ROOT/pages/whats-new.adoc @@ -6,6 +6,8 @@ Below are the highlights of the release. == Breaking Changes +* https://github.com/spring-projects/spring-security/issues/10556[gh-10556] - Remove EOL OpenSaml 3 Support. +Use the OpenSaml 4 Support instead. * https://github.com/spring-projects/spring-security/issues/8980[gh-8980] - Remove unsafe/deprecated `Encryptors.querableText(CharSequence,CharSequence)`. Instead use data storage to encrypt values. * https://github.com/spring-projects/spring-security/issues/11520[gh-11520] - Remember Me uses SHA256 by default diff --git a/gradle.properties b/gradle.properties index 9ae8803d893..a91d38f11a4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,7 @@ aspectjVersion=1.9.9.1 springJavaformatVersion=0.0.34 springBootVersion=2.4.2 springFrameworkVersion=6.0.0-M6 -openSamlVersion=3.4.6 +openSamlVersion=4.1.1 version=6.0.0-SNAPSHOT kotlinVersion=1.7.10 samplesBranch=main diff --git a/saml2/saml2-service-provider/spring-security-saml2-service-provider.gradle b/saml2/saml2-service-provider/spring-security-saml2-service-provider.gradle index 631a202da5b..e00593c1493 100644 --- a/saml2/saml2-service-provider/spring-security-saml2-service-provider.gradle +++ b/saml2/saml2-service-provider/spring-security-saml2-service-provider.gradle @@ -1,44 +1,4 @@ apply plugin: 'io.spring.convention.spring-module' -apply plugin: 'nebula.facet' - -facets { - opensaml3Main { - parentSourceSet = 'main' - } - opensaml4Main { - parentSourceSet = 'main' - } - opensaml3Test { - parentSourceSet = 'opensaml3Main' - } - opensaml4Test { - parentSourceSet = 'opensaml4Main' - } -} - -sourceSets { - opensaml3Test { - compileClasspath += sourceSets.test.output - runtimeClasspath += sourceSets.test.output - } - opensaml4Test { - compileClasspath += sourceSets.test.output - runtimeClasspath += sourceSets.test.output - } -} - -configurations { - opensaml3TestImplementation.extendsFrom testImplementation - opensaml4TestImplementation.extendsFrom testImplementation - opensaml4MainImplementation { - canBeConsumed = true - } -} - -compileOpensaml4MainJava { - sourceCompatibility = JavaVersion.VERSION_17 - targetCompatibility = JavaVersion.VERSION_17 -} dependencies { management platform(project(":spring-security-dependencies")) @@ -50,13 +10,6 @@ dependencies { api ("org.opensaml:opensaml-saml-impl") { exclude group: 'commons-logging', module: 'commons-logging' } - opensaml4MainImplementation "org.opensaml:opensaml-core:4.1.0" - opensaml4MainImplementation ("org.opensaml:opensaml-saml-api:4.1.0") { - exclude group: 'commons-logging', module: 'commons-logging' - } - opensaml4MainImplementation ("org.opensaml:opensaml-saml-impl:4.1.0") { - exclude group: 'commons-logging', module: 'commons-logging' - } provided 'jakarta.servlet:jakarta.servlet-api' @@ -73,35 +26,3 @@ dependencies { testImplementation "org.mockito:mockito-junit-jupiter" testImplementation "org.springframework:spring-test" } - -project.tasks.matching { t -> t.name == "jar"}.configureEach { - duplicatesStrategy = DuplicatesStrategy.EXCLUDE - from { - compileOpensaml3MainJava - } - from { - compileOpensaml4MainJava - } -} - -project.tasks.matching { t -> t.name == "sourcesJar"}.configureEach { - from { - sourceSets.opensaml3Main.allSource - } - from { - sourceSets.opensaml4Main.allSource - } -} - - -javadoc { - source += sourceSets.opensaml3Main.allJava + sourceSets.opensaml4Main.allJava -} - -opensaml3Test { - useJUnitPlatform() -} - -opensaml4Test { - useJUnitPlatform() -} diff --git a/saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java similarity index 99% rename from saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java rename to saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java index d4ac38f6840..5fef242e0ce 100644 --- a/saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java +++ b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProvider.java @@ -134,7 +134,7 @@ * @see SAML 2 * StatusResponse - * @see OpenSAML 3 + * @see OpenSAML */ public final class OpenSaml4AuthenticationProvider implements AuthenticationProvider { diff --git a/saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml4AuthenticationRequestResolver.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml4AuthenticationRequestResolver.java similarity index 100% rename from saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml4AuthenticationRequestResolver.java rename to saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml4AuthenticationRequestResolver.java diff --git a/saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutRequestResolver.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutRequestResolver.java similarity index 100% rename from saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutRequestResolver.java rename to saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutRequestResolver.java diff --git a/saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolver.java b/saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolver.java similarity index 100% rename from saml2/saml2-service-provider/src/opensaml4Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolver.java rename to saml2/saml2-service-provider/src/main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolver.java diff --git a/saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProvider.java b/saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProvider.java deleted file mode 100644 index 18ec5f77ebb..00000000000 --- a/saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProvider.java +++ /dev/null @@ -1,864 +0,0 @@ -/* - * Copyright 2002-2021 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.saml2.provider.service.authentication; - -import java.io.ByteArrayInputStream; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.time.Instant; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -import javax.annotation.Nonnull; -import javax.xml.namespace.QName; - -import net.shibboleth.utilities.java.support.xml.ParserPool; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.opensaml.core.config.ConfigurationService; -import org.opensaml.core.xml.XMLObject; -import org.opensaml.core.xml.config.XMLObjectProviderRegistry; -import org.opensaml.core.xml.schema.XSAny; -import org.opensaml.core.xml.schema.XSBoolean; -import org.opensaml.core.xml.schema.XSBooleanValue; -import org.opensaml.core.xml.schema.XSDateTime; -import org.opensaml.core.xml.schema.XSInteger; -import org.opensaml.core.xml.schema.XSString; -import org.opensaml.core.xml.schema.XSURI; -import org.opensaml.saml.common.assertion.AssertionValidationException; -import org.opensaml.saml.common.assertion.ValidationContext; -import org.opensaml.saml.common.assertion.ValidationResult; -import org.opensaml.saml.saml2.assertion.ConditionValidator; -import org.opensaml.saml.saml2.assertion.SAML20AssertionValidator; -import org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters; -import org.opensaml.saml.saml2.assertion.StatementValidator; -import org.opensaml.saml.saml2.assertion.SubjectConfirmationValidator; -import org.opensaml.saml.saml2.assertion.impl.AudienceRestrictionConditionValidator; -import org.opensaml.saml.saml2.assertion.impl.BearerSubjectConfirmationValidator; -import org.opensaml.saml.saml2.assertion.impl.DelegationRestrictionConditionValidator; -import org.opensaml.saml.saml2.core.Assertion; -import org.opensaml.saml.saml2.core.Attribute; -import org.opensaml.saml.saml2.core.AttributeStatement; -import org.opensaml.saml.saml2.core.Condition; -import org.opensaml.saml.saml2.core.EncryptedAssertion; -import org.opensaml.saml.saml2.core.OneTimeUse; -import org.opensaml.saml.saml2.core.Response; -import org.opensaml.saml.saml2.core.StatusCode; -import org.opensaml.saml.saml2.core.SubjectConfirmation; -import org.opensaml.saml.saml2.core.impl.ResponseUnmarshaller; -import org.opensaml.saml.saml2.encryption.Decrypter; -import org.opensaml.saml.security.impl.SAMLSignatureProfileValidator; -import org.opensaml.xmlsec.signature.support.SignaturePrevalidator; -import org.opensaml.xmlsec.signature.support.SignatureTrustEngine; -import org.w3c.dom.Document; -import org.w3c.dom.Element; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.core.log.LogMessage; -import org.springframework.security.authentication.AbstractAuthenticationToken; -import org.springframework.security.authentication.AuthenticationProvider; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.authority.mapping.GrantedAuthoritiesMapper; -import org.springframework.security.saml2.Saml2Exception; -import org.springframework.security.saml2.core.OpenSamlInitializationService; -import org.springframework.security.saml2.core.Saml2Error; -import org.springframework.security.saml2.core.Saml2ErrorCodes; -import org.springframework.security.saml2.core.Saml2ResponseValidatorResult; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.util.Assert; -import org.springframework.util.CollectionUtils; -import org.springframework.util.StringUtils; - -/** - * Implementation of {@link AuthenticationProvider} for SAML authentications when - * receiving a {@code Response} object containing an {@code Assertion}. This - * implementation uses the {@code OpenSAML 3} library. - * - *

- * The {@link OpenSamlAuthenticationProvider} supports {@link Saml2AuthenticationToken} - * objects that contain a SAML response in its decoded XML format - * {@link Saml2AuthenticationToken#getSaml2Response()} along with the information about - * the asserting party, the identity provider (IDP), as well as the relying party, the - * service provider (SP, this application). - *

- * The {@link Saml2AuthenticationToken} will be processed into a SAML Response object. The - * SAML response object can be signed. If the Response is signed, a signature will not be - * required on the assertion. - *

- * While a response object can contain a list of assertion, this provider will only - * leverage the first valid assertion for the purpose of authentication. Assertions that - * do not pass validation will be ignored. If no valid assertions are found a - * {@link Saml2AuthenticationException} is thrown. - *

- * This provider supports two types of encrypted SAML elements - *

- * If the assertion is encrypted, then signature validation on the assertion is no longer - * required. - *

- * This provider does not perform an X509 certificate validation on the configured - * asserting party, IDP, verification certificates. - * - * @author Ryan Cassar - * @since 5.2 - * @see SAML 2 - * StatusResponse - * @see OpenSAML 3 - * @deprecated Because OpenSAML 3 has reached End-of-Life, please update to - * {@code OpenSaml4AuthenticationProvider} - */ -public final class OpenSamlAuthenticationProvider implements AuthenticationProvider { - - static { - OpenSamlInitializationService.initialize(); - } - - private static Log logger = LogFactory.getLog(OpenSamlAuthenticationProvider.class); - - private final XMLObjectProviderRegistry registry; - - private final ResponseUnmarshaller responseUnmarshaller; - - private final ParserPool parserPool; - - private Converter> authoritiesExtractor = ((a) -> Collections - .singletonList(new SimpleGrantedAuthority("ROLE_USER"))); - - private GrantedAuthoritiesMapper authoritiesMapper = ((a) -> a); - - private Duration responseTimeValidationSkew = Duration.ofMinutes(5); - - private Converter responseSignatureValidator = createDefaultResponseSignatureValidator(); - - private Consumer responseElementsDecrypter = createDefaultResponseElementsDecrypter(); - - private Converter responseValidator = createDefaultResponseValidator(); - - private Converter assertionSignatureValidator = createDefaultAssertionSignatureValidator(); - - private Consumer assertionElementsDecrypter = createDefaultAssertionElementsDecrypter(); - - private Converter assertionValidator = createCompatibleAssertionValidator(); - - private Converter responseAuthenticationConverter = createCompatibleResponseAuthenticationConverter(); - - /** - * Creates an {@link OpenSamlAuthenticationProvider} - */ - public OpenSamlAuthenticationProvider() { - this.registry = ConfigurationService.get(XMLObjectProviderRegistry.class); - this.responseUnmarshaller = (ResponseUnmarshaller) this.registry.getUnmarshallerFactory() - .getUnmarshaller(Response.DEFAULT_ELEMENT_NAME); - this.parserPool = this.registry.getParserPool(); - } - - /** - * Set the {@link Consumer} strategy to use for decrypting elements of a validated - * {@link Response}. The default strategy decrypts all {@link EncryptedAssertion}s - * using OpenSAML's {@link Decrypter}, adding the results to - * {@link Response#getAssertions()}. - * - * You can use this method to configure the {@link Decrypter} instance like so: - * - *

-	 *	OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
-	 *	provider.setResponseElementsDecrypter((responseToken) -> {
-	 *	    DecrypterParameters parameters = new DecrypterParameters();
-	 *	    // ... set parameters as needed
-	 *	    Decrypter decrypter = new Decrypter(parameters);
-	 *		Response response = responseToken.getResponse();
-	 *  	EncryptedAssertion encrypted = response.getEncryptedAssertions().get(0);
-	 *  	try {
-	 *  		Assertion assertion = decrypter.decrypt(encrypted);
-	 *  		response.getAssertions().add(assertion);
-	 *  	} catch (Exception e) {
-	 *  	 	throw new Saml2AuthenticationException(...);
-	 *  	}
-	 *	});
-	 * 
- * - * Or, in the event that you have your own custom decryption interface, the same - * pattern applies: - * - *
-	 *	OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
-	 *	Converter<EncryptedAssertion, Assertion> myService = ...
-	 *	provider.setResponseDecrypter((responseToken) -> {
-	 *	   Response response = responseToken.getResponse();
-	 *	   response.getEncryptedAssertions().stream()
-	 *	   		.map(service::decrypt).forEach(response.getAssertions()::add);
-	 *	});
-	 * 
- * - * This is valuable when using an external service to perform the decryption. - * @param responseElementsDecrypter the {@link Consumer} for decrypting response - * elements - * @since 5.5 - */ - public void setResponseElementsDecrypter(Consumer responseElementsDecrypter) { - Assert.notNull(responseElementsDecrypter, "responseElementsDecrypter cannot be null"); - this.responseElementsDecrypter = responseElementsDecrypter; - } - - /** - * Set the {@link Converter} to use for validating each {@link Assertion} in the SAML - * 2.0 Response. - * - * You can still invoke the default validator by delgating to - * {@link #createAssertionValidator}, like so: - * - *
-	 *	OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
-	 *  provider.setAssertionValidator(assertionToken -> {
-	 *		Saml2ResponseValidatorResult result = createDefaultAssertionValidator()
-	 *			.convert(assertionToken)
-	 *		return result.concat(myCustomValidator.convert(assertionToken));
-	 *  });
-	 * 
- * - * You can also use this method to configure the provider to use a different - * {@link ValidationContext} from the default, like so: - * - *
-	 *	OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
-	 *	provider.setAssertionValidator(
-	 *		createDefaultAssertionValidator(assertionToken -> {
-	 *			Map<String, Object> params = new HashMap<>();
-	 *			params.put(CLOCK_SKEW, 2 * 60 * 1000);
-	 *			// other parameters
-	 *			return new ValidationContext(params);
-	 *		}));
-	 * 
- * - * Consider taking a look at {@link #createValidationContext} to see how it constructs - * a {@link ValidationContext}. - * - * It is not necessary to delegate to the default validator. You can safely replace it - * entirely with your own. Note that signature verification is performed as a separate - * step from this validator. - * - * This method takes precedence over {@link #setResponseTimeValidationSkew}. - * @param assertionValidator the strategy for validating a given assertion - * @since 5.4 - */ - public void setAssertionValidator(Converter assertionValidator) { - Assert.notNull(assertionValidator, "assertionValidator cannot be null"); - this.assertionValidator = assertionValidator; - } - - /** - * Set the {@link Consumer} strategy to use for decrypting elements of a validated - * {@link Assertion}. - * - * You can use this method to configure the {@link Decrypter} used like so: - * - *
-	 *	OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
-	 *	provider.setResponseDecrypter((assertionToken) -> {
-	 *	    DecrypterParameters parameters = new DecrypterParameters();
-	 *	    // ... set parameters as needed
-	 *	    Decrypter decrypter = new Decrypter(parameters);
-	 *		Assertion assertion = assertionToken.getAssertion();
-	 *  	EncryptedID encrypted = assertion.getSubject().getEncryptedID();
-	 *  	try {
-	 *  		NameID name = decrypter.decrypt(encrypted);
-	 *  		assertion.getSubject().setNameID(name);
-	 *  	} catch (Exception e) {
-	 *  	 	throw new Saml2AuthenticationException(...);
-	 *  	}
-	 *	});
-	 * 
- * - * Or, in the event that you have your own custom interface, the same pattern applies: - * - *
-	 *	OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
-	 *	MyDecryptionService myService = ...
-	 *	provider.setResponseDecrypter((responseToken) -> {
-	 *	   	Assertion assertion = assertionToken.getAssertion();
-	 *	   	EncryptedID encrypted = assertion.getSubject().getEncryptedID();
-	 *		NameID name = myService.decrypt(encrypted);
-	 *		assertion.getSubject().setNameID(name);
-	 *	});
-	 * 
- * @param assertionDecrypter the {@link Consumer} for decrypting assertion elements - * @since 5.5 - */ - public void setAssertionElementsDecrypter(Consumer assertionDecrypter) { - Assert.notNull(assertionDecrypter, "assertionDecrypter cannot be null"); - this.assertionElementsDecrypter = assertionDecrypter; - } - - /** - * Set the {@link Converter} to use for converting a validated {@link Response} into - * an {@link AbstractAuthenticationToken}. - * - * You can delegate to the default behavior by calling - * {@link #createDefaultResponseAuthenticationConverter()} like so: - * - *
-	 *	OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider();
-	 * 	Converter<ResponseToken, Saml2Authentication> authenticationConverter =
-	 * 			createDefaultResponseAuthenticationConverter();
-	 *	provider.setResponseAuthenticationConverter(responseToken -> {
-	 *		Saml2Authentication authentication = authenticationConverter.convert(responseToken);
-	 *		User user = myUserRepository.findByUsername(authentication.getName());
-	 *		return new MyAuthentication(authentication, user);
-	 *	});
-	 * 
- * - * This method takes precedence over {@link #setAuthoritiesExtractor(Converter)} and - * {@link #setAuthoritiesMapper(GrantedAuthoritiesMapper)}. - * @param responseAuthenticationConverter the {@link Converter} to use - * @since 5.4 - */ - public void setResponseAuthenticationConverter( - Converter responseAuthenticationConverter) { - Assert.notNull(responseAuthenticationConverter, "responseAuthenticationConverter cannot be null"); - this.responseAuthenticationConverter = responseAuthenticationConverter; - } - - /** - * Sets the {@link Converter} used for extracting assertion attributes that can be - * mapped to authorities. - * @param authoritiesExtractor the {@code Converter} used for mapping the assertion - * attributes to authorities - * @deprecated Use {@link #setResponseAuthenticationConverter(Converter)} instead - */ - public void setAuthoritiesExtractor( - Converter> authoritiesExtractor) { - Assert.notNull(authoritiesExtractor, "authoritiesExtractor cannot be null"); - this.authoritiesExtractor = authoritiesExtractor; - } - - /** - * Sets the {@link GrantedAuthoritiesMapper} used for mapping assertion attributes to - * a new set of authorities which will be associated to the - * {@link Saml2Authentication}. Note: This implementation is only retrieving - * @param authoritiesMapper the {@link GrantedAuthoritiesMapper} used for mapping the - * user's authorities - * @deprecated Use {@link #setResponseAuthenticationConverter(Converter)} instead - */ - public void setAuthoritiesMapper(GrantedAuthoritiesMapper authoritiesMapper) { - Assert.notNull(authoritiesMapper, "authoritiesMapper cannot be null"); - this.authoritiesMapper = authoritiesMapper; - } - - /** - * Sets the duration for how much time skew an assertion may tolerate during - * timestamp, NotOnOrBefore and NotOnOrAfter, validation. - * @param responseTimeValidationSkew duration for skew tolerance - * @deprecated Use {@link #setAssertionValidator(Converter)} instead - */ - public void setResponseTimeValidationSkew(Duration responseTimeValidationSkew) { - this.responseTimeValidationSkew = responseTimeValidationSkew; - } - - /** - * Construct a default strategy for validating each SAML 2.0 Assertion and associated - * {@link Authentication} token - * @return the default assertion validator strategy - * @since 5.4 - */ - public static Converter createDefaultAssertionValidator() { - - return createAssertionValidator(Saml2ErrorCodes.INVALID_ASSERTION, - (assertionToken) -> SAML20AssertionValidators.attributeValidator, - (assertionToken) -> createValidationContext(assertionToken, (params) -> { - })); - } - - /** - * Construct a default strategy for validating each SAML 2.0 Assertion and associated - * {@link Authentication} token - * @param contextConverter the conversion strategy to use to generate a - * {@link ValidationContext} for each assertion being validated - * @return the default assertion validator strategy - * @since 5.4 - */ - public static Converter createDefaultAssertionValidator( - Converter contextConverter) { - - return createAssertionValidator(Saml2ErrorCodes.INVALID_ASSERTION, - (assertionToken) -> SAML20AssertionValidators.attributeValidator, contextConverter); - } - - /** - * Construct a default strategy for converting a SAML 2.0 Response and - * {@link Authentication} token into a {@link Saml2Authentication} - * @return the default response authentication converter strategy - * @since 5.4 - */ - public static Converter createDefaultResponseAuthenticationConverter() { - return (responseToken) -> { - Saml2AuthenticationToken token = responseToken.token; - Response response = responseToken.response; - Assertion assertion = CollectionUtils.firstElement(response.getAssertions()); - String username = assertion.getSubject().getNameID().getValue(); - Map> attributes = getAssertionAttributes(assertion); - DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, attributes); - String registrationId = responseToken.token.getRelyingPartyRegistration().getRegistrationId(); - principal.setRelyingPartyRegistrationId(registrationId); - return new Saml2Authentication(principal, token.getSaml2Response(), - Collections.singleton(new SimpleGrantedAuthority("ROLE_USER"))); - }; - } - - /** - * @param authentication the authentication request object, must be of type - * {@link Saml2AuthenticationToken} - * @return {@link Saml2Authentication} if the assertion is valid - * @throws AuthenticationException if a validation exception occurs - */ - @Override - public Authentication authenticate(Authentication authentication) throws AuthenticationException { - try { - Saml2AuthenticationToken token = (Saml2AuthenticationToken) authentication; - String serializedResponse = token.getSaml2Response(); - Response response = parse(serializedResponse); - process(token, response); - return this.responseAuthenticationConverter.convert(new ResponseToken(response, token)); - } - catch (Saml2AuthenticationException ex) { - throw ex; - } - catch (Exception ex) { - throw createAuthenticationException(Saml2ErrorCodes.INTERNAL_VALIDATION_ERROR, ex.getMessage(), ex); - } - } - - @Override - public boolean supports(Class authentication) { - return authentication != null && Saml2AuthenticationToken.class.isAssignableFrom(authentication); - } - - private Response parse(String response) throws Saml2Exception, Saml2AuthenticationException { - try { - Document document = this.parserPool - .parse(new ByteArrayInputStream(response.getBytes(StandardCharsets.UTF_8))); - Element element = document.getDocumentElement(); - return (Response) this.responseUnmarshaller.unmarshall(element); - } - catch (Exception ex) { - throw createAuthenticationException(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA, ex.getMessage(), ex); - } - } - - private void process(Saml2AuthenticationToken token, Response response) { - String issuer = response.getIssuer().getValue(); - logger.debug(LogMessage.format("Processing SAML response from %s", issuer)); - boolean responseSigned = response.isSigned(); - - ResponseToken responseToken = new ResponseToken(response, token); - Saml2ResponseValidatorResult result = this.responseSignatureValidator.convert(responseToken); - if (responseSigned) { - this.responseElementsDecrypter.accept(responseToken); - } - result = result.concat(this.responseValidator.convert(responseToken)); - boolean allAssertionsSigned = true; - for (Assertion assertion : response.getAssertions()) { - AssertionToken assertionToken = new AssertionToken(assertion, token); - result = result.concat(this.assertionSignatureValidator.convert(assertionToken)); - allAssertionsSigned = allAssertionsSigned && assertion.isSigned(); - if (responseSigned || assertion.isSigned()) { - this.assertionElementsDecrypter.accept(new AssertionToken(assertion, token)); - } - result = result.concat(this.assertionValidator.convert(assertionToken)); - } - if (!responseSigned && !allAssertionsSigned) { - String description = "Either the response or one of the assertions is unsigned. " - + "Please either sign the response or all of the assertions."; - throw createAuthenticationException(Saml2ErrorCodes.INVALID_SIGNATURE, description, null); - } - Assertion firstAssertion = CollectionUtils.firstElement(response.getAssertions()); - if (!hasName(firstAssertion)) { - Saml2Error error = new Saml2Error(Saml2ErrorCodes.SUBJECT_NOT_FOUND, - "Assertion [" + firstAssertion.getID() + "] is missing a subject"); - result = result.concat(error); - } - - if (result.hasErrors()) { - Collection errors = result.getErrors(); - if (logger.isTraceEnabled()) { - logger.debug("Found " + errors.size() + " validation errors in SAML response [" + response.getID() - + "]: " + errors); - } - else if (logger.isDebugEnabled()) { - logger.debug( - "Found " + errors.size() + " validation errors in SAML response [" + response.getID() + "]"); - } - Saml2Error first = errors.iterator().next(); - throw createAuthenticationException(first.getErrorCode(), first.getDescription(), null); - } - else { - if (logger.isDebugEnabled()) { - logger.debug("Successfully processed SAML Response [" + response.getID() + "]"); - } - } - } - - private Converter createDefaultResponseSignatureValidator() { - return (responseToken) -> { - Response response = responseToken.getResponse(); - RelyingPartyRegistration registration = responseToken.getToken().getRelyingPartyRegistration(); - if (response.isSigned()) { - return OpenSamlVerificationUtils.verifySignature(response, registration).post(response.getSignature()); - } - return Saml2ResponseValidatorResult.success(); - }; - } - - private Consumer createDefaultResponseElementsDecrypter() { - return (responseToken) -> { - Response response = responseToken.getResponse(); - RelyingPartyRegistration registration = responseToken.getToken().getRelyingPartyRegistration(); - try { - OpenSamlDecryptionUtils.decryptResponseElements(response, registration); - } - catch (Saml2Exception ex) { - throw createAuthenticationException(Saml2ErrorCodes.DECRYPTION_ERROR, ex.getMessage(), ex); - } - }; - } - - private Converter createDefaultResponseValidator() { - return (responseToken) -> { - Response response = responseToken.getResponse(); - Saml2AuthenticationToken token = responseToken.getToken(); - Saml2ResponseValidatorResult result = Saml2ResponseValidatorResult.success(); - String statusCode = getStatusCode(response); - if (!StatusCode.SUCCESS.equals(statusCode)) { - String message = String.format("Invalid status [%s] for SAML response [%s]", statusCode, - response.getID()); - result = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_RESPONSE, message)); - } - String issuer = response.getIssuer().getValue(); - String destination = response.getDestination(); - String location = token.getRelyingPartyRegistration().getAssertionConsumerServiceLocation(); - if (StringUtils.hasText(destination) && !destination.equals(location)) { - String message = "Invalid destination [" + destination + "] for SAML response [" + response.getID() - + "]"; - result = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_DESTINATION, message)); - } - String assertingPartyEntityId = token.getRelyingPartyRegistration().getAssertingPartyDetails() - .getEntityId(); - if (!StringUtils.hasText(issuer) || !issuer.equals(assertingPartyEntityId)) { - String message = String.format("Invalid issuer [%s] for SAML response [%s]", issuer, response.getID()); - result = result.concat(new Saml2Error(Saml2ErrorCodes.INVALID_ISSUER, message)); - } - if (response.getAssertions().isEmpty()) { - throw createAuthenticationException(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA, - "No assertions found in response.", null); - } - return result; - }; - } - - private String getStatusCode(Response response) { - if (response.getStatus() == null) { - return StatusCode.SUCCESS; - } - if (response.getStatus().getStatusCode() == null) { - return StatusCode.SUCCESS; - } - return response.getStatus().getStatusCode().getValue(); - } - - private Converter createDefaultAssertionSignatureValidator() { - return createAssertionValidator(Saml2ErrorCodes.INVALID_SIGNATURE, (assertionToken) -> { - RelyingPartyRegistration registration = assertionToken.getToken().getRelyingPartyRegistration(); - SignatureTrustEngine engine = OpenSamlVerificationUtils.trustEngine(registration); - return SAML20AssertionValidators.createSignatureValidator(engine); - }, (assertionToken) -> new ValidationContext( - Collections.singletonMap(SAML2AssertionValidationParameters.SIGNATURE_REQUIRED, false))); - } - - private Consumer createDefaultAssertionElementsDecrypter() { - return (assertionToken) -> { - Assertion assertion = assertionToken.getAssertion(); - RelyingPartyRegistration registration = assertionToken.getToken().getRelyingPartyRegistration(); - try { - OpenSamlDecryptionUtils.decryptAssertionElements(assertion, registration); - } - catch (Saml2Exception ex) { - throw createAuthenticationException(Saml2ErrorCodes.DECRYPTION_ERROR, ex.getMessage(), ex); - } - }; - } - - private Converter createCompatibleAssertionValidator() { - return createAssertionValidator(Saml2ErrorCodes.INVALID_ASSERTION, - (assertionToken) -> SAML20AssertionValidators.attributeValidator, - (assertionToken) -> createValidationContext(assertionToken, - (params) -> params.put(SAML2AssertionValidationParameters.CLOCK_SKEW, - this.responseTimeValidationSkew.toMillis()))); - } - - private Converter createCompatibleResponseAuthenticationConverter() { - return (responseToken) -> { - Response response = responseToken.response; - Saml2AuthenticationToken token = responseToken.token; - Assertion assertion = CollectionUtils.firstElement(response.getAssertions()); - String username = assertion.getSubject().getNameID().getValue(); - Map> attributes = getAssertionAttributes(assertion); - DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal(username, attributes); - String registrationId = responseToken.token.getRelyingPartyRegistration().getRegistrationId(); - principal.setRelyingPartyRegistrationId(registrationId); - return new Saml2Authentication(principal, token.getSaml2Response(), - this.authoritiesMapper.mapAuthorities(getAssertionAuthorities(assertion))); - }; - } - - private Collection getAssertionAuthorities(Assertion assertion) { - return this.authoritiesExtractor.convert(assertion); - } - - private boolean hasName(Assertion assertion) { - if (assertion == null) { - return false; - } - if (assertion.getSubject() == null) { - return false; - } - if (assertion.getSubject().getNameID() == null) { - return false; - } - return assertion.getSubject().getNameID().getValue() != null; - } - - private static Map> getAssertionAttributes(Assertion assertion) { - Map> attributeMap = new LinkedHashMap<>(); - for (AttributeStatement attributeStatement : assertion.getAttributeStatements()) { - for (Attribute attribute : attributeStatement.getAttributes()) { - List attributeValues = new ArrayList<>(); - for (XMLObject xmlObject : attribute.getAttributeValues()) { - Object attributeValue = getXmlObjectValue(xmlObject); - if (attributeValue != null) { - attributeValues.add(attributeValue); - } - } - attributeMap.put(attribute.getName(), attributeValues); - } - } - return attributeMap; - } - - private static Object getXmlObjectValue(XMLObject xmlObject) { - if (xmlObject instanceof XSAny) { - return ((XSAny) xmlObject).getTextContent(); - } - if (xmlObject instanceof XSString) { - return ((XSString) xmlObject).getValue(); - } - if (xmlObject instanceof XSInteger) { - return ((XSInteger) xmlObject).getValue(); - } - if (xmlObject instanceof XSURI) { - return ((XSURI) xmlObject).getValue(); - } - if (xmlObject instanceof XSBoolean) { - XSBooleanValue xsBooleanValue = ((XSBoolean) xmlObject).getValue(); - return (xsBooleanValue != null) ? xsBooleanValue.getValue() : null; - } - if (xmlObject instanceof XSDateTime) { - return Instant.ofEpochMilli(((XSDateTime) xmlObject).getValue().getMillis()); - } - return null; - } - - private static Saml2AuthenticationException createAuthenticationException(String code, String message, - Exception cause) { - return new Saml2AuthenticationException(new Saml2Error(code, message), cause); - } - - private static Converter createAssertionValidator(String errorCode, - Converter validatorConverter, - Converter contextConverter) { - - return (assertionToken) -> { - Assertion assertion = assertionToken.assertion; - SAML20AssertionValidator validator = validatorConverter.convert(assertionToken); - ValidationContext context = contextConverter.convert(assertionToken); - try { - ValidationResult result = validator.validate(assertion, context); - if (result == ValidationResult.VALID) { - return Saml2ResponseValidatorResult.success(); - } - } - catch (Exception ex) { - String message = String.format("Invalid assertion [%s] for SAML response [%s]: %s", assertion.getID(), - ((Response) assertion.getParent()).getID(), ex.getMessage()); - return Saml2ResponseValidatorResult.failure(new Saml2Error(errorCode, message)); - } - String message = String.format("Invalid assertion [%s] for SAML response [%s]: %s", assertion.getID(), - ((Response) assertion.getParent()).getID(), context.getValidationFailureMessage()); - return Saml2ResponseValidatorResult.failure(new Saml2Error(errorCode, message)); - }; - } - - private static ValidationContext createValidationContext(AssertionToken assertionToken, - Consumer> paramsConsumer) { - String audience = assertionToken.token.getRelyingPartyRegistration().getEntityId(); - String recipient = assertionToken.token.getRelyingPartyRegistration().getAssertionConsumerServiceLocation(); - Map params = new HashMap<>(); - params.put(SAML2AssertionValidationParameters.COND_VALID_AUDIENCES, Collections.singleton(audience)); - params.put(SAML2AssertionValidationParameters.SC_VALID_RECIPIENTS, Collections.singleton(recipient)); - paramsConsumer.accept(params); - return new ValidationContext(params); - } - - private static class SAML20AssertionValidators { - - private static final Collection conditions = new ArrayList<>(); - - private static final Collection subjects = new ArrayList<>(); - - private static final Collection statements = new ArrayList<>(); - - private static final SignaturePrevalidator validator = new SAMLSignatureProfileValidator(); - - static { - conditions.add(new AudienceRestrictionConditionValidator()); - conditions.add(new DelegationRestrictionConditionValidator()); - conditions.add(new ConditionValidator() { - @Nonnull - @Override - public QName getServicedCondition() { - return OneTimeUse.DEFAULT_ELEMENT_NAME; - } - - @Nonnull - @Override - public ValidationResult validate(Condition condition, Assertion assertion, ValidationContext context) { - // applications should validate their own OneTimeUse conditions - return ValidationResult.VALID; - } - }); - subjects.add(new BearerSubjectConfirmationValidator() { - @Override - protected ValidationResult validateAddress(SubjectConfirmation confirmation, Assertion assertion, - ValidationContext context) throws AssertionValidationException { - // applications should validate their own addresses - gh-7514 - return ValidationResult.VALID; - } - }); - } - - private static final SAML20AssertionValidator attributeValidator = new SAML20AssertionValidator(conditions, - subjects, statements, null, null) { - @Nonnull - @Override - protected ValidationResult validateSignature(Assertion token, ValidationContext context) { - return ValidationResult.VALID; - } - }; - - static SAML20AssertionValidator createSignatureValidator(SignatureTrustEngine engine) { - return new SAML20AssertionValidator(new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), engine, - validator) { - @Nonnull - @Override - protected ValidationResult validateConditions(Assertion assertion, ValidationContext context) { - return ValidationResult.VALID; - } - - @Nonnull - @Override - protected ValidationResult validateSubjectConfirmation(Assertion assertion, ValidationContext context) { - return ValidationResult.VALID; - } - - @Nonnull - @Override - protected ValidationResult validateStatements(Assertion assertion, ValidationContext context) { - return ValidationResult.VALID; - } - }; - - } - - } - - /** - * A tuple containing an OpenSAML {@link Response} and its associated authentication - * token. - * - * @since 5.4 - */ - public static class ResponseToken { - - private final Saml2AuthenticationToken token; - - private final Response response; - - ResponseToken(Response response, Saml2AuthenticationToken token) { - this.token = token; - this.response = response; - } - - public Response getResponse() { - return this.response; - } - - public Saml2AuthenticationToken getToken() { - return this.token; - } - - } - - /** - * A tuple containing an OpenSAML {@link Assertion} and its associated authentication - * token. - * - * @since 5.4 - */ - public static class AssertionToken { - - private final Saml2AuthenticationToken token; - - private final Assertion assertion; - - AssertionToken(Assertion assertion, Saml2AuthenticationToken token) { - this.token = token; - this.assertion = assertion; - } - - public Assertion getAssertion() { - return this.assertion; - } - - public Saml2AuthenticationToken getToken() { - return this.token; - } - - } - -} diff --git a/saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml3AuthenticationRequestResolver.java b/saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml3AuthenticationRequestResolver.java deleted file mode 100644 index b3e67e97bb5..00000000000 --- a/saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml3AuthenticationRequestResolver.java +++ /dev/null @@ -1,112 +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.saml2.provider.service.web.authentication; - -import java.time.Clock; -import java.util.function.Consumer; - -import jakarta.servlet.http.HttpServletRequest; -import org.joda.time.DateTime; -import org.opensaml.saml.saml2.core.AuthnRequest; -import org.opensaml.saml.saml2.core.LogoutRequest; - -import org.springframework.security.saml2.provider.service.authentication.AbstractSaml2AuthenticationRequest; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; -import org.springframework.util.Assert; - -/** - * A strategy for resolving a SAML 2.0 Authentication Request from the - * {@link HttpServletRequest} using OpenSAML. - * - * @author Josh Cummings - * @since 5.7 - * @deprecated OpenSAML 3 has reached end-of-life so this version is no longer recommended - */ -@Deprecated -public final class OpenSaml3AuthenticationRequestResolver implements Saml2AuthenticationRequestResolver { - - private final OpenSamlAuthenticationRequestResolver authnRequestResolver; - - private Consumer contextConsumer = (parameters) -> { - }; - - private Clock clock = Clock.systemUTC(); - - /** - * Construct a {@link OpenSaml3AuthenticationRequestResolver} - */ - public OpenSaml3AuthenticationRequestResolver(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) { - this.authnRequestResolver = new OpenSamlAuthenticationRequestResolver(relyingPartyRegistrationResolver); - } - - @Override - public T resolve(HttpServletRequest request) { - return this.authnRequestResolver.resolve(request, (registration, authnRequest) -> { - authnRequest.setIssueInstant(new DateTime(this.clock.millis())); - this.contextConsumer.accept(new AuthnRequestContext(request, registration, authnRequest)); - }); - } - - /** - * Set a {@link Consumer} for modifying the OpenSAML {@link LogoutRequest} - * @param contextConsumer a consumer that accepts an {@link AuthnRequestContext} - */ - public void setAuthnRequestCustomizer(Consumer contextConsumer) { - Assert.notNull(contextConsumer, "contextConsumer cannot be null"); - this.contextConsumer = contextConsumer; - } - - /** - * Use this {@link Clock} for generating the issued {@link DateTime} - * @param clock the {@link Clock} to use - */ - public void setClock(Clock clock) { - Assert.notNull(clock, "clock must not be null"); - this.clock = clock; - } - - public static final class AuthnRequestContext { - - private final HttpServletRequest request; - - private final RelyingPartyRegistration registration; - - private final AuthnRequest authnRequest; - - public AuthnRequestContext(HttpServletRequest request, RelyingPartyRegistration registration, - AuthnRequest authnRequest) { - this.request = request; - this.registration = registration; - this.authnRequest = authnRequest; - } - - public HttpServletRequest getRequest() { - return this.request; - } - - public RelyingPartyRegistration getRelyingPartyRegistration() { - return this.registration; - } - - public AuthnRequest getAuthnRequest() { - return this.authnRequest; - } - - } - -} diff --git a/saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutRequestResolver.java b/saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutRequestResolver.java deleted file mode 100644 index db7a4771f8e..00000000000 --- a/saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutRequestResolver.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright 2002-2021 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.saml2.provider.service.web.authentication.logout; - -import java.time.Clock; -import java.util.function.Consumer; - -import jakarta.servlet.http.HttpServletRequest; -import org.joda.time.DateTime; -import org.opensaml.saml.saml2.core.LogoutRequest; - -import org.springframework.security.core.Authentication; -import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; -import org.springframework.util.Assert; - -/** - * A {@link Saml2LogoutRequestResolver} for resolving SAML 2.0 Logout Requests with - * OpenSAML 3 - * - * @author Josh Cummings - * @since 5.6 - * @deprecated Because OpenSAML 3 has reached End-of-Life, please update to - * {@code OpenSaml4LogoutRequestResolver} - */ -public final class OpenSaml3LogoutRequestResolver implements Saml2LogoutRequestResolver { - - private final OpenSamlLogoutRequestResolver logoutRequestResolver; - - private Consumer parametersConsumer = (parameters) -> { - }; - - private Clock clock = Clock.systemUTC(); - - /** - * Construct a {@link OpenSaml3LogoutRequestResolver} - */ - public OpenSaml3LogoutRequestResolver(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) { - this.logoutRequestResolver = new OpenSamlLogoutRequestResolver(relyingPartyRegistrationResolver); - } - - /** - * {@inheritDoc} - */ - @Override - public Saml2LogoutRequest resolve(HttpServletRequest request, Authentication authentication) { - return this.logoutRequestResolver.resolve(request, authentication, (registration, logoutRequest) -> { - logoutRequest.setIssueInstant(new DateTime(this.clock.millis())); - this.parametersConsumer - .accept(new LogoutRequestParameters(request, registration, authentication, logoutRequest)); - }); - } - - /** - * Set a {@link Consumer} for modifying the OpenSAML {@link LogoutRequest} - * @param parametersConsumer a consumer that accepts an - * {@link LogoutRequestParameters} - */ - public void setParametersConsumer(Consumer parametersConsumer) { - Assert.notNull(parametersConsumer, "parametersConsumer cannot be null"); - this.parametersConsumer = parametersConsumer; - } - - /** - * Use this {@link Clock} for generating the issued {@link DateTime} - * @param clock the {@link Clock} to use - */ - public void setClock(Clock clock) { - Assert.notNull(clock, "clock must not be null"); - this.clock = clock; - } - - public static final class LogoutRequestParameters { - - private final HttpServletRequest request; - - private final RelyingPartyRegistration registration; - - private final Authentication authentication; - - private final LogoutRequest logoutRequest; - - public LogoutRequestParameters(HttpServletRequest request, RelyingPartyRegistration registration, - Authentication authentication, LogoutRequest logoutRequest) { - this.request = request; - this.registration = registration; - this.authentication = authentication; - this.logoutRequest = logoutRequest; - } - - public HttpServletRequest getRequest() { - return this.request; - } - - public RelyingPartyRegistration getRelyingPartyRegistration() { - return this.registration; - } - - public Authentication getAuthentication() { - return this.authentication; - } - - public LogoutRequest getLogoutRequest() { - return this.logoutRequest; - } - - } - -} diff --git a/saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutResponseResolver.java b/saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutResponseResolver.java deleted file mode 100644 index f5071ae2144..00000000000 --- a/saml2/saml2-service-provider/src/opensaml3Main/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutResponseResolver.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2002-2021 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.saml2.provider.service.web.authentication.logout; - -import java.time.Clock; -import java.util.function.Consumer; - -import jakarta.servlet.http.HttpServletRequest; -import org.joda.time.DateTime; -import org.opensaml.saml.saml2.core.LogoutResponse; - -import org.springframework.security.core.Authentication; -import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; -import org.springframework.util.Assert; - -/** - * A {@link Saml2LogoutResponseResolver} for resolving SAML 2.0 Logout Responses with - * OpenSAML 3 - * - * @author Josh Cummings - * @since 5.6 - * @deprecated Because OpenSAML 3 has reached End-of-Life, please update to - * {@code OpenSaml4LogoutResponseResolver} - */ -public final class OpenSaml3LogoutResponseResolver implements Saml2LogoutResponseResolver { - - private final OpenSamlLogoutResponseResolver logoutResponseResolver; - - private Consumer parametersConsumer = (parameters) -> { - }; - - private Clock clock = Clock.systemUTC(); - - /** - * Construct a {@link OpenSaml3LogoutResponseResolver} - */ - public OpenSaml3LogoutResponseResolver(RelyingPartyRegistrationResolver relyingPartyRegistrationResolver) { - this.logoutResponseResolver = new OpenSamlLogoutResponseResolver(relyingPartyRegistrationResolver); - } - - /** - * {@inheritDoc} - */ - @Override - public Saml2LogoutResponse resolve(HttpServletRequest request, Authentication authentication) { - return this.logoutResponseResolver.resolve(request, authentication, (registration, logoutResponse) -> { - logoutResponse.setIssueInstant(new DateTime(this.clock.millis())); - this.parametersConsumer - .accept(new LogoutResponseParameters(request, registration, authentication, logoutResponse)); - }); - } - - public void setClock(Clock clock) { - Assert.notNull(clock, "clock must not be null"); - this.clock = clock; - } - - /** - * Set a {@link Consumer} for modifying the OpenSAML {@link LogoutResponse} - * @param parametersConsumer a consumer that accepts an - * {@link LogoutResponseParameters} - */ - public void setParametersConsumer(Consumer parametersConsumer) { - Assert.notNull(parametersConsumer, "parametersConsumer cannot be null"); - this.parametersConsumer = parametersConsumer; - } - - public static final class LogoutResponseParameters { - - private final HttpServletRequest request; - - private final RelyingPartyRegistration registration; - - private final Authentication authentication; - - private final LogoutResponse logoutResponse; - - public LogoutResponseParameters(HttpServletRequest request, RelyingPartyRegistration registration, - Authentication authentication, LogoutResponse logoutResponse) { - this.request = request; - this.registration = registration; - this.authentication = authentication; - this.logoutResponse = logoutResponse; - } - - public HttpServletRequest getRequest() { - return this.request; - } - - public RelyingPartyRegistration getRelyingPartyRegistration() { - return this.registration; - } - - public Authentication getAuthentication() { - return this.authentication; - } - - public LogoutResponse getLogoutResponse() { - return this.logoutResponse; - } - - } - -} diff --git a/saml2/saml2-service-provider/src/opensaml3Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java b/saml2/saml2-service-provider/src/opensaml3Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java deleted file mode 100644 index debcdd2f48b..00000000000 --- a/saml2/saml2-service-provider/src/opensaml3Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSamlAuthenticationProviderTests.java +++ /dev/null @@ -1,682 +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.saml2.provider.service.authentication; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.time.Instant; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - -import javax.xml.namespace.QName; - -import net.shibboleth.utilities.java.support.xml.SerializeSupport; -import org.joda.time.DateTime; -import org.joda.time.Duration; -import org.junit.jupiter.api.Test; -import org.opensaml.core.xml.XMLObject; -import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport; -import org.opensaml.core.xml.io.Marshaller; -import org.opensaml.core.xml.io.MarshallingException; -import org.opensaml.core.xml.schema.XSDateTime; -import org.opensaml.core.xml.schema.impl.XSDateTimeBuilder; -import org.opensaml.saml.common.assertion.ValidationContext; -import org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters; -import org.opensaml.saml.saml2.core.Assertion; -import org.opensaml.saml.saml2.core.Attribute; -import org.opensaml.saml.saml2.core.AttributeStatement; -import org.opensaml.saml.saml2.core.AttributeValue; -import org.opensaml.saml.saml2.core.Conditions; -import org.opensaml.saml.saml2.core.EncryptedAssertion; -import org.opensaml.saml.saml2.core.EncryptedAttribute; -import org.opensaml.saml.saml2.core.EncryptedID; -import org.opensaml.saml.saml2.core.NameID; -import org.opensaml.saml.saml2.core.OneTimeUse; -import org.opensaml.saml.saml2.core.Response; -import org.opensaml.saml.saml2.core.StatusCode; -import org.opensaml.saml.saml2.core.SubjectConfirmation; -import org.opensaml.saml.saml2.core.SubjectConfirmationData; -import org.opensaml.saml.saml2.core.impl.AttributeBuilder; -import org.opensaml.saml.saml2.core.impl.EncryptedAssertionBuilder; -import org.opensaml.saml.saml2.core.impl.EncryptedIDBuilder; -import org.opensaml.saml.saml2.core.impl.NameIDBuilder; -import org.opensaml.xmlsec.encryption.impl.EncryptedDataBuilder; -import org.opensaml.xmlsec.signature.support.SignatureConstants; -import org.w3c.dom.Element; - -import org.springframework.core.convert.converter.Converter; -import org.springframework.security.core.Authentication; -import org.springframework.security.saml2.Saml2Exception; -import org.springframework.security.saml2.core.Saml2Error; -import org.springframework.security.saml2.core.Saml2ErrorCodes; -import org.springframework.security.saml2.core.Saml2ResponseValidatorResult; -import org.springframework.security.saml2.core.TestSaml2X509Credentials; -import org.springframework.security.saml2.provider.service.authentication.OpenSamlAuthenticationProvider.ResponseToken; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations; -import org.springframework.util.StringUtils; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link OpenSamlAuthenticationProvider} - * - * @author Filip Hanik - * @author Josh Cummings - */ -public class OpenSamlAuthenticationProviderTests { - - private static String DESTINATION = "https://localhost/login/saml2/sso/idp-alias"; - - private static String RELYING_PARTY_ENTITY_ID = "https://localhost/saml2/service-provider-metadata/idp-alias"; - - private static String ASSERTING_PARTY_ENTITY_ID = "https://some.idp.test/saml2/idp"; - - private OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); - - private Saml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal("name", - Collections.emptyMap()); - - private Saml2Authentication authentication = new Saml2Authentication(this.principal, "response", - Collections.emptyList()); - - @Test - public void supportsWhenSaml2AuthenticationTokenThenReturnTrue() { - assertThat(this.provider.supports(Saml2AuthenticationToken.class)) - .withFailMessage( - OpenSamlAuthenticationProvider.class + "should support " + Saml2AuthenticationToken.class) - .isTrue(); - } - - @Test - public void supportsWhenNotSaml2AuthenticationTokenThenReturnFalse() { - assertThat(!this.provider.supports(Authentication.class)) - .withFailMessage(OpenSamlAuthenticationProvider.class + "should not support " + Authentication.class) - .isTrue(); - } - - @Test - public void authenticateWhenUnknownDataClassThenThrowAuthenticationException() { - Assertion assertion = (Assertion) XMLObjectProviderRegistrySupport.getBuilderFactory() - .getBuilder(Assertion.DEFAULT_ELEMENT_NAME).buildObject(Assertion.DEFAULT_ELEMENT_NAME); - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> this.provider.authenticate( - new Saml2AuthenticationToken(verifying(registration()).build(), serialize(assertion)))) - .satisfies(errorOf(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA)); - } - - @Test - public void authenticateWhenXmlErrorThenThrowAuthenticationException() { - Saml2AuthenticationToken token = new Saml2AuthenticationToken(verifying(registration()).build(), "invalid xml"); - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> this.provider.authenticate(token)) - .satisfies(errorOf(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA)); - } - - @Test - public void authenticateWhenInvalidDestinationThenThrowAuthenticationException() { - Response response = response(DESTINATION + "invalid", ASSERTING_PARTY_ENTITY_ID); - response.getAssertions().add(assertion()); - TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, verifying(registration())); - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> this.provider.authenticate(token)) - .satisfies(errorOf(Saml2ErrorCodes.INVALID_DESTINATION)); - } - - @Test - public void authenticateWhenNoAssertionsPresentThenThrowAuthenticationException() { - Saml2AuthenticationToken token = token(); - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> this.provider.authenticate(token)) - .satisfies(errorOf(Saml2ErrorCodes.MALFORMED_RESPONSE_DATA, "No assertions found in response.")); - } - - @Test - public void authenticateWhenInvalidSignatureOnAssertionThenThrowAuthenticationException() { - Response response = response(); - response.getAssertions().add(assertion()); - Saml2AuthenticationToken token = token(response, verifying(registration())); - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> this.provider.authenticate(token)) - .satisfies(errorOf(Saml2ErrorCodes.INVALID_SIGNATURE)); - } - - @Test - public void authenticateWhenOpenSAMLValidationErrorThenThrowAuthenticationException() { - Response response = response(); - Assertion assertion = assertion(); - assertion.getSubject().getSubjectConfirmations().get(0).getSubjectConfirmationData() - .setNotOnOrAfter(DateTime.now().minus(Duration.standardDays(3))); - TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - response.getAssertions().add(assertion); - Saml2AuthenticationToken token = token(response, verifying(registration())); - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> this.provider.authenticate(token)) - .satisfies(errorOf(Saml2ErrorCodes.INVALID_ASSERTION)); - } - - @Test - public void authenticateWhenMissingSubjectThenThrowAuthenticationException() { - Response response = response(); - Assertion assertion = assertion(); - assertion.setSubject(null); - TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - response.getAssertions().add(assertion); - Saml2AuthenticationToken token = token(response, verifying(registration())); - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> this.provider.authenticate(token)) - .satisfies(errorOf(Saml2ErrorCodes.SUBJECT_NOT_FOUND)); - } - - @Test - public void authenticateWhenUsernameMissingThenThrowAuthenticationException() { - Response response = response(); - Assertion assertion = assertion(); - assertion.getSubject().getNameID().setValue(null); - TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - response.getAssertions().add(assertion); - Saml2AuthenticationToken token = token(response, verifying(registration())); - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> this.provider.authenticate(token)) - .satisfies(errorOf(Saml2ErrorCodes.SUBJECT_NOT_FOUND)); - } - - @Test - public void authenticateWhenAssertionContainsValidationAddressThenItSucceeds() { - Response response = response(); - Assertion assertion = assertion(); - assertion.getSubject().getSubjectConfirmations() - .forEach((sc) -> sc.getSubjectConfirmationData().setAddress("10.10.10.10")); - TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - response.getAssertions().add(assertion); - Saml2AuthenticationToken token = token(response, verifying(registration())); - this.provider.authenticate(token); - } - - @Test - public void authenticateWhenAssertionContainsAttributesThenItSucceeds() { - Response response = response(); - Assertion assertion = assertion(); - List attributes = attributeStatements(); - assertion.getAttributeStatements().addAll(attributes); - TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - response.getAssertions().add(assertion); - Saml2AuthenticationToken token = token(response, verifying(registration())); - Authentication authentication = this.provider.authenticate(token); - Saml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) authentication.getPrincipal(); - Map expected = new LinkedHashMap<>(); - expected.put("email", Arrays.asList("john.doe@example.com", "doe.john@example.com")); - expected.put("name", Collections.singletonList("John Doe")); - expected.put("age", Collections.singletonList(21)); - expected.put("website", Collections.singletonList("https://johndoe.com/")); - expected.put("registered", Collections.singletonList(true)); - expected.put("role", Arrays.asList("RoleTwo")); - Instant registeredDate = Instant.ofEpochMilli(DateTime.parse("1970-01-01T00:00:00Z").getMillis()); - expected.put("registeredDate", Collections.singletonList(registeredDate)); - assertThat((String) principal.getFirstAttribute("name")).isEqualTo("John Doe"); - assertThat(principal.getAttributes()).isEqualTo(expected); - } - - @Test - public void authenticateWhenEncryptedAssertionWithoutSignatureThenItFails() { - Response response = response(); - EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(), - TestSaml2X509Credentials.assertingPartyEncryptingCredential()); - response.getEncryptedAssertions().add(encryptedAssertion); - TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, decrypting(registration())); - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> this.provider.authenticate(token)) - .satisfies(errorOf(Saml2ErrorCodes.INVALID_SIGNATURE)); - } - - @Test - public void authenticateWhenEncryptedAssertionWithSignatureThenItSucceeds() { - Response response = response(); - Assertion assertion = TestOpenSamlObjects.signed(assertion(), - TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID); - EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion, - TestSaml2X509Credentials.assertingPartyEncryptingCredential()); - response.getEncryptedAssertions().add(encryptedAssertion); - TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, decrypting(verifying(registration()))); - this.provider.authenticate(token); - } - - @Test - public void authenticateWhenEncryptedAssertionWithResponseSignatureThenItSucceeds() { - Response response = response(); - EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(), - TestSaml2X509Credentials.assertingPartyEncryptingCredential()); - response.getEncryptedAssertions().add(encryptedAssertion); - TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, decrypting(verifying(registration()))); - this.provider.authenticate(token); - } - - @Test - public void authenticateWhenEncryptedNameIdWithSignatureThenItSucceeds() { - Response response = response(); - Assertion assertion = assertion(); - NameID nameId = assertion.getSubject().getNameID(); - EncryptedID encryptedID = TestOpenSamlObjects.encrypted(nameId, - TestSaml2X509Credentials.assertingPartyEncryptingCredential()); - assertion.getSubject().setNameID(null); - assertion.getSubject().setEncryptedID(encryptedID); - response.getAssertions().add(assertion); - TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, decrypting(verifying(registration()))); - this.provider.authenticate(token); - } - - @Test - public void authenticateWhenEncryptedAttributeThenDecrypts() { - Response response = response(); - Assertion assertion = assertion(); - EncryptedAttribute attribute = TestOpenSamlObjects.encrypted("name", "value", - TestSaml2X509Credentials.assertingPartyEncryptingCredential()); - AttributeStatement statement = build(AttributeStatement.DEFAULT_ELEMENT_NAME); - statement.getEncryptedAttributes().add(attribute); - assertion.getAttributeStatements().add(statement); - response.getAssertions().add(assertion); - TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, decrypting(verifying(registration()))); - Saml2Authentication authentication = (Saml2Authentication) this.provider.authenticate(token); - Saml2AuthenticatedPrincipal principal = (Saml2AuthenticatedPrincipal) authentication.getPrincipal(); - assertThat(principal.getAttribute("name")).containsExactly("value"); - } - - @Test - public void authenticateWhenDecryptionKeysAreMissingThenThrowAuthenticationException() { - Response response = response(); - EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(), - TestSaml2X509Credentials.assertingPartyEncryptingCredential()); - response.getEncryptedAssertions().add(encryptedAssertion); - TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, verifying(registration())); - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> this.provider.authenticate(token)) - .satisfies(errorOf(Saml2ErrorCodes.DECRYPTION_ERROR, "Failed to decrypt EncryptedData")); - } - - @Test - public void authenticateWhenDecryptionKeysAreWrongThenThrowAuthenticationException() { - Response response = response(); - EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion(), - TestSaml2X509Credentials.assertingPartyEncryptingCredential()); - response.getEncryptedAssertions().add(encryptedAssertion); - TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, registration() - .decryptionX509Credentials((c) -> c.add(TestSaml2X509Credentials.assertingPartyPrivateCredential()))); - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> this.provider.authenticate(token)) - .satisfies(errorOf(Saml2ErrorCodes.DECRYPTION_ERROR, "Failed to decrypt EncryptedData")); - } - - @Test - public void writeObjectWhenTypeIsSaml2AuthenticationThenNoException() throws IOException { - Response response = response(); - Assertion assertion = TestOpenSamlObjects.signed(assertion(), - TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID); - EncryptedAssertion encryptedAssertion = TestOpenSamlObjects.encrypted(assertion, - TestSaml2X509Credentials.assertingPartyEncryptingCredential()); - response.getEncryptedAssertions().add(encryptedAssertion); - TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, decrypting(verifying(registration()))); - Saml2Authentication authentication = (Saml2Authentication) this.provider.authenticate(token); - // the following code will throw an exception if authentication isn't serializable - ByteArrayOutputStream byteStream = new ByteArrayOutputStream(1024); - ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteStream); - objectOutputStream.writeObject(authentication); - objectOutputStream.flush(); - } - - @Test - public void createDefaultAssertionValidatorWhenAssertionThenValidates() { - Response response = TestOpenSamlObjects.signedResponseWithOneAssertion(); - Assertion assertion = response.getAssertions().get(0); - OpenSamlAuthenticationProvider.AssertionToken assertionToken = new OpenSamlAuthenticationProvider.AssertionToken( - assertion, token()); - assertThat(OpenSamlAuthenticationProvider.createDefaultAssertionValidator().convert(assertionToken).hasErrors()) - .isFalse(); - } - - @Test - public void authenticateWhenDelegatingToDefaultAssertionValidatorThenUses() { - OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); - // @formatter:off - provider.setAssertionValidator((assertionToken) -> OpenSamlAuthenticationProvider - .createDefaultAssertionValidator((token) -> new ValidationContext()) - .convert(assertionToken) - .concat(new Saml2Error("wrong error", "wrong error")) - ); - // @formatter:on - Response response = response(); - Assertion assertion = assertion(); - OneTimeUse oneTimeUse = build(OneTimeUse.DEFAULT_ELEMENT_NAME); - assertion.getConditions().getConditions().add(oneTimeUse); - response.getAssertions().add(assertion); - TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), - ASSERTING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, verifying(registration())); - // @formatter:off - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> provider.authenticate(token)).isInstanceOf(Saml2AuthenticationException.class) - .satisfies((error) -> assertThat(error.getSaml2Error().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_ASSERTION)); - // @formatter:on - } - - @Test - public void authenticateWhenCustomAssertionValidatorThenUses() { - Converter validator = mock( - Converter.class); - OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); - // @formatter:off - provider.setAssertionValidator((assertionToken) -> OpenSamlAuthenticationProvider.createDefaultAssertionValidator() - .convert(assertionToken) - .concat(validator.convert(assertionToken)) - ); - // @formatter:on - Response response = response(); - Assertion assertion = assertion(); - response.getAssertions().add(assertion); - TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), - ASSERTING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, verifying(registration())); - given(validator.convert(any(OpenSamlAuthenticationProvider.AssertionToken.class))) - .willReturn(Saml2ResponseValidatorResult.success()); - provider.authenticate(token); - verify(validator).convert(any(OpenSamlAuthenticationProvider.AssertionToken.class)); - } - - @Test - public void authenticateWhenDefaultConditionValidatorNotUsedThenSignatureStillChecked() { - OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); - provider.setAssertionValidator((assertionToken) -> Saml2ResponseValidatorResult.success()); - Response response = response(); - Assertion assertion = assertion(); - TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.relyingPartyDecryptingCredential(), - RELYING_PARTY_ENTITY_ID); // broken - // signature - response.getAssertions().add(assertion); - TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), - ASSERTING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, verifying(registration())); - // @formatter:off - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> provider.authenticate(token)) - .satisfies((error) -> assertThat(error.getSaml2Error().getErrorCode()).isEqualTo(Saml2ErrorCodes.INVALID_SIGNATURE)); - // @formatter:on - } - - @Test - public void authenticateWhenValidationContextCustomizedThenUsers() { - Map parameters = new HashMap<>(); - parameters.put(SAML2AssertionValidationParameters.SC_VALID_RECIPIENTS, Collections.singleton("blah")); - ValidationContext context = mock(ValidationContext.class); - given(context.getStaticParameters()).willReturn(parameters); - OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); - provider.setAssertionValidator( - OpenSamlAuthenticationProvider.createDefaultAssertionValidator((assertionToken) -> context)); - Response response = response(); - Assertion assertion = assertion(); - response.getAssertions().add(assertion); - TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), - ASSERTING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, verifying(registration())); - // @formatter:off - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> provider.authenticate(token)).isInstanceOf(Saml2AuthenticationException.class) - .satisfies((error) -> assertThat(error).hasMessageContaining("Invalid assertion")); - // @formatter:on - verify(context, atLeastOnce()).getStaticParameters(); - } - - @Test - public void authenticateWithSHA1SignatureThenItSucceeds() throws Exception { - Response response = response(); - Assertion assertion = TestOpenSamlObjects.signed(assertion(), - TestSaml2X509Credentials.assertingPartySigningCredential(), RELYING_PARTY_ENTITY_ID, - SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA1); - response.getAssertions().add(assertion); - Saml2AuthenticationToken token = token(response, verifying(registration())); - this.provider.authenticate(token); - } - - @Test - public void setAssertionValidatorWhenNullThenIllegalArgument() { - // @formatter:off - assertThatIllegalArgumentException() - .isThrownBy(() -> this.provider.setAssertionValidator(null)); - // @formatter:on - } - - @Test - public void createDefaultResponseAuthenticationConverterWhenResponseThenConverts() { - Response response = TestOpenSamlObjects.signedResponseWithOneAssertion(); - Saml2AuthenticationToken token = token(response, verifying(registration())); - ResponseToken responseToken = new ResponseToken(response, token); - Saml2Authentication authentication = OpenSamlAuthenticationProvider - .createDefaultResponseAuthenticationConverter().convert(responseToken); - assertThat(authentication.getName()).isEqualTo("test@saml.user"); - } - - @Test - public void authenticateWhenResponseAuthenticationConverterConfiguredThenUses() { - Converter authenticationConverter = mock(Converter.class); - OpenSamlAuthenticationProvider provider = new OpenSamlAuthenticationProvider(); - provider.setResponseAuthenticationConverter(authenticationConverter); - Response response = TestOpenSamlObjects.signedResponseWithOneAssertion(); - Saml2AuthenticationToken token = token(response, verifying(registration())); - provider.authenticate(token); - verify(authenticationConverter).convert(any()); - } - - @Test - public void setResponseAuthenticationConverterWhenNullThenIllegalArgument() { - // @formatter:off - assertThatIllegalArgumentException() - .isThrownBy(() -> this.provider.setResponseAuthenticationConverter(null)); - // @formatter:on - } - - @Test - public void setResponseElementsDecrypterWhenNullThenIllegalArgument() { - assertThatIllegalArgumentException().isThrownBy(() -> this.provider.setResponseElementsDecrypter(null)); - } - - @Test - public void setAssertionElementsDecrypterWhenNullThenIllegalArgument() { - assertThatIllegalArgumentException().isThrownBy(() -> this.provider.setAssertionElementsDecrypter(null)); - } - - @Test - public void authenticateWhenCustomResponseElementsDecrypterThenDecryptsResponse() { - Response response = response(); - Assertion assertion = assertion(); - TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - response.getEncryptedAssertions().add(new EncryptedAssertionBuilder().buildObject()); - TestOpenSamlObjects.signed(response, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - Saml2AuthenticationToken token = token(response, verifying(registration())); - this.provider.setResponseElementsDecrypter((tuple) -> tuple.getResponse().getAssertions().add(assertion)); - Authentication authentication = this.provider.authenticate(token); - assertThat(authentication.getName()).isEqualTo("test@saml.user"); - } - - @Test - public void authenticateWhenCustomAssertionElementsDecrypterThenDecryptsAssertion() { - Response response = response(); - Assertion assertion = assertion(); - EncryptedID id = new EncryptedIDBuilder().buildObject(); - id.setEncryptedData(new EncryptedDataBuilder().buildObject()); - assertion.getSubject().setEncryptedID(id); - TestOpenSamlObjects.signed(assertion, TestSaml2X509Credentials.assertingPartySigningCredential(), - RELYING_PARTY_ENTITY_ID); - response.getAssertions().add(assertion); - Saml2AuthenticationToken token = token(response, verifying(registration())); - this.provider.setAssertionElementsDecrypter((tuple) -> { - NameID name = new NameIDBuilder().buildObject(); - name.setValue("decrypted name"); - tuple.getAssertion().getSubject().setNameID(name); - }); - Authentication authentication = this.provider.authenticate(token); - assertThat(authentication.getName()).isEqualTo("decrypted name"); - } - - @Test - public void authenticateWhenResponseStatusIsNotSuccessThenFails() { - Response response = TestOpenSamlObjects.signedResponseWithOneAssertion( - (r) -> r.setStatus(TestOpenSamlObjects.status(StatusCode.AUTHN_FAILED))); - Saml2AuthenticationToken token = token(response, verifying(registration())); - assertThatExceptionOfType(Saml2AuthenticationException.class) - .isThrownBy(() -> this.provider.authenticate(token)) - .satisfies(errorOf(Saml2ErrorCodes.INVALID_RESPONSE, "Invalid status")); - } - - @Test - public void authenticateWhenResponseStatusIsSuccessThenSucceeds() { - Response response = TestOpenSamlObjects - .signedResponseWithOneAssertion((r) -> r.setStatus(TestOpenSamlObjects.successStatus())); - Saml2AuthenticationToken token = token(response, verifying(registration())); - Authentication authentication = this.provider.authenticate(token); - assertThat(authentication.getName()).isEqualTo("test@saml.user"); - } - - private T build(QName qName) { - return (T) XMLObjectProviderRegistrySupport.getBuilderFactory().getBuilder(qName).buildObject(qName); - } - - private String serialize(XMLObject object) { - try { - Marshaller marshaller = XMLObjectProviderRegistrySupport.getMarshallerFactory().getMarshaller(object); - Element element = marshaller.marshall(object); - return SerializeSupport.nodeToString(element); - } - catch (MarshallingException ex) { - throw new Saml2Exception(ex); - } - } - - private Consumer errorOf(String errorCode) { - return errorOf(errorCode, null); - } - - private Consumer errorOf(String errorCode, String description) { - return (ex) -> { - assertThat(ex.getSaml2Error().getErrorCode()).isEqualTo(errorCode); - if (StringUtils.hasText(description)) { - assertThat(ex.getSaml2Error().getDescription()).contains(description); - } - }; - } - - private Response response() { - Response response = TestOpenSamlObjects.response(); - response.setIssueInstant(DateTime.now()); - return response; - } - - private Response response(String destination, String issuerEntityId) { - Response response = TestOpenSamlObjects.response(destination, issuerEntityId); - response.setIssueInstant(DateTime.now()); - return response; - } - - private Assertion assertion() { - Assertion assertion = TestOpenSamlObjects.assertion(); - assertion.setIssueInstant(DateTime.now()); - for (SubjectConfirmation confirmation : assertion.getSubject().getSubjectConfirmations()) { - SubjectConfirmationData data = confirmation.getSubjectConfirmationData(); - data.setNotBefore(DateTime.now().minus(Duration.millis(5 * 60 * 1000))); - data.setNotOnOrAfter(DateTime.now().plus(Duration.millis(5 * 60 * 1000))); - } - Conditions conditions = assertion.getConditions(); - conditions.setNotBefore(DateTime.now().minus(Duration.millis(5 * 60 * 1000))); - conditions.setNotOnOrAfter(DateTime.now().plus(Duration.millis(5 * 60 * 1000))); - return assertion; - } - - private List attributeStatements() { - List attributeStatements = TestOpenSamlObjects.attributeStatements(); - AttributeBuilder attributeBuilder = new AttributeBuilder(); - Attribute registeredDateAttr = attributeBuilder.buildObject(); - registeredDateAttr.setName("registeredDate"); - XSDateTime registeredDate = new XSDateTimeBuilder().buildObject(AttributeValue.DEFAULT_ELEMENT_NAME, - XSDateTime.TYPE_NAME); - registeredDate.setValue(DateTime.parse("1970-01-01T00:00:00Z")); - registeredDateAttr.getAttributeValues().add(registeredDate); - attributeStatements.get(0).getAttributes().add(registeredDateAttr); - return attributeStatements; - } - - private Saml2AuthenticationToken token() { - Response response = response(); - RelyingPartyRegistration registration = verifying(registration()).build(); - return new Saml2AuthenticationToken(registration, serialize(response)); - } - - private Saml2AuthenticationToken token(Response response, RelyingPartyRegistration.Builder registration) { - return new Saml2AuthenticationToken(registration.build(), serialize(response)); - } - - private RelyingPartyRegistration.Builder registration() { - return TestRelyingPartyRegistrations.noCredentials().entityId(RELYING_PARTY_ENTITY_ID) - .assertionConsumerServiceLocation(DESTINATION) - .assertingPartyDetails((party) -> party.entityId(ASSERTING_PARTY_ENTITY_ID)); - } - - private RelyingPartyRegistration.Builder verifying(RelyingPartyRegistration.Builder builder) { - return builder.assertingPartyDetails((party) -> party - .verificationX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyVerifyingCredential()))); - } - - private RelyingPartyRegistration.Builder decrypting(RelyingPartyRegistration.Builder builder) { - return builder - .decryptionX509Credentials((c) -> c.add(TestSaml2X509Credentials.relyingPartyDecryptingCredential())); - } - -} diff --git a/saml2/saml2-service-provider/src/opensaml3Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutRequestResolverTests.java b/saml2/saml2-service-provider/src/opensaml3Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutRequestResolverTests.java deleted file mode 100644 index 6a1f9afd42f..00000000000 --- a/saml2/saml2-service-provider/src/opensaml3Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutRequestResolverTests.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2002-2021 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.saml2.provider.service.web.authentication.logout; - -import jakarta.servlet.http.HttpServletRequest; -import org.junit.jupiter.api.Test; - -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutRequest; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations; -import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; - -/** - * Tests for {@link OpenSaml3LogoutRequestResolver} - */ -public class OpenSaml3LogoutRequestResolverTests { - - RelyingPartyRegistrationResolver relyingPartyRegistrationResolver = mock(RelyingPartyRegistrationResolver.class); - - @Test - public void resolveWhenCustomParametersConsumerThenUses() { - OpenSaml3LogoutRequestResolver logoutRequestResolver = new OpenSaml3LogoutRequestResolver( - this.relyingPartyRegistrationResolver); - logoutRequestResolver.setParametersConsumer((parameters) -> parameters.getLogoutRequest().setID("myid")); - HttpServletRequest request = new MockHttpServletRequest(); - RelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration() - .assertingPartyDetails((party) -> party.singleLogoutServiceLocation("https://ap.example.com/logout")) - .build(); - Authentication authentication = new TestingAuthenticationToken("user", "password"); - given(this.relyingPartyRegistrationResolver.resolve(any(), any())).willReturn(registration); - Saml2LogoutRequest logoutRequest = logoutRequestResolver.resolve(request, authentication); - assertThat(logoutRequest.getId()).isEqualTo("myid"); - } - - @Test - public void setParametersConsumerWhenNullThenIllegalArgument() { - OpenSaml3LogoutRequestResolver logoutRequestResolver = new OpenSaml3LogoutRequestResolver( - this.relyingPartyRegistrationResolver); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> logoutRequestResolver.setParametersConsumer(null)); - } - -} diff --git a/saml2/saml2-service-provider/src/opensaml3Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutResponseResolverTests.java b/saml2/saml2-service-provider/src/opensaml3Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutResponseResolverTests.java deleted file mode 100644 index 628d2401e44..00000000000 --- a/saml2/saml2-service-provider/src/opensaml3Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml3LogoutResponseResolverTests.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2002-2021 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.saml2.provider.service.web.authentication.logout; - -import java.util.function.Consumer; - -import org.junit.jupiter.api.Test; -import org.opensaml.saml.saml2.core.LogoutRequest; - -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.security.authentication.TestingAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.saml2.core.Saml2ParameterNames; -import org.springframework.security.saml2.provider.service.authentication.TestOpenSamlObjects; -import org.springframework.security.saml2.provider.service.authentication.logout.Saml2LogoutResponse; -import org.springframework.security.saml2.provider.service.registration.RelyingPartyRegistration; -import org.springframework.security.saml2.provider.service.registration.TestRelyingPartyRegistrations; -import org.springframework.security.saml2.provider.service.web.RelyingPartyRegistrationResolver; -import org.springframework.security.saml2.provider.service.web.authentication.logout.OpenSaml3LogoutResponseResolver.LogoutResponseParameters; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -/** - * Tests for {@link OpenSaml3LogoutResponseResolver} - */ -public class OpenSaml3LogoutResponseResolverTests { - - RelyingPartyRegistrationResolver relyingPartyRegistrationResolver = mock(RelyingPartyRegistrationResolver.class); - - @Test - public void resolveWhenCustomParametersConsumerThenUses() { - OpenSaml3LogoutResponseResolver logoutResponseResolver = new OpenSaml3LogoutResponseResolver( - this.relyingPartyRegistrationResolver); - Consumer parametersConsumer = mock(Consumer.class); - logoutResponseResolver.setParametersConsumer(parametersConsumer); - MockHttpServletRequest request = new MockHttpServletRequest(); - RelyingPartyRegistration registration = TestRelyingPartyRegistrations.relyingPartyRegistration() - .assertingPartyDetails( - (party) -> party.singleLogoutServiceResponseLocation("https://ap.example.com/logout")) - .build(); - Authentication authentication = new TestingAuthenticationToken("user", "password"); - LogoutRequest logoutRequest = TestOpenSamlObjects.assertingPartyLogoutRequest(registration); - request.setParameter(Saml2ParameterNames.SAML_REQUEST, - Saml2Utils.samlEncode(OpenSamlSigningUtils.serialize(logoutRequest).getBytes())); - given(this.relyingPartyRegistrationResolver.resolve(any(), any())).willReturn(registration); - Saml2LogoutResponse logoutResponse = logoutResponseResolver.resolve(request, authentication); - assertThat(logoutResponse).isNotNull(); - verify(parametersConsumer).accept(any()); - } - - @Test - public void setParametersConsumerWhenNullThenIllegalArgument() { - OpenSaml3LogoutRequestResolver logoutRequestResolver = new OpenSaml3LogoutRequestResolver( - this.relyingPartyRegistrationResolver); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> logoutRequestResolver.setParametersConsumer(null)); - } - -} diff --git a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipalTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipalTests.java index fe848b5a7f7..1eaa84d50bb 100644 --- a/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipalTests.java +++ b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/DefaultSaml2AuthenticatedPrincipalTests.java @@ -22,7 +22,6 @@ import java.util.List; import java.util.Map; -import org.joda.time.DateTime; import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -72,7 +71,7 @@ public void getAttributeWhenStringValuesThenReturnsValues() { @Test public void getAttributeWhenDistinctValuesThenReturnsValues() { final Boolean registered = true; - final Instant registeredDate = Instant.ofEpochMilli(DateTime.parse("1970-01-01T00:00:00Z").getMillis()); + final Instant registeredDate = Instant.parse("1970-01-01T00:00:00Z"); Map> attributes = new LinkedHashMap<>(); attributes.put("registration", Arrays.asList(registered, registeredDate)); DefaultSaml2AuthenticatedPrincipal principal = new DefaultSaml2AuthenticatedPrincipal("user", attributes); diff --git a/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java similarity index 100% rename from saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java rename to saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/authentication/OpenSaml4AuthenticationProviderTests.java diff --git a/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml4AuthenticationRequestResolverTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml4AuthenticationRequestResolverTests.java similarity index 100% rename from saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml4AuthenticationRequestResolverTests.java rename to saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/OpenSaml4AuthenticationRequestResolverTests.java diff --git a/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutRequestResolverTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutRequestResolverTests.java similarity index 100% rename from saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutRequestResolverTests.java rename to saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutRequestResolverTests.java diff --git a/saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolverTests.java b/saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolverTests.java similarity index 100% rename from saml2/saml2-service-provider/src/opensaml4Test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolverTests.java rename to saml2/saml2-service-provider/src/test/java/org/springframework/security/saml2/provider/service/web/authentication/logout/OpenSaml4LogoutResponseResolverTests.java