From 36ca94fb44cf709ae1305340e2e1d6ace569f0df Mon Sep 17 00:00:00 2001 From: ch4mpy Date: Tue, 13 Jun 2023 17:31:13 -1000 Subject: [PATCH] gh-122 parametrized OAuth2 Authentications for @ParameterizedTest --- README.MD | 17 ++ .../springaddons/tutorials/WithMyAuth.java | 7 +- .../springaddons/tutorials/ProxiesAuth.java | 5 +- .../src/main/resources/application.yml | 5 +- .../servlet-resource-server/README.md | 10 +- .../tutorials/servlet-resource-server/pom.xml | 13 +- .../tutorials/GreetingController.java | 2 +- .../tutorials/WebSecurityConfig.java | 23 +- .../tutorials/GreetingControllerTest.java | 85 +++++- spring-addons-oauth2-test/pom.xml | 4 + ...bstractAnnotatedAuthenticationBuilder.java | 46 ++-- .../oauth2/test/annotations/Claims.java | 252 +++++++++--------- .../oauth2/test/annotations/OpenId.java | 46 ++-- .../WithMockBearerTokenAuthentication.java | 59 ++-- .../test/annotations/WithMockJwtAuth.java | 48 ++-- .../test/annotations/WithOAuth2Login.java | 46 ++-- .../test/annotations/WithOidcLogin.java | 47 ++-- .../BearerAuthenticationSource.java | 57 ++++ .../JwtAuthenticationSource.java | 57 ++++ .../OAuth2LoginAuthenticationSource.java | 57 ++++ .../OidcLoginAuthenticationSource.java | 57 ++++ .../OpenIdAuthenticationSource.java | 58 ++++ .../ParameterizedBearerAuth.java | 49 ++++ .../parameterized/ParameterizedJwtAuth.java | 49 ++++ .../ParameterizedOAuth2Login.java | 49 ++++ .../parameterized/ParameterizedOidcLogin.java | 49 ++++ .../parameterized/ParameterizedOpenId.java | 59 ++++ 27 files changed, 946 insertions(+), 310 deletions(-) create mode 100644 spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/BearerAuthenticationSource.java create mode 100644 spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/JwtAuthenticationSource.java create mode 100644 spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OAuth2LoginAuthenticationSource.java create mode 100644 spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OidcLoginAuthenticationSource.java create mode 100644 spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OpenIdAuthenticationSource.java create mode 100644 spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedBearerAuth.java create mode 100644 spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedJwtAuth.java create mode 100644 spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOAuth2Login.java create mode 100644 spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOidcLogin.java create mode 100644 spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOpenId.java diff --git a/README.MD b/README.MD index 0603103cf..5a4d4ea5e 100644 --- a/README.MD +++ b/README.MD @@ -117,6 +117,23 @@ 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`): +```java +@ParameterizedTest +@JwtAuthenticationSource({ @WithMockJwtAuth("NICE"), @WithMockJwtAuth("VERY_NICE") }) +void givenUserIsGrantedWithAnyJwtAuthentication_whenGetRestricted_thenOk(@ParameterizedJwtAuth JwtAuthenticationToken auth) throws Exception { + api.perform(get("/restricted")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.body").value("You are so nice!")); +} +``` + The above will run two distinct tests in sequence, one with each of the provided `@WithMockJwtAuth`. Same for: + * `@WithMockBearerTokenAuthentication` with `@BearerAuthenticationSource` and `@ParameterizedBearerAuth` + * `@OpenId` with `@OpenIdAuthenticationSource` and `@ParameterizedOpenId` + * `@WithOAuth2Login` with `@OAuth2LoginAuthenticationSource` and `@ParameterizedOAuth2Login` + * `@WithOidcLogin` with `@OidcLoginAuthenticationSource` and `@ParameterizedOidcLogin` + ### 6.1.11 - Spring Boot 3.1.0 diff --git a/samples/tutorials/resource-server_with_additional-header/src/test/java/com/c4soft/springaddons/tutorials/WithMyAuth.java b/samples/tutorials/resource-server_with_additional-header/src/test/java/com/c4soft/springaddons/tutorials/WithMyAuth.java index db781b9f5..cae5b7f6d 100644 --- a/samples/tutorials/resource-server_with_additional-header/src/test/java/com/c4soft/springaddons/tutorials/WithMyAuth.java +++ b/samples/tutorials/resource-server_with_additional-header/src/test/java/com/c4soft/springaddons/tutorials/WithMyAuth.java @@ -56,7 +56,12 @@ public MyAuth authentication(WithMyAuth annotation) { final var accessClaims = new OpenidClaimSet(super.claims(annotation.accessClaims())); final var idClaims = new OpenidClaimSet(super.claims(annotation.idClaims())); - return new MyAuth(super.authorities(annotation.authorities()), annotation.accessTokenString(), accessClaims, annotation.idTokenString(), idClaims); + return new MyAuth( + super.authorities(annotation.authorities(), annotation.value()), + annotation.accessTokenString(), + accessClaims, + annotation.idTokenString(), + idClaims); } } } diff --git a/samples/tutorials/resource-server_with_specialized_oauthentication/src/test/java/com/c4soft/springaddons/tutorials/ProxiesAuth.java b/samples/tutorials/resource-server_with_specialized_oauthentication/src/test/java/com/c4soft/springaddons/tutorials/ProxiesAuth.java index 93ae73b2a..ff41f3ec5 100644 --- a/samples/tutorials/resource-server_with_specialized_oauthentication/src/test/java/com/c4soft/springaddons/tutorials/ProxiesAuth.java +++ b/samples/tutorials/resource-server_with_specialized_oauthentication/src/test/java/com/c4soft/springaddons/tutorials/ProxiesAuth.java @@ -60,7 +60,10 @@ public ProxiesAuthentication authentication(ProxiesAuth annotation) { }); openidClaims.put("proxies", proxiesClaim); - return new ProxiesAuthentication(new ProxiesClaimSet(openidClaims), super.authorities(annotation.authorities()), annotation.bearerString()); + return new ProxiesAuthentication( + new ProxiesClaimSet(openidClaims), + super.authorities(annotation.authorities(), annotation.value()), + annotation.bearerString()); } } } diff --git a/samples/tutorials/resource-server_with_ui/src/main/resources/application.yml b/samples/tutorials/resource-server_with_ui/src/main/resources/application.yml index 1444dee46..a40a87755 100644 --- a/samples/tutorials/resource-server_with_ui/src/main/resources/application.yml +++ b/samples/tutorials/resource-server_with_ui/src/main/resources/application.yml @@ -6,6 +6,7 @@ scheme: http keycloak-port: 8442 keycloak-issuer: ${scheme}://localhost:${keycloak-port}/realms/master keycloak-secret: change-me +keycloak-client-id: spring-addons-confidential cognito-issuer: https://cognito-idp.us-west-2.amazonaws.com/us-west-2_RzhmgLwjl cognito-secret: change-me auth0-issuer: https://dev-ch4mpy.eu.auth0.com/ @@ -33,13 +34,13 @@ spring: keycloak-user: authorization-grant-type: authorization_code client-name: a local Keycloak instance - client-id: spring-addons-confidential + client-id: ${keycloak-client-id} client-secret: ${keycloak-secret} provider: keycloak scope: openid,profile,email,offline_access keycloak-programmatic: authorization-grant-type: client_credentials - client-id: spring-addons-confidential + client-id: ${keycloak-client-id} client-secret: ${keycloak-secret} provider: keycloak scope: openid,offline_access diff --git a/samples/tutorials/servlet-resource-server/README.md b/samples/tutorials/servlet-resource-server/README.md index d80f6f315..3ad7ebcd8 100644 --- a/samples/tutorials/servlet-resource-server/README.md +++ b/samples/tutorials/servlet-resource-server/README.md @@ -115,7 +115,7 @@ public class WebSecurityConfig { @Bean SecurityFilterChain - filterChain(HttpSecurity http, ServerProperties serverProperties, @Value("origins") String[] origins, @Value("permit-all") String[] permitAll) + filterChain(HttpSecurity http, ServerProperties serverProperties, @Value("${origins:[]}") String[] origins, @Value("${permit-all:[]}") String[] permitAll) throws Exception { http.oauth2ResourceServer(resourceServer -> resourceServer.jwt()); @@ -316,8 +316,8 @@ The last missing configuration piece is an update of the security filter-chain: SecurityFilterChain filterChain( HttpSecurity http, ServerProperties serverProperties, - @Value("origins") String[] origins, - @Value("permit-all") String[] permitAll, + @Value("${origins:[]}") String[] origins, + @Value("${permit-all:[]}") String[] permitAll, SpringAddonsProperties springAddonsProperties, SpringAddonsJwtAuthenticationConverter authenticationConverter) throws Exception { @@ -373,8 +373,8 @@ Last, when configuring the resource server within the security filter-chain, we' SecurityFilterChain filterChain( HttpSecurity http, ServerProperties serverProperties, - @Value("origins") String[] origins, - @Value("permit-all") String[] permitAll, + @Value("${origins:[]}") String[] origins, + @Value("${permit-all:[]}") String[] permitAll, AuthenticationManagerResolver authenticationManagerResolver) throws Exception { diff --git a/samples/tutorials/servlet-resource-server/pom.xml b/samples/tutorials/servlet-resource-server/pom.xml index d7a8fabbc..16e334b1a 100644 --- a/samples/tutorials/servlet-resource-server/pom.xml +++ b/samples/tutorials/servlet-resource-server/pom.xml @@ -1,5 +1,7 @@ - + 4.0.0 com.c4-soft.springaddons.samples.tutorials @@ -9,7 +11,7 @@ servlet-resource-server servlet-resource-server - + org.springframework.boot @@ -27,7 +29,7 @@ com.jayway.jsonpath json-path - + org.springframework.boot spring-boot-devtools @@ -49,6 +51,11 @@ spring-security-test test + + com.c4-soft.springaddons + spring-addons-oauth2-test + test + org.springframework.boot spring-boot-configuration-processor diff --git a/samples/tutorials/servlet-resource-server/src/main/java/com/c4soft/springaddons/tutorials/GreetingController.java b/samples/tutorials/servlet-resource-server/src/main/java/com/c4soft/springaddons/tutorials/GreetingController.java index 6fd70ac2a..af0e3b3eb 100644 --- a/samples/tutorials/servlet-resource-server/src/main/java/com/c4soft/springaddons/tutorials/GreetingController.java +++ b/samples/tutorials/servlet-resource-server/src/main/java/com/c4soft/springaddons/tutorials/GreetingController.java @@ -14,7 +14,7 @@ public MessageDto getGreeting(Authentication auth) { } @GetMapping("/restricted") - @PreAuthorize("hasAuthority('NICE')") + @PreAuthorize("hasAnyAuthority('NICE', 'VERY_NICE')") public MessageDto getRestricted() { return new MessageDto("You are so nice!"); } diff --git a/samples/tutorials/servlet-resource-server/src/main/java/com/c4soft/springaddons/tutorials/WebSecurityConfig.java b/samples/tutorials/servlet-resource-server/src/main/java/com/c4soft/springaddons/tutorials/WebSecurityConfig.java index 42f608a94..38919e58b 100644 --- a/samples/tutorials/servlet-resource-server/src/main/java/com/c4soft/springaddons/tutorials/WebSecurityConfig.java +++ b/samples/tutorials/servlet-resource-server/src/main/java/com/c4soft/springaddons/tutorials/WebSecurityConfig.java @@ -54,41 +54,38 @@ public class WebSecurityConfig { SecurityFilterChain filterChain( HttpSecurity http, ServerProperties serverProperties, - @Value("origins") String[] origins, - @Value("permit-all") String[] permitAll, + @Value("${origins:[]}") String[] origins, + @Value("${permit-all:[]}") String[] permitAll, AuthenticationManagerResolver authenticationManagerResolver) throws Exception { http.oauth2ResourceServer(oauth2 -> oauth2.authenticationManagerResolver(authenticationManagerResolver)); - // Enable anonymous - http.anonymous(); - // Enable and configure CORS - http.cors().configurationSource(corsConfigurationSource(origins)); + http.cors(cors -> cors.configurationSource(corsConfigurationSource(origins))); // State-less session (state in access-token only) - http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); + http.sessionManagement(sm -> sm.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // Disable CSRF because of state-less session-management - http.csrf().disable(); + http.csrf(csrf -> csrf.disable()); // Return 401 (unauthorized) instead of 302 (redirect to login) when // authorization is missing or invalid - http.exceptionHandling().authenticationEntryPoint((request, response, authException) -> { + http.exceptionHandling(eh -> eh.authenticationEntryPoint((request, response, authException) -> { response.addHeader(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"Restricted Content\""); response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase()); - }); + })); // If SSL enabled, disable http (https only) if (serverProperties.getSsl() != null && serverProperties.getSsl().isEnabled()) { - http.requiresChannel().anyRequest().requiresSecure(); + http.requiresChannel(channel -> channel.anyRequest().requiresSecure()); } // @formatter:off - http.authorizeHttpRequests() + http.authorizeHttpRequests(requests -> requests .requestMatchers(permitAll).permitAll() - .anyRequest().authenticated(); + .anyRequest().authenticated()); // @formatter:on return http.build(); diff --git a/samples/tutorials/servlet-resource-server/src/test/java/com/c4soft/springaddons/tutorials/GreetingControllerTest.java b/samples/tutorials/servlet-resource-server/src/test/java/com/c4soft/springaddons/tutorials/GreetingControllerTest.java index 824a3eb1e..2e6170dae 100644 --- a/samples/tutorials/servlet-resource-server/src/test/java/com/c4soft/springaddons/tutorials/GreetingControllerTest.java +++ b/samples/tutorials/servlet-resource-server/src/test/java/com/c4soft/springaddons/tutorials/GreetingControllerTest.java @@ -5,15 +5,38 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.security.authentication.AuthenticationManagerResolver; import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; +import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors; import org.springframework.test.web.servlet.MockMvc; +import com.c4_soft.springaddons.security.oauth2.OAuthentication; +import com.c4_soft.springaddons.security.oauth2.OpenidClaimSet; +import com.c4_soft.springaddons.security.oauth2.test.annotations.OpenId; +import com.c4_soft.springaddons.security.oauth2.test.annotations.WithMockBearerTokenAuthentication; +import com.c4_soft.springaddons.security.oauth2.test.annotations.WithMockJwtAuth; +import com.c4_soft.springaddons.security.oauth2.test.annotations.WithOAuth2Login; +import com.c4_soft.springaddons.security.oauth2.test.annotations.WithOidcLogin; +import com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized.BearerAuthenticationSource; +import com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized.JwtAuthenticationSource; +import com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized.OAuth2LoginAuthenticationSource; +import com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized.OidcLoginAuthenticationSource; +import com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized.OpenIdAuthenticationSource; +import com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized.ParameterizedBearerAuth; +import com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized.ParameterizedJwtAuth; +import com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized.ParameterizedOAuth2Login; +import com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized.ParameterizedOidcLogin; +import com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized.ParameterizedOpenId; + import jakarta.servlet.http.HttpServletRequest; @WebMvcTest(controllers = GreetingController.class) @@ -55,10 +78,70 @@ void givenUserIsNice_whenGetRestricted_thenOk() throws Exception { } @Test - void givenUserIsNotNicewhenGetRestricted_thenForbidden() throws Exception { + void givenUserIsNotNice_whenGetRestricted_thenForbidden() throws Exception { // @formatter:off api.perform(get("/restricted").with(SecurityMockMvcRequestPostProcessors.jwt().authorities(new SimpleGrantedAuthority("AUTHOR")))) .andExpect(status().isForbidden()); // @formatter:on } + + @ParameterizedTest + @ValueSource(strings = { "NICE", "VERY_NICE" }) + void givenUserIsGrantedWithAnyNiceAuthority_whenGetRestricted_thenOk(String authority) throws Exception { + // @formatter:off + api.perform(get("/restricted").with(SecurityMockMvcRequestPostProcessors.jwt().authorities(new SimpleGrantedAuthority(authority)))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.body").value("You are so nice!")); + // @formatter:on + } + + @ParameterizedTest + @JwtAuthenticationSource({ @WithMockJwtAuth("NICE"), @WithMockJwtAuth("VERY_NICE") }) + void givenUserIsGrantedWithAnyJwtAuthentication_whenGetRestricted_thenOk(@ParameterizedJwtAuth JwtAuthenticationToken auth) throws Exception { + // @formatter:off + api.perform(get("/restricted")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.body").value("You are so nice!")); + // @formatter:on + } + + @ParameterizedTest + @BearerAuthenticationSource({ @WithMockBearerTokenAuthentication("NICE"), @WithMockBearerTokenAuthentication("VERY_NICE") }) + void givenUserIsGrantedWithAnyBearerAuthentication_whenGetRestricted_thenOk(@ParameterizedBearerAuth BearerTokenAuthentication auth) throws Exception { + // @formatter:off + api.perform(get("/restricted")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.body").value("You are so nice!")); + // @formatter:on + } + + @ParameterizedTest + @OpenIdAuthenticationSource({ @OpenId("NICE"), @OpenId("VERY_NICE") }) + void givenUserIsGrantedWithAnyOAuthentication_whenGetRestricted_thenOk(@ParameterizedOpenId OAuthentication auth) throws Exception { + // @formatter:off + api.perform(get("/restricted")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.body").value("You are so nice!")); + // @formatter:on + } + + @ParameterizedTest + @OAuth2LoginAuthenticationSource({ @WithOAuth2Login("NICE"), @WithOAuth2Login("VERY_NICE") }) + void givenUserIsGrantedWithAnyOAuth2Login_whenGetRestricted_thenOk(@ParameterizedOAuth2Login OAuth2AuthenticationToken auth) throws Exception { + // @formatter:off + api.perform(get("/restricted")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.body").value("You are so nice!")); + // @formatter:on + } + + @ParameterizedTest + @OidcLoginAuthenticationSource({ @WithOidcLogin("NICE"), @WithOidcLogin("VERY_NICE") }) + void givenUserIsGrantedWithAnyOidcLogin_whenGetRestricted_thenOk(@ParameterizedOidcLogin OAuth2AuthenticationToken auth) throws Exception { + // @formatter:off + api.perform(get("/restricted")) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.body").value("You are so nice!")); + // @formatter:on + } } diff --git a/spring-addons-oauth2-test/pom.xml b/spring-addons-oauth2-test/pom.xml index 4d91dd39f..38b187826 100644 --- a/spring-addons-oauth2-test/pom.xml +++ b/spring-addons-oauth2-test/pom.xml @@ -51,6 +51,10 @@ org.apache.tomcat.embed tomcat-embed-core + + org.junit.jupiter + junit-jupiter-params + junit diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/AbstractAnnotatedAuthenticationBuilder.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/AbstractAnnotatedAuthenticationBuilder.java index b53f0ca53..13337c1e4 100644 --- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/AbstractAnnotatedAuthenticationBuilder.java +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/AbstractAnnotatedAuthenticationBuilder.java @@ -1,14 +1,13 @@ /* * Copyright 2020 Jérôme Wacongne. * - * 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 + * 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. + * 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 com.c4_soft.springaddons.security.oauth2.test.annotations; @@ -26,29 +25,28 @@ import com.c4_soft.springaddons.security.oauth2.test.OpenidClaimSetBuilder; -public abstract class AbstractAnnotatedAuthenticationBuilder - implements WithSecurityContextFactory { +public abstract class AbstractAnnotatedAuthenticationBuilder implements WithSecurityContextFactory { - protected abstract T authentication(A annotation); + protected abstract T authentication(A annotation); - @Override - public SecurityContext createSecurityContext(A annotation) { - final var context = SecurityContextHolder.createEmptyContext(); - context.setAuthentication(authentication(annotation)); + @Override + public SecurityContext createSecurityContext(A annotation) { + final var context = SecurityContextHolder.createEmptyContext(); + context.setAuthentication(authentication(annotation)); - return context; - } + return context; + } - public Set authorities(String... authorities) { - return Stream.of(authorities).map(SimpleGrantedAuthority::new).collect(Collectors.toSet()); - } + public Set authorities(String[] source1, String[] source2) { + return Stream.concat(Stream.of(source1), Stream.of(source2)).distinct().map(SimpleGrantedAuthority::new).collect(Collectors.toSet()); + } - public OpenidClaimSetBuilder claims(OpenIdClaims annotation) { - return OpenIdClaims.Builder.of(annotation).usernameClaim(annotation.usernameClaim()); - } + public OpenidClaimSetBuilder claims(OpenIdClaims annotation) { + return OpenIdClaims.Builder.of(annotation).usernameClaim(annotation.usernameClaim()); + } - @SuppressWarnings("unchecked") - protected T downcast() { - return (T) this; - } + @SuppressWarnings("unchecked") + protected T downcast() { + return (T) this; + } } \ No newline at end of file diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/Claims.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/Claims.java index 6a940b9a8..68777d0d5 100644 --- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/Claims.java +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/Claims.java @@ -1,14 +1,13 @@ /* * Copyright 2019 Jérôme Wacongne * - * 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 + * 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. + * 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 com.c4_soft.springaddons.security.oauth2.test.annotations; @@ -30,127 +29,126 @@ @Retention(RetentionPolicy.RUNTIME) public @interface Claims { - /** - * @return Claims containing JSON number to be parsed as Java int - */ - IntClaim[] intClaims() default {}; - - /** - * @return Claims containing JSON number to be parsed as Java long - */ - LongClaim[] longClaims() default {}; - - /** - * @return Claims containing JSON number to be parsed as Java double - */ - DoubleClaim[] doubleClaims() default {}; - - /** - * @return Claims containing JSON string to be parsed as Java String - */ - StringClaim[] stringClaims() default {}; - - /** - * @return Claims containing JSON string to be parsed as Java URI - */ - StringClaim[] uriClaims() default {}; - - /** - * @return Claims containing JSON string to be parsed as Java URL - */ - StringClaim[] urlClaims() default {}; - - /** - * @return Claims containing JSON number representing the number of seconds from - * 1970-01-01T00:00:00Z as measured in UTC to be parsed as Java Date - */ - IntClaim[] epochSecondClaims() default {}; - - /** - * @return Claims containing JSON string with "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" - * format to be parsed as Java Date. "epochSecondClaims" is generally be - * preferred to this representation (this is the case for OpenID claims - * like "exp", "iat", "auth_time" and "updated_at") - */ - StringClaim[] dateClaims() default {}; - - /** - * @return Claims containing JSON array to be parsed as Java String[] - */ - StringArrayClaim[] stringArrayClaims() default {}; - - /** - * @return Claims containing nested claim-sets defined with annotations - */ - NestedClaims[] nestedClaims() default {}; - - /** - * @return Claims to be parsed as nested Object using a JSON parser - */ - JsonObjectClaim[] jsonObjectClaims() default {}; - - /** - * @return Claims to be parsed as an array of nested Objects using a JSON parser - */ - JsonObjectArrayClaim[] jsonObjectArrayClaims() default {}; - - public static class Token { - private static final SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); - - private Token() { - } - - public static ModifiableClaimSet of(Claims annotation) { - final var claims = new ModifiableClaimSet(); - try { - for (final var claim : annotation.intClaims()) { - claims.claim(claim.name(), claim.value()); - } - for (final var claim : annotation.longClaims()) { - claims.claim(claim.name(), claim.value()); - } - for (final var claim : annotation.doubleClaims()) { - claims.claim(claim.name(), claim.value()); - } - for (final var claim : annotation.stringClaims()) { - claims.claim(claim.name(), claim.value()); - } - for (final var claim : annotation.uriClaims()) { - claims.claim(claim.name(), URI.create(claim.value())); - } - for (final var claim : annotation.urlClaims()) { - claims.claim(claim.name(), new URL(claim.value())); - } - for (final var claim : annotation.epochSecondClaims()) { - claims.claim(claim.name(), new Date(1000L * claim.value())); - } - for (final var claim : annotation.dateClaims()) { - claims.claim(claim.name(), isoFormat.parse(claim.value())); - } - for (final var claim : annotation.stringArrayClaims()) { - claims.claim(claim.name(), claim.value()); - } - for (final var claim : annotation.nestedClaims()) { - claims.claim(claim.name(), NestedClaims.Support.parse(claim)); - } - for (final var claim : annotation.jsonObjectClaims()) { - claims.claim(claim.name(), JsonObjectClaim.Support.parse(claim)); - } - for (final var claim : annotation.jsonObjectArrayClaims()) { - claims.claim(claim.name(), JsonObjectArrayClaim.Support.parse(claim)); - } - } catch (MalformedURLException | ParseException e) { - throw new MalformedTestClaimAnotation(e); - } - return claims; - } - - static class MalformedTestClaimAnotation extends RuntimeException { - public MalformedTestClaimAnotation(Throwable e) { - super(e); - } - } - - } + /** + * @return Claims containing JSON number to be parsed as Java int + */ + IntClaim[] intClaims() default {}; + + /** + * @return Claims containing JSON number to be parsed as Java long + */ + LongClaim[] longClaims() default {}; + + /** + * @return Claims containing JSON number to be parsed as Java double + */ + DoubleClaim[] doubleClaims() default {}; + + /** + * @return Claims containing JSON string to be parsed as Java String + */ + StringClaim[] stringClaims() default {}; + + /** + * @return Claims containing JSON string to be parsed as Java URI + */ + StringClaim[] uriClaims() default {}; + + /** + * @return Claims containing JSON string to be parsed as Java URL + */ + StringClaim[] urlClaims() default {}; + + /** + * @return Claims containing JSON number representing the number of seconds from 1970-01-01T00:00:00Z as measured in UTC to be parsed as Java Date + */ + IntClaim[] epochSecondClaims() default {}; + + /** + * @return Claims containing JSON string with "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" format to be parsed as Java Date. "epochSecondClaims" is generally be preferred + * to this representation (this is the case for OpenID claims like "exp", "iat", "auth_time" and "updated_at") + */ + StringClaim[] dateClaims() default {}; + + /** + * @return Claims containing JSON array to be parsed as Java String[] + */ + StringArrayClaim[] stringArrayClaims() default {}; + + /** + * @return Claims containing nested claim-sets defined with annotations + */ + NestedClaims[] nestedClaims() default {}; + + /** + * @return Claims to be parsed as nested Object using a JSON parser + */ + JsonObjectClaim[] jsonObjectClaims() default {}; + + /** + * @return Claims to be parsed as an array of nested Objects using a JSON parser + */ + JsonObjectArrayClaim[] jsonObjectArrayClaims() default {}; + + public static class Token { + private static final SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + + private Token() { + } + + public static ModifiableClaimSet of(Claims annotation) { + final var claims = new ModifiableClaimSet(); + try { + for (final var claim : annotation.intClaims()) { + claims.claim(claim.name(), claim.value()); + } + for (final var claim : annotation.longClaims()) { + claims.claim(claim.name(), claim.value()); + } + for (final var claim : annotation.doubleClaims()) { + claims.claim(claim.name(), claim.value()); + } + for (final var claim : annotation.stringClaims()) { + claims.claim(claim.name(), claim.value()); + } + for (final var claim : annotation.uriClaims()) { + claims.claim(claim.name(), URI.create(claim.value())); + } + for (final var claim : annotation.urlClaims()) { + claims.claim(claim.name(), new URL(claim.value())); + } + for (final var claim : annotation.epochSecondClaims()) { + claims.claim(claim.name(), new Date(1000L * claim.value())); + } + for (final var claim : annotation.dateClaims()) { + claims.claim(claim.name(), isoFormat.parse(claim.value())); + } + for (final var claim : annotation.stringArrayClaims()) { + claims.claim(claim.name(), claim.value()); + } + for (final var claim : annotation.nestedClaims()) { + claims.claim(claim.name(), NestedClaims.Support.parse(claim)); + } + for (final var claim : annotation.jsonObjectClaims()) { + claims.claim(claim.name(), JsonObjectClaim.Support.parse(claim)); + } + for (final var claim : annotation.jsonObjectArrayClaims()) { + claims.claim(claim.name(), JsonObjectArrayClaim.Support.parse(claim)); + } + } catch (MalformedURLException | ParseException e) { + throw new MalformedTestClaimAnotation(e); + } + return claims; + } + + static class MalformedTestClaimAnotation extends RuntimeException { + private static final long serialVersionUID = -7450332192058408179L; + + public MalformedTestClaimAnotation(Throwable e) { + super(e); + } + } + + } } diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/OpenId.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/OpenId.java index a5f524af2..dc6a0b586 100644 --- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/OpenId.java +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/OpenId.java @@ -1,14 +1,13 @@ /* * Copyright 2019 Jérôme Wacongne. * - * 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 + * 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. + * 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 com.c4_soft.springaddons.security.oauth2.test.annotations; @@ -28,8 +27,7 @@ import com.c4_soft.springaddons.security.oauth2.OpenidClaimSet; /** - * Annotation to setup test {@link SecurityContext} with an - * {@link OAuthentication}. Sample usage: + * Annotation to setup test {@link SecurityContext} with an {@link OAuthentication}. Sample usage: * *
  * @Test
