diff --git a/README.MD b/README.MD index 5a4d4ea5e..e6c07f3fd 100644 --- a/README.MD +++ b/README.MD @@ -2,7 +2,7 @@ Do not hesitate to fork this repo and send pull requests, even for things as sma # Spring-addons The libraries hosted in this repo shine in two domains: -- provide with annotations to mock OAuth2 `Authentication` during tests (`@WithMockJwtAuth`, `@WithOAuth2Login`, `@WithOidcLogin`, `@WithMockBearerTokenAuthentication`, etc.), which allow to test method security on any `@Component`. Details below. +- provide with annotations to mock OAuth2 `Authentication` during tests (`@WithMockJwtAuth`, `@WithOAuth2Login`, `@WithOidcLogin`, `@WithMockBearerTokenAuthentication`, etc.), which allow to test method security on any `@Component`. **New in 6.1.12: `@JwtAuthenticationSource` and alike to work with JUnit 5 `@ParameterizedTest`**. Details below. - help configuring Spring Boot 3 applications OAuth2 configuration by pushing auto-configuration to the next level. As shown in **[Tutorials](https://github.com/ch4mpy/spring-addons/tree/master/samples/tutorials)**, with 0 Java conf (just properties), we can configure: * authorities mapping (source claims, prefix and case transformation), without having to provide authentication converter, user service or `GrantedAuthoritiesMapper` in each app * fine grained CORS configuration (per path matcher), which enables to override allowed origins as environment variable when switching from `localhost` to `dev` or `prod` environments @@ -29,6 +29,8 @@ To test method security on any type of `@Component` (`@Controller`, off course, An [article covering the usage of OAuth2 test annotations from this lib](https://www.baeldung.com/spring-oauth-testing-access-control) was published on Baeldung. This, along with all [samples](https://github.com/ch4mpy/spring-addons/tree/master/samples) and [tutorials](https://github.com/ch4mpy/spring-addons/tree/master/samples/tutorials) source-code (which contain a lot of unit and integration testing), should be enough to get you started. +Starting from `6.1.12`, `@JwtAuthenticationSource`, `@BearerAuthenticationSource`, `@OpenIdAuthenticationSource`, `@OAuth2LoginAuthenticationSource` and `@OidcLoginAuthenticationSource` were added to run the same JUnit 5 `@ParameterizedTest` with different `Authentication` instance. This can prove pretty handy to assert that access is actually granted for each of the entries in a `asAnyAuthority(...)` or `asAnyRole(...)` expression. See [Release Notes](#release-notes) or Javadoc for usage. + ## 2. Spring Boot Starters This repo contains thin wrappers around `spring-boot-starter-oauth2-resource-server` or `spring-boot-starter-oauth2-client`: - [spring-addons-webflux-client](https://github.com/ch4mpy/spring-addons/tree/master/webflux/spring-addons-webflux-client) to be used in reactive applications rendering templates on the server (Thymeleaf, JSF, etc.), **or in `spring-cloud-gateway` used as BFF** (server-side OAuth2 confidential client securing a browser application with sessions and replacing session cookies with OAuth2 access tokens before forwarding requests from browsers to resource servers) @@ -63,7 +65,7 @@ If locked wtih a lower JDK or spring-boot version, you'll have to use a 5.4.x re I could forget to update README before releasing, so please refer to [maven central](https://repo1.maven.org/maven2/com/c4-soft/springaddons/spring-addons/) to pick latest available release ```xml - 6.1.11 + 6.1.12 webmvc jwt @@ -118,11 +120,11 @@ Using such libs is dead simple: just declare depedency on one of those libs and 2.0 comes with a noticeable amount of breaking changes. So lets start tracking features. ### 6.1.12 -- [gh-122](https://github.com/ch4mpy/spring-addons/issues/122) Support for parametrized OAuth2 Authentications in `@ParameterizedTest`. Sample usage (mind the `@JwtAuthenticationSource` and `@ParameterizedJwtAuth`): +- [gh-122](https://github.com/ch4mpy/spring-addons/issues/122) Support for parametrized OAuth2 Authentications in `@ParameterizedTest`. **Mind the `@JwtAuthenticationSource` and `@ParameterizedJwtAuth`** in this sample (the first annotation defines the different authentication instances, the second inserts the one for the current test in the security context and provides it as test method parameter): ```java @ParameterizedTest @JwtAuthenticationSource({ @WithMockJwtAuth("NICE"), @WithMockJwtAuth("VERY_NICE") }) -void givenUserIsGrantedWithAnyJwtAuthentication_whenGetRestricted_thenOk(@ParameterizedJwtAuth JwtAuthenticationToken auth) throws Exception { +void givenUserIsGrantedWithAnyNiceAuthority_whenGetRestricted_thenOk(@ParameterizedJwtAuth JwtAuthenticationToken auth) throws Exception { api.perform(get("/restricted")) .andExpect(status().isOk()) .andExpect(jsonPath("$.body").value("You are so nice!")); diff --git a/samples/tutorials/reactive-client/README.md b/samples/tutorials/reactive-client/README.md index 7ce3ecb94..79bf8812d 100644 --- a/samples/tutorials/reactive-client/README.md +++ b/samples/tutorials/reactive-client/README.md @@ -244,12 +244,18 @@ static class DelegatingOidcClientInitiatedServerLogoutSuccessHandler implements String postLogoutRedirectUri) { delegates = StreamSupport.stream(clientRegistrationRepository.spliterator(), false) .collect(Collectors.toMap(ClientRegistration::getRegistrationId, clientRegistration -> { - final var registrationProperties = properties.getRegistration().get(clientRegistration.getRegistrationId()); - if (registrationProperties == null) { + final var endSessionEnpoint = (String) (clientRegistration.getProviderDetails().getConfigurationMetadata().get("end_session_endpoint")); + if (StringUtils.hasText(endSessionEnpoint)) { final var handler = new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository); handler.setPostLogoutRedirectUri(postLogoutRedirectUri); return handler; } + final var registrationProperties = properties.getRegistration().get(clientRegistration.getRegistrationId()); + if (registrationProperties == null) { + throw new MisconfigurationException( + "OAuth2 client registration \"%s\" has no end_session_endpoint in OpenID configuration nor spring-addons logout properties" + .formatted(clientRegistration.getRegistrationId())); + } return new AlmostOidcClientInitiatedServerLogoutSuccessHandler(registrationProperties, clientRegistration, postLogoutRedirectUri); })); } @@ -262,7 +268,7 @@ static class DelegatingOidcClientInitiatedServerLogoutSuccessHandler implements } ``` -This handler switches between Spring's `OidcClientInitiatedServerLogoutSuccessHandler` and our `AlmostOidcClientInitiatedServerLogoutSuccessHandler` depending on the configuration properties. +This handler switches between Spring's `OidcClientInitiatedServerLogoutSuccessHandler` (used if the `.well-known/openid-configuration` exposes an `end_session_endpont`) and our `AlmostOidcClientInitiatedServerLogoutSuccessHandler` (if the logout configuration properties are present, or throws an exception). Last we need to update the security filter-chain to use the new `DelegatingOidcClientInitiatedServerLogoutSuccessHandler`: ```java diff --git a/samples/tutorials/reactive-client/src/main/java/com/c4soft/springaddons/tutorials/WebSecurityConfig.java b/samples/tutorials/reactive-client/src/main/java/com/c4soft/springaddons/tutorials/WebSecurityConfig.java index 182a0f753..0497e54ce 100644 --- a/samples/tutorials/reactive-client/src/main/java/com/c4soft/springaddons/tutorials/WebSecurityConfig.java +++ b/samples/tutorials/reactive-client/src/main/java/com/c4soft/springaddons/tutorials/WebSecurityConfig.java @@ -1,5 +1,7 @@ package com.c4soft.springaddons.tutorials; +import static org.springframework.security.config.Customizer.withDefaults; + import java.net.MalformedURLException; import java.net.URI; import java.net.URL; @@ -45,12 +47,14 @@ import org.springframework.security.web.server.authentication.logout.RedirectServerLogoutSuccessHandler; import org.springframework.security.web.server.authentication.logout.ServerLogoutSuccessHandler; import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import org.springframework.web.server.WebFilterChain; import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; +import com.c4soft.springaddons.tutorials.WebSecurityConfig.AuthoritiesMappingProperties.MisconfigurationException; import com.jayway.jsonpath.JsonPath; import com.jayway.jsonpath.PathNotFoundException; @@ -69,7 +73,7 @@ SecurityWebFilterChain clientSecurityFilterChain( InMemoryReactiveClientRegistrationRepository clientRegistrationRepository, LogoutProperties logoutProperties) { http.addFilterBefore(loginPageWebFilter(), SecurityWebFiltersOrder.LOGIN_PAGE_GENERATING); - http.oauth2Login(); + http.oauth2Login(withDefaults()); http.logout(logout -> { logout.logoutSuccessHandler( new DelegatingOidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository, logoutProperties, "{baseUrl}")); @@ -189,12 +193,18 @@ public DelegatingOidcClientInitiatedServerLogoutSuccessHandler( String postLogoutRedirectUri) { delegates = StreamSupport.stream(clientRegistrationRepository.spliterator(), false) .collect(Collectors.toMap(ClientRegistration::getRegistrationId, clientRegistration -> { - final var registrationProperties = properties.getRegistration().get(clientRegistration.getRegistrationId()); - if (registrationProperties == null) { + final var endSessionEnpoint = (String) (clientRegistration.getProviderDetails().getConfigurationMetadata().get("end_session_endpoint")); + if (StringUtils.hasText(endSessionEnpoint)) { final var handler = new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository); handler.setPostLogoutRedirectUri(postLogoutRedirectUri); return handler; } + final var registrationProperties = properties.getRegistration().get(clientRegistration.getRegistrationId()); + if (registrationProperties == null) { + throw new MisconfigurationException( + "OAuth2 client registration \"%s\" has no end_session_endpoint in OpenID configuration nor spring-addons logout properties" + .formatted(clientRegistration.getRegistrationId())); + } return new AlmostOidcClientInitiatedServerLogoutSuccessHandler(registrationProperties, clientRegistration, postLogoutRedirectUri); })); } diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/BearerAuthenticationSource.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/BearerAuthenticationSource.java index e3de94753..874834c86 100644 --- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/BearerAuthenticationSource.java +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/BearerAuthenticationSource.java @@ -31,6 +31,7 @@ * * @author Jerome Wacongne ch4mp@c4-soft.com * @see ParameterizedBearerAuth + * @since 6.1.12 */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/JwtAuthenticationSource.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/JwtAuthenticationSource.java index 1e06c1d7b..516934c65 100644 --- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/JwtAuthenticationSource.java +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/JwtAuthenticationSource.java @@ -31,6 +31,7 @@ * * @author Jerome Wacongne ch4mp@c4-soft.com * @see ParameterizedJwtAuth + * @since 6.1.12 */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OAuth2LoginAuthenticationSource.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OAuth2LoginAuthenticationSource.java index 2e1577d99..66fe9f9e1 100644 --- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OAuth2LoginAuthenticationSource.java +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OAuth2LoginAuthenticationSource.java @@ -31,6 +31,7 @@ * * @author Jerome Wacongne ch4mp@c4-soft.com * @see ParameterizedOAuth2Login + * @since 6.1.12 */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OidcLoginAuthenticationSource.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OidcLoginAuthenticationSource.java index fba6afd8a..5990e8d87 100644 --- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OidcLoginAuthenticationSource.java +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OidcLoginAuthenticationSource.java @@ -31,6 +31,7 @@ * * @author Jerome Wacongne ch4mp@c4-soft.com * @see ParameterizedOidcLogin + * @since 6.1.12 */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OpenIdAuthenticationSource.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OpenIdAuthenticationSource.java index 69ac3ed09..dec249a8e 100644 --- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OpenIdAuthenticationSource.java +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OpenIdAuthenticationSource.java @@ -32,6 +32,7 @@ * * @author Jerome Wacongne ch4mp@c4-soft.com * @see ParameterizedOpenId + * @since 6.1.12 */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedBearerAuth.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedBearerAuth.java index 5a45b8c91..badea4b37 100644 --- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedBearerAuth.java +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedBearerAuth.java @@ -26,6 +26,7 @@ * * @author Jerome Wacongne ch4mp@c4-soft.com * @see BearerAuthenticationSource + * @since 6.1.12 */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedJwtAuth.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedJwtAuth.java index 9826600ba..052497ee1 100644 --- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedJwtAuth.java +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedJwtAuth.java @@ -26,6 +26,7 @@ * * @author Jerome Wacongne ch4mp@c4-soft.com * @see JwtAuthenticationSource + * @since 6.1.12 */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOAuth2Login.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOAuth2Login.java index e6ded63bb..a8a5fb47a 100644 --- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOAuth2Login.java +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOAuth2Login.java @@ -26,6 +26,7 @@ * * @author Jerome Wacongne ch4mp@c4-soft.com * @see OAuth2LoginAuthenticationSource + * @since 6.1.12 */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOidcLogin.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOidcLogin.java index 9401ba3df..6f5648154 100644 --- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOidcLogin.java +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOidcLogin.java @@ -26,6 +26,7 @@ * * @author Jerome Wacongne ch4mp@c4-soft.com * @see OidcLoginAuthenticationSource + * @since 6.1.12 */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME) diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOpenId.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOpenId.java index 0ef2a155c..370564a2a 100644 --- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOpenId.java +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOpenId.java @@ -28,6 +28,7 @@ * * @author Jerome Wacongne ch4mp@c4-soft.com * @see OpenIdAuthenticationSource + * @since 6.1.12 */ @Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER }) @Retention(RetentionPolicy.RUNTIME)