@@ -56,28 +54,24 @@
 @WithSecurityContext(factory = OpenId.AuthenticationFactory.class)
 public @interface OpenId {
 
-    @AliasFor("authorities")
-    String[] value() default {};
+	@AliasFor("authorities")
+	String[] value() default {};
 
-    @AliasFor("value")
-    String[] authorities() default {};
+	@AliasFor("value")
+	String[] authorities() default {};
 
-    OpenIdClaims claims() default @OpenIdClaims();
+	OpenIdClaims claims() default @OpenIdClaims();
 
-    String bearerString() default "machin.truc.chose";
+	String bearerString() default "machin.truc.chose";
 
-    @AliasFor(annotation = WithSecurityContext.class)
-    TestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;
+	@AliasFor(annotation = WithSecurityContext.class)
+	TestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;
 
-    public static final class AuthenticationFactory
-            extends AbstractAnnotatedAuthenticationBuilder> {
-        @Override
-        public OAuthentication authentication(OpenId annotation) {
-            final var claims = super.claims(annotation.claims()).build();
-            return new OAuthentication<>(
-                    claims,
-                    super.authorities(annotation.authorities()),
-                    annotation.bearerString());
-        }
-    }
+	public static final class AuthenticationFactory extends AbstractAnnotatedAuthenticationBuilder> {
+		@Override
+		public OAuthentication authentication(OpenId annotation) {
+			final var claims = super.claims(annotation.claims()).build();
+			return new OAuthentication<>(claims, super.authorities(annotation.authorities(), annotation.value()), annotation.bearerString());
+		}
+	}
 }
diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithMockBearerTokenAuthentication.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithMockBearerTokenAuthentication.java
index f2ce4daba..b11c01a70 100644
--- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithMockBearerTokenAuthentication.java
+++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithMockBearerTokenAuthentication.java
@@ -1,14 +1,13 @@
 /*
  * Copyright 2019 Jérôme Wacongne.
  *
- * 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
+ * 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.
+ * 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 com.c4_soft.springaddons.security.oauth2.test.annotations;
 
@@ -29,8 +28,7 @@
 import org.springframework.security.test.context.support.WithSecurityContext;
 
 /**
- * Annotation to setup test {@link SecurityContext} with an
- * {@link BearerTokenAuthentication}. Sample usage:
+ * Annotation to setup test {@link SecurityContext} with an {@link BearerTokenAuthentication}. Sample usage:
  *
  * 
  * @Test
@@ -57,33 +55,32 @@
 @WithSecurityContext(factory = WithMockBearerTokenAuthentication.AuthenticationFactory.class)
 public @interface WithMockBearerTokenAuthentication {
 
-    @AliasFor("authorities")
-    String[] value() default {};
+	@AliasFor("authorities")
+	String[] value() default {};
 
-    @AliasFor("value")
-    String[] authorities() default {};
+	@AliasFor("value")
+	String[] authorities() default {};
 
-    OpenIdClaims attributes() default @OpenIdClaims();
+	OpenIdClaims attributes() default @OpenIdClaims();
 
-    String bearerString() default "machin.truc.chose";
+	String bearerString() default "machin.truc.chose";
 
-    @AliasFor(annotation = WithSecurityContext.class)
-    TestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;
+	@AliasFor(annotation = WithSecurityContext.class)
+	TestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;
 
-    public static final class AuthenticationFactory
-            extends
-            AbstractAnnotatedAuthenticationBuilder {
-        @Override
-        public BearerTokenAuthentication authentication(WithMockBearerTokenAuthentication annotation) {
-            final var claims = super.claims(annotation.attributes()).build();
-            final var authorities = super.authorities(annotation.authorities());
-            final var principal = new OAuth2IntrospectionAuthenticatedPrincipal(claims.getName(), claims, authorities);
-            final var credentials = new OAuth2AccessToken(
-                    OAuth2AccessToken.TokenType.BEARER,
-                    annotation.bearerString(),
-                    claims.getAsInstant(JwtClaimNames.IAT),
-                    claims.getAsInstant(JwtClaimNames.EXP));
-            return new BearerTokenAuthentication(principal, credentials, authorities);
-        }
-    }
+	public static final class AuthenticationFactory
+			extends AbstractAnnotatedAuthenticationBuilder {
+		@Override
+		public BearerTokenAuthentication authentication(WithMockBearerTokenAuthentication annotation) {
+			final var claims = super.claims(annotation.attributes()).build();
+			final var authorities = super.authorities(annotation.authorities(), annotation.value());
+			final var principal = new OAuth2IntrospectionAuthenticatedPrincipal(claims.getName(), claims, authorities);
+			final var credentials = new OAuth2AccessToken(
+					OAuth2AccessToken.TokenType.BEARER,
+					annotation.bearerString(),
+					claims.getAsInstant(JwtClaimNames.IAT),
+					claims.getAsInstant(JwtClaimNames.EXP));
+			return new BearerTokenAuthentication(principal, credentials, authorities);
+		}
+	}
 }
diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithMockJwtAuth.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithMockJwtAuth.java
index c139906b4..34c0fb2a8 100644
--- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithMockJwtAuth.java
+++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithMockJwtAuth.java
@@ -1,14 +1,13 @@
 /*
  * Copyright 2019 Jérôme Wacongne.
  *
- * 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
+ * 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.
+ * 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 com.c4_soft.springaddons.security.oauth2.test.annotations;
 
@@ -27,8 +26,7 @@
 import org.springframework.security.test.context.support.WithSecurityContext;
 
 /**
- * Annotation to setup test {@link SecurityContext} with an
- * {@link JwtAuthenticationToken}. Sample usage:
+ * Annotation to setup test {@link SecurityContext} with an {@link JwtAuthenticationToken}. Sample usage:
  *
  * 
  * @Test
@@ -49,31 +47,29 @@
 @WithSecurityContext(factory = WithMockJwtAuth.JwtAuthenticationTokenFactory.class)
 public @interface WithMockJwtAuth {
 
-    @AliasFor("authorities")
-    String[] value() default {};
+	@AliasFor("authorities")
+	String[] value() default {};
 
-    @AliasFor("value")
-    String[] authorities() default {};
+	@AliasFor("value")
+	String[] authorities() default {};
 
-    OpenIdClaims claims() default @OpenIdClaims();
+	OpenIdClaims claims() default @OpenIdClaims();
 
-    String tokenString() default "machin.truc.chose";
+	String tokenString() default "machin.truc.chose";
 
-    Claims headers() default @Claims(stringClaims = @StringClaim(name = "alg", value = "none"));
+	Claims headers() default @Claims(stringClaims = @StringClaim(name = "alg", value = "none"));
 
-    @AliasFor(annotation = WithSecurityContext.class)
-    TestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;
+	@AliasFor(annotation = WithSecurityContext.class)
+	TestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;
 
-    public static final class JwtAuthenticationTokenFactory
-            extends AbstractAnnotatedAuthenticationBuilder {
-        @Override
-        public JwtAuthenticationToken authentication(WithMockJwtAuth annotation) {
-            final var token = super.claims(annotation.claims()).build();
+	public static final class JwtAuthenticationTokenFactory extends AbstractAnnotatedAuthenticationBuilder {
+		@Override
+		public JwtAuthenticationToken authentication(WithMockJwtAuth annotation) {
+			final var token = super.claims(annotation.claims()).build();
 
-            final var jwt = new Jwt(annotation.tokenString(), token.getIssuedAt(), token.getExpiresAt(),
-                    Claims.Token.of(annotation.headers()), token);
+			final var jwt = new Jwt(annotation.tokenString(), token.getIssuedAt(), token.getExpiresAt(), Claims.Token.of(annotation.headers()), token);
 
-            return new JwtAuthenticationToken(jwt, super.authorities(annotation.authorities()), token.getName());
-        }
-    }
+			return new JwtAuthenticationToken(jwt, super.authorities(annotation.authorities(), annotation.value()), token.getName());
+		}
+	}
 }
diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithOAuth2Login.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithOAuth2Login.java
index 771aabfac..f0d17a097 100644
--- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithOAuth2Login.java
+++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithOAuth2Login.java
@@ -21,36 +21,34 @@
 @WithSecurityContext(factory = WithOAuth2Login.OAuth2AuthenticationTokenFactory.class)
 public @interface WithOAuth2Login {
 
-    @AliasFor("authorities")
-    String[] value() default {};
+	@AliasFor("authorities")
+	String[] value() default {};
 
-    @AliasFor("value")
-    String[] authorities() default {};
+	@AliasFor("value")
+	String[] authorities() default {};
 
-    OpenIdClaims claims() default @OpenIdClaims();
+	OpenIdClaims claims() default @OpenIdClaims();
 
-    String tokenString() default "machin.truc.chose";
+	String tokenString() default "machin.truc.chose";
 
-    String authorizedClientRegistrationId() default "bidule";
+	String authorizedClientRegistrationId() default "bidule";
 
-    /**
-     * @return the key used to access the user's "name" from claims. This
-     *         takes precedence over OpenIdClaims::usernameClaim if both are defined
-     */
-    String nameAttributeKey() default JwtClaimNames.SUB;
+	/**
+	 * @return the key used to access the user's "name" from claims. This takes precedence over OpenIdClaims::usernameClaim if both are defined
+	 */
+	String nameAttributeKey() default JwtClaimNames.SUB;
 
-    @AliasFor(annotation = WithSecurityContext.class)
-    TestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;
+	@AliasFor(annotation = WithSecurityContext.class)
+	TestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;
 
-    public static final class OAuth2AuthenticationTokenFactory
-            extends AbstractAnnotatedAuthenticationBuilder {
-        @Override
-        public OAuth2AuthenticationToken authentication(WithOAuth2Login annotation) {
-            final var token = super.claims(annotation.claims()).usernameClaim(annotation.nameAttributeKey()).build();
-            final var authorities = super.authorities(annotation.authorities());
-            final var principal = new DefaultOAuth2User(authorities, token, annotation.nameAttributeKey());
+	public static final class OAuth2AuthenticationTokenFactory extends AbstractAnnotatedAuthenticationBuilder {
+		@Override
+		public OAuth2AuthenticationToken authentication(WithOAuth2Login annotation) {
+			final var token = super.claims(annotation.claims()).usernameClaim(annotation.nameAttributeKey()).build();
+			final var authorities = super.authorities(annotation.authorities(), annotation.value());
+			final var principal = new DefaultOAuth2User(authorities, token, annotation.nameAttributeKey());
 
-            return new OAuth2AuthenticationToken(principal, authorities, annotation.authorizedClientRegistrationId());
-        }
-    }
+			return new OAuth2AuthenticationToken(principal, authorities, annotation.authorizedClientRegistrationId());
+		}
+	}
 }
diff --git a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithOidcLogin.java b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithOidcLogin.java
index c22259d11..0320d4e86 100644
--- a/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithOidcLogin.java
+++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/WithOidcLogin.java
@@ -22,37 +22,34 @@
 @WithSecurityContext(factory = WithOidcLogin.OAuth2AuthenticationTokenFactory.class)
 public @interface WithOidcLogin {
 
-    @AliasFor("authorities")
-    String[] value() default {};
+	@AliasFor("authorities")
+	String[] value() default {};
 
-    @AliasFor("value")
-    String[] authorities() default {};
+	@AliasFor("value")
+	String[] authorities() default {};
 
-    OpenIdClaims claims() default @OpenIdClaims();
+	OpenIdClaims claims() default @OpenIdClaims();
 
-    String tokenString() default "machin.truc.chose";
+	String tokenString() default "machin.truc.chose";
 
-    String authorizedClientRegistrationId() default "bidule";
+	String authorizedClientRegistrationId() default "bidule";
 
-    /**
-     * @return the key used to access the user's "name" from claims. This
-     *         takes precedence over OpenIdClaims::usernameClaim if both are defined
-     */
-    String nameAttributeKey() default StandardClaimNames.SUB;
+	/**
+	 * @return the key used to access the user's "name" from claims. This takes precedence over OpenIdClaims::usernameClaim if both are defined
+	 */
+	String nameAttributeKey() default StandardClaimNames.SUB;
 
-    @AliasFor(annotation = WithSecurityContext.class)
-    TestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;
+	@AliasFor(annotation = WithSecurityContext.class)
+	TestExecutionEvent setupBefore() default TestExecutionEvent.TEST_METHOD;
 
-    public static final class OAuth2AuthenticationTokenFactory
-            extends AbstractAnnotatedAuthenticationBuilder {
-        @Override
-        public OAuth2AuthenticationToken authentication(WithOidcLogin annotation) {
-            final var token = super.claims(annotation.claims()).usernameClaim(annotation.nameAttributeKey()).build();
-            final var authorities = super.authorities(annotation.authorities());
-            final var principal = new DefaultOidcUser(authorities,
-                    new OidcIdToken(annotation.tokenString(), token.getIssuedAt(), token.getExpiresAt(), token));
+	public static final class OAuth2AuthenticationTokenFactory extends AbstractAnnotatedAuthenticationBuilder {
+		@Override
+		public OAuth2AuthenticationToken authentication(WithOidcLogin annotation) {
+			final var token = super.claims(annotation.claims()).usernameClaim(annotation.nameAttributeKey()).build();
+			final var authorities = super.authorities(annotation.authorities(), annotation.value());
+			final var principal = new DefaultOidcUser(authorities, new OidcIdToken(annotation.tokenString(), token.getIssuedAt(), token.getExpiresAt(), token));
 
-            return new OAuth2AuthenticationToken(principal, authorities, annotation.authorizedClientRegistrationId());
-        }
-    }
+			return new OAuth2AuthenticationToken(principal, authorities, annotation.authorizedClientRegistrationId());
+		}
+	}
 }
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
new file mode 100644
index 000000000..e3de94753
--- /dev/null
+++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/BearerAuthenticationSource.java
@@ -0,0 +1,57 @@
+package com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Collection;
+import java.util.stream.Stream;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.ArgumentsProvider;
+import org.junit.jupiter.params.provider.ArgumentsSource;
+import org.junit.jupiter.params.support.AnnotationConsumer;
+import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication;
+
+import com.c4_soft.springaddons.security.oauth2.test.annotations.WithMockBearerTokenAuthentication;
+
+/**
+ * 

+ * Define the different {@link BearerTokenAuthentication} instances to run each of JUnit 5 @ParameterizedTest with. + *

+ * Usage: + * + *
+ * @BearerAuthenticationSource({ @WithMockBearerTokenAuthentication("NICE"), @WithMockBearerTokenAuthentication("VERY_NICE") })
+ * void test(@ParameterizedBearerAuth BearerTokenAuthentication auth) throws Exception {
+ *     ...
+ * }
+ * 
+ * + * @author Jerome Wacongne ch4mp@c4-soft.com + * @see ParameterizedBearerAuth + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@ArgumentsSource(BearerAuthenticationSource.AuthenticationProvider.class) +public @interface BearerAuthenticationSource { + WithMockBearerTokenAuthentication[] value() default {}; + + static class AuthenticationProvider implements ArgumentsProvider, AnnotationConsumer { + private final WithMockBearerTokenAuthentication.AuthenticationFactory authFactory = new WithMockBearerTokenAuthentication.AuthenticationFactory(); + + private Collection arguments; + + @Override + public void accept(BearerAuthenticationSource source) { + arguments = Stream.of(source.value()).map(authFactory::authentication).toList(); + } + + @Override + public Stream provideArguments(ExtensionContext context) { + return arguments.stream().map(Arguments::of); + } + + } +} 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 new file mode 100644 index 000000000..1e06c1d7b --- /dev/null +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/JwtAuthenticationSource.java @@ -0,0 +1,57 @@ +package com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Collection; +import java.util.stream.Stream; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.jupiter.params.support.AnnotationConsumer; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; + +import com.c4_soft.springaddons.security.oauth2.test.annotations.WithMockJwtAuth; + +/** + *

+ * Define the different {@link JwtAuthenticationToken} instances to run each of JUnit 5 @ParameterizedTest with. + *

+ * Usage: + * + *
+ * @JwtAuthenticationSource({ @WithMockJwtAuth("NICE"), @WithMockJwtAuth("VERY_NICE") })
+ * void test(@ParameterizedJwtAuth JwtAuthenticationToken auth) throws Exception {
+ *     ...
+ * }
+ * 
+ * + * @author Jerome Wacongne ch4mp@c4-soft.com + * @see ParameterizedJwtAuth + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@ArgumentsSource(JwtAuthenticationSource.AuthenticationProvider.class) +public @interface JwtAuthenticationSource { + WithMockJwtAuth[] value() default {}; + + static class AuthenticationProvider implements ArgumentsProvider, AnnotationConsumer { + private final WithMockJwtAuth.JwtAuthenticationTokenFactory authFactory = new WithMockJwtAuth.JwtAuthenticationTokenFactory(); + + private Collection arguments; + + @Override + public void accept(JwtAuthenticationSource source) { + arguments = Stream.of(source.value()).map(authFactory::authentication).toList(); + } + + @Override + public Stream provideArguments(ExtensionContext context) { + return arguments.stream().map(Arguments::of); + } + + } +} 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 new file mode 100644 index 000000000..2e1577d99 --- /dev/null +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OAuth2LoginAuthenticationSource.java @@ -0,0 +1,57 @@ +package com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Collection; +import java.util.stream.Stream; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.jupiter.params.support.AnnotationConsumer; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; + +import com.c4_soft.springaddons.security.oauth2.test.annotations.WithOAuth2Login; + +/** + *

+ * Define the different {@link OAuth2AuthenticationToken} instances to run each of JUnit 5 @ParameterizedTest with. + *

+ * Usage: + * + *
+ * @OAuth2LoginAuthenticationSource({ @WithOAuth2Login("NICE"), @WithOAuth2Login("VERY_NICE") })
+ * void test(@ParameterizedOAuth2Login OAuth2AuthenticationToken auth) throws Exception {
+ *     ...
+ * }
+ * 
+ * + * @author Jerome Wacongne ch4mp@c4-soft.com + * @see ParameterizedOAuth2Login + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@ArgumentsSource(OAuth2LoginAuthenticationSource.AuthenticationProvider.class) +public @interface OAuth2LoginAuthenticationSource { + WithOAuth2Login[] value() default {}; + + static class AuthenticationProvider implements ArgumentsProvider, AnnotationConsumer { + private final WithOAuth2Login.OAuth2AuthenticationTokenFactory authFactory = new WithOAuth2Login.OAuth2AuthenticationTokenFactory(); + + private Collection arguments; + + @Override + public void accept(OAuth2LoginAuthenticationSource source) { + arguments = Stream.of(source.value()).map(authFactory::authentication).toList(); + } + + @Override + public Stream provideArguments(ExtensionContext context) { + return arguments.stream().map(Arguments::of); + } + + } +} 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 new file mode 100644 index 000000000..fba6afd8a --- /dev/null +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OidcLoginAuthenticationSource.java @@ -0,0 +1,57 @@ +package com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Collection; +import java.util.stream.Stream; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.jupiter.params.support.AnnotationConsumer; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; + +import com.c4_soft.springaddons.security.oauth2.test.annotations.WithOidcLogin; + +/** + *

+ * Define the different {@link OAuth2AuthenticationToken} instances to run each of JUnit 5 @ParameterizedTest with. + *

+ * Usage: + * + *
+ * @OidcLoginAuthenticationSource({ @WithOidcLogin("NICE"), @WithOidcLogin("VERY_NICE") })
+ * void test(@ParameterizedOidcLogin OAuth2AuthenticationToken auth) throws Exception {
+ *     ...
+ * }
+ * 
+ * + * @author Jerome Wacongne ch4mp@c4-soft.com + * @see ParameterizedOidcLogin + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@ArgumentsSource(OidcLoginAuthenticationSource.AuthenticationProvider.class) +public @interface OidcLoginAuthenticationSource { + WithOidcLogin[] value() default {}; + + static class AuthenticationProvider implements ArgumentsProvider, AnnotationConsumer { + private final WithOidcLogin.OAuth2AuthenticationTokenFactory authFactory = new WithOidcLogin.OAuth2AuthenticationTokenFactory(); + + private Collection arguments; + + @Override + public void accept(OidcLoginAuthenticationSource source) { + arguments = Stream.of(source.value()).map(authFactory::authentication).toList(); + } + + @Override + public Stream provideArguments(ExtensionContext context) { + return arguments.stream().map(Arguments::of); + } + + } +} 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 new file mode 100644 index 000000000..69ac3ed09 --- /dev/null +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/OpenIdAuthenticationSource.java @@ -0,0 +1,58 @@ +package com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.util.Collection; +import java.util.stream.Stream; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.ArgumentsProvider; +import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.jupiter.params.support.AnnotationConsumer; + +import com.c4_soft.springaddons.security.oauth2.OAuthentication; +import com.c4_soft.springaddons.security.oauth2.OpenidClaimSet; +import com.c4_soft.springaddons.security.oauth2.test.annotations.OpenId; + +/** + *

+ * Define the different {@link OAuthentication OAuthentication<OpenidClaimSet>} instances to run each of JUnit 5 @ParameterizedTest with. + *

+ * Usage: + * + *
+ * @OpenIdAuthenticationSource({ @OpenId("NICE"), @OpenId("VERY_NICE") })
+ * void test(@ParameterizedOpenId OAuthentication<OpenidClaimSet> auth) throws Exception {
+ *     ...
+ * }
+ * 
+ * + * @author Jerome Wacongne ch4mp@c4-soft.com + * @see ParameterizedOpenId + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD }) +@Retention(RetentionPolicy.RUNTIME) +@ArgumentsSource(OpenIdAuthenticationSource.AuthenticationProvider.class) +public @interface OpenIdAuthenticationSource { + OpenId[] value() default {}; + + static class AuthenticationProvider implements ArgumentsProvider, AnnotationConsumer { + private final OpenId.AuthenticationFactory authFactory = new OpenId.AuthenticationFactory(); + + private Collection> arguments; + + @Override + public void accept(OpenIdAuthenticationSource source) { + arguments = Stream.of(source.value()).map(authFactory::authentication).toList(); + } + + @Override + public Stream provideArguments(ExtensionContext context) { + return arguments.stream().map(Arguments::of); + } + + } +} 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 new file mode 100644 index 000000000..5a45b8c91 --- /dev/null +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedBearerAuth.java @@ -0,0 +1,49 @@ +package com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.params.converter.ConvertWith; +import org.junit.jupiter.params.converter.TypedArgumentConverter; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.server.resource.authentication.BearerTokenAuthentication; + +/** + *

+ * Shortcut for {@link ConvertWith @ConvertWith(ParameterizedBearerAuth.AuthenticationArgumentProcessor.class)}, which populates the passed + * @ParameterizedTest parameter. + *

+ * Usage: + * + *
+ * @BearerAuthenticationSource({ @WithMockBearerTokenAuthentication("NICE"), @WithMockBearerTokenAuthentication("VERY_NICE") })
+ * void test(@ParameterizedBearerAuth BearerTokenAuthentication auth) throws Exception {
+ *     ...
+ * }
+ * 
+ * + * @author Jerome Wacongne ch4mp@c4-soft.com + * @see BearerAuthenticationSource + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +@ConvertWith(ParameterizedBearerAuth.AuthenticationArgumentProcessor.class) +public @interface ParameterizedBearerAuth { + + static class AuthenticationArgumentProcessor extends TypedArgumentConverter { + + protected AuthenticationArgumentProcessor() { + super(BearerTokenAuthentication.class, BearerTokenAuthentication.class); + } + + @Override + protected BearerTokenAuthentication convert(BearerTokenAuthentication source) { + SecurityContextHolder.getContext().setAuthentication(source); + + return source; + } + + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..9826600ba --- /dev/null +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedJwtAuth.java @@ -0,0 +1,49 @@ +package com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.params.converter.ConvertWith; +import org.junit.jupiter.params.converter.TypedArgumentConverter; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; + +/** + *

+ * Shortcut for {@link ConvertWith @ConvertWith(ParameterizedJwtAuth.AuthenticationArgumentProcessor.class)}, which populates the passed + * @ParameterizedTest parameter. + *

+ * Usage: + * + *
+ * @JwtAuthenticationSource({ @WithMockJwtAuth("NICE"), @WithMockJwtAuth("VERY_NICE") })
+ * void test(@ParameterizedJwtAuth JwtAuthenticationToken auth) throws Exception {
+ *     ...
+ * }
+ * 
+ * + * @author Jerome Wacongne ch4mp@c4-soft.com + * @see JwtAuthenticationSource + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +@ConvertWith(ParameterizedJwtAuth.AuthenticationArgumentProcessor.class) +public @interface ParameterizedJwtAuth { + + static class AuthenticationArgumentProcessor extends TypedArgumentConverter { + + protected AuthenticationArgumentProcessor() { + super(JwtAuthenticationToken.class, JwtAuthenticationToken.class); + } + + @Override + protected JwtAuthenticationToken convert(JwtAuthenticationToken source) { + SecurityContextHolder.getContext().setAuthentication(source); + + return source; + } + + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..e6ded63bb --- /dev/null +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOAuth2Login.java @@ -0,0 +1,49 @@ +package com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.params.converter.ConvertWith; +import org.junit.jupiter.params.converter.TypedArgumentConverter; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; + +/** + *

+ * Shortcut for {@link ConvertWith @ConvertWith(ParameterizedOAuth2Login.AuthenticationArgumentProcessor.class)}, which populates the passed + * @ParameterizedTest parameter. + *

+ * Usage: + * + *
+ * @OAuth2LoginAuthenticationSource({ @WithOAuth2Login("NICE"), @WithOAuth2Login("VERY_NICE") })
+ * void test(@ParameterizedOAuth2Login OAuth2AuthenticationToken auth) throws Exception {
+ *     ...
+ * }
+ * 
+ * + * @author Jerome Wacongne ch4mp@c4-soft.com + * @see OAuth2LoginAuthenticationSource + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +@ConvertWith(ParameterizedOAuth2Login.AuthenticationArgumentProcessor.class) +public @interface ParameterizedOAuth2Login { + + static class AuthenticationArgumentProcessor extends TypedArgumentConverter { + + protected AuthenticationArgumentProcessor() { + super(OAuth2AuthenticationToken.class, OAuth2AuthenticationToken.class); + } + + @Override + protected OAuth2AuthenticationToken convert(OAuth2AuthenticationToken source) { + SecurityContextHolder.getContext().setAuthentication(source); + + return source; + } + + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..9401ba3df --- /dev/null +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOidcLogin.java @@ -0,0 +1,49 @@ +package com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.params.converter.ConvertWith; +import org.junit.jupiter.params.converter.TypedArgumentConverter; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; + +/** + *

+ * Shortcut for {@link ConvertWith @ConvertWith(ParameterizedOidcLogin.AuthenticationArgumentProcessor.class)}, which populates the passed + * @ParameterizedTest parameter. + *

+ * Usage: + * + *
+ * @OidcLoginAuthenticationSource({ @WithOidcLogin("NICE"), @WithOidcLogin("VERY_NICE") })
+ * void test(@ParameterizedOidcLogin OAuth2AuthenticationToken auth) throws Exception {
+ *     ...
+ * }
+ * 
+ * + * @author Jerome Wacongne ch4mp@c4-soft.com + * @see OidcLoginAuthenticationSource + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +@ConvertWith(ParameterizedOidcLogin.AuthenticationArgumentProcessor.class) +public @interface ParameterizedOidcLogin { + + static class AuthenticationArgumentProcessor extends TypedArgumentConverter { + + protected AuthenticationArgumentProcessor() { + super(OAuth2AuthenticationToken.class, OAuth2AuthenticationToken.class); + } + + @Override + protected OAuth2AuthenticationToken convert(OAuth2AuthenticationToken source) { + SecurityContextHolder.getContext().setAuthentication(source); + + return source; + } + + } +} \ No newline at end of file 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 new file mode 100644 index 000000000..0ef2a155c --- /dev/null +++ b/spring-addons-oauth2-test/src/main/java/com/c4_soft/springaddons/security/oauth2/test/annotations/parameterized/ParameterizedOpenId.java @@ -0,0 +1,59 @@ +package com.c4_soft.springaddons.security.oauth2.test.annotations.parameterized; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.junit.jupiter.params.converter.ConvertWith; +import org.junit.jupiter.params.converter.TypedArgumentConverter; +import org.springframework.security.core.context.SecurityContextHolder; + +import com.c4_soft.springaddons.security.oauth2.OAuthentication; +import com.c4_soft.springaddons.security.oauth2.OpenidClaimSet; + +/** + *

+ * Shortcut for {@link ConvertWith @ConvertWith(ParameterizedOpenId.AuthenticationArgumentProcessor.class)}, which populates the passed + * @ParameterizedTest parameter. + *

+ * Usage: + * + *
+ * @OpenIdAuthenticationSource({ @OpenId("NICE"), @OpenId("VERY_NICE") })
+ * void test(@ParameterizedOpenId OAuthentication<OpenidClaimSet> auth) throws Exception {
+ *     ...
+ * }
+ * 
+ * + * @author Jerome Wacongne ch4mp@c4-soft.com + * @see OpenIdAuthenticationSource + */ +@Target({ ElementType.ANNOTATION_TYPE, ElementType.PARAMETER }) +@Retention(RetentionPolicy.RUNTIME) +@ConvertWith(ParameterizedOpenId.AuthenticationArgumentProcessor.class) +public @interface ParameterizedOpenId { + + @SuppressWarnings("unchecked") + static class AuthenticationArgumentProcessor extends TypedArgumentConverter, OAuthentication> { + private static Class> clazz; + static { + try { + clazz = (Class>) Class.forName(OAuthentication.class.getName()); + } catch (ClassNotFoundException e) { + } + } + + protected AuthenticationArgumentProcessor() { + super(clazz, clazz); + } + + @Override + protected OAuthentication convert(OAuthentication source) { + SecurityContextHolder.getContext().setAuthentication(source); + + return source; + } + + } +} \ No newline at end of file