diff --git a/module/spring-boot-security-oauth2-resource-server/build.gradle b/module/spring-boot-security-oauth2-resource-server/build.gradle index bd98ca47339..257e5b79845 100644 --- a/module/spring-boot-security-oauth2-resource-server/build.gradle +++ b/module/spring-boot-security-oauth2-resource-server/build.gradle @@ -47,6 +47,12 @@ dependencies { testImplementation(testFixtures(project(":core:spring-boot-autoconfigure"))) testImplementation("com.squareup.okhttp3:mockwebserver") + testCompileOnly("com.google.code.findbugs:jsr305") + testRuntimeOnly("ch.qos.logback:logback-classic") testRuntimeOnly("org.springframework:spring-webflux") } + +tasks.named("compileTestJava") { + options.nullability.checking = "tests" +} diff --git a/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java b/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java index a02896a214b..8d708825a9a 100644 --- a/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java +++ b/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java @@ -39,6 +39,7 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.assertj.core.api.InstanceOfAssertFactories; import org.assertj.core.api.ThrowingConsumer; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -87,6 +88,7 @@ import org.springframework.security.web.server.MatcherSecurityWebFilterChain; import org.springframework.security.web.server.SecurityWebFilterChain; import org.springframework.security.web.server.authentication.AuthenticationWebFilter; import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.web.server.ServerWebExchange; import org.springframework.web.server.WebFilter; import static org.assertj.core.api.Assertions.assertThat; @@ -112,7 +114,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { .withConfiguration(AutoConfigurations.of(ReactiveOAuth2ResourceServerAutoConfiguration.class)) .withUserConfiguration(TestConfig.class); - private MockWebServer server; + private @Nullable MockWebServer server; private static final Duration TIMEOUT = Duration.ofSeconds(5000000); @@ -236,6 +238,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { SupplierReactiveJwtDecoder supplierReactiveJwtDecoder = context.getBean(SupplierReactiveJwtDecoder.class); Mono reactiveJwtDecoderSupplier = (Mono) ReflectionTestUtils .getField(supplierReactiveJwtDecoder, "jwtDecoderMono"); + assertThat(reactiveJwtDecoderSupplier).isNotNull(); try { reactiveJwtDecoderSupplier.flatMap((decoder) -> decoder.decode("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9." + "eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0." @@ -538,7 +541,9 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { SupplierReactiveJwtDecoder supplierJwtDecoderBean = context.getBean(SupplierReactiveJwtDecoder.class); Mono jwtDecoderSupplier = (Mono) ReflectionTestUtils .getField(supplierJwtDecoderBean, "jwtDecoderMono"); + assertThat(jwtDecoderSupplier).isNotNull(); ReactiveJwtDecoder jwtDecoder = jwtDecoderSupplier.block(); + assertThat(jwtDecoder).isNotNull(); validate( jwt().claim("iss", URI.create(issuerUri).toURL()) .claim("aud", List.of("https://test-audience.com")), @@ -612,6 +617,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { ReactiveJwtDecoder jwtDecoder = context.getBean(ReactiveJwtDecoder.class); DelegatingOAuth2TokenValidator jwtValidator = (DelegatingOAuth2TokenValidator) ReflectionTestUtils .getField(jwtDecoder, "jwtValidator"); + assertThat(jwtValidator).isNotNull(); Jwt jwt = jwt().claim("iss", new URL(issuerUri)) .claim("aud", Collections.singletonList("https://other-audience.com")) .build(); @@ -638,6 +644,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { ReactiveJwtDecoder jwtDecoder = context.getBean(ReactiveJwtDecoder.class); DelegatingOAuth2TokenValidator jwtValidator = (DelegatingOAuth2TokenValidator) ReflectionTestUtils .getField(jwtDecoder, "jwtValidator"); + assertThat(jwtValidator).isNotNull(); Jwt jwt = jwt().claim("iss", new URL(issuerUri)).claim("custom_claim", "invalid_value").build(); assertThat(jwtValidator.validate(jwt).hasErrors()).isTrue(); }); @@ -720,6 +727,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { .doesNotHaveBean(ReactiveManagementWebSecurityAutoConfiguration.class)); } + @SuppressWarnings("unchecked") private void assertFilterConfiguredWithJwtAuthenticationManager(AssertableReactiveWebApplicationContext context) { MatcherSecurityWebFilterChain filterChain = (MatcherSecurityWebFilterChain) context .getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN); @@ -728,12 +736,16 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { .filter((f) -> f instanceof AuthenticationWebFilter) .findFirst() .orElse(null); - ReactiveAuthenticationManagerResolver authenticationManagerResolver = (ReactiveAuthenticationManagerResolver) ReflectionTestUtils + assertThat(webFilter).isNotNull(); + ReactiveAuthenticationManagerResolver authenticationManagerResolver = (ReactiveAuthenticationManagerResolver) ReflectionTestUtils .getField(webFilter, "authenticationManagerResolver"); - Object authenticationManager = authenticationManagerResolver.resolve(null).block(TIMEOUT); + assertThat(authenticationManagerResolver).isNotNull(); + Object authenticationManager = authenticationManagerResolver.resolve(mock(ServerWebExchange.class)) + .block(TIMEOUT); assertThat(authenticationManager).isInstanceOf(JwtReactiveAuthenticationManager.class); } + @SuppressWarnings("unchecked") private void assertFilterConfiguredWithOpaqueTokenAuthenticationManager( AssertableReactiveWebApplicationContext context) { MatcherSecurityWebFilterChain filterChain = (MatcherSecurityWebFilterChain) context @@ -743,9 +755,12 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { .filter((f) -> f instanceof AuthenticationWebFilter) .findFirst() .orElse(null); - ReactiveAuthenticationManagerResolver authenticationManagerResolver = (ReactiveAuthenticationManagerResolver) ReflectionTestUtils + assertThat(webFilter).isNotNull(); + ReactiveAuthenticationManagerResolver authenticationManagerResolver = (ReactiveAuthenticationManagerResolver) ReflectionTestUtils .getField(webFilter, "authenticationManagerResolver"); - Object authenticationManager = authenticationManagerResolver.resolve(null).block(TIMEOUT); + assertThat(authenticationManagerResolver).isNotNull(); + Object authenticationManager = authenticationManagerResolver.resolve(mock(ServerWebExchange.class)) + .block(TIMEOUT); assertThat(authenticationManager).isInstanceOf(OpaqueTokenReactiveAuthenticationManager.class); } @@ -760,12 +775,14 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { MockResponse mockResponse = new MockResponse().setResponseCode(HttpStatus.OK.value()) .setBody(new ObjectMapper().writeValueAsString(getResponse(issuer))) .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + assertThat(this.server).isNotNull(); this.server.enqueue(mockResponse); this.server.enqueue( new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json").setBody(JWK_SET)); } private void setupMockResponsesWithErrors(String issuer, int errorResponseCount) { + assertThat(this.server).isNotNull(); for (int i = 0; i < errorResponseCount; i++) { MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value()); this.server.enqueue(emptyResponse); @@ -808,6 +825,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { ThrowingConsumer>> validatorsConsumer) { DelegatingOAuth2TokenValidator jwtValidator = (DelegatingOAuth2TokenValidator) ReflectionTestUtils .getField(jwtDecoder, "jwtValidator"); + assertThat(jwtValidator).isNotNull(); assertThat(jwtValidator.validate(builder.build()).hasErrors()).isFalse(); validatorsConsumer.accept(extractValidators(jwtValidator)); } @@ -816,6 +834,7 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { private List> extractValidators(DelegatingOAuth2TokenValidator delegatingValidator) { Collection> delegates = (Collection>) ReflectionTestUtils .getField(delegatingValidator, "tokenValidators"); + assertThat(delegates).isNotNull(); List> extracted = new ArrayList<>(); for (OAuth2TokenValidator delegate : delegates) { if (delegate instanceof DelegatingOAuth2TokenValidator delegatingDelegate) { diff --git a/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerAutoConfigurationTests.java b/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerAutoConfigurationTests.java index 08021f4afb0..ca6518eee03 100644 --- a/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerAutoConfigurationTests.java +++ b/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerAutoConfigurationTests.java @@ -38,6 +38,7 @@ import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.assertj.core.api.InstanceOfAssertFactories; import org.assertj.core.api.ThrowingConsumer; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -105,7 +106,7 @@ class OAuth2ResourceServerAutoConfigurationTests { .withConfiguration(AutoConfigurations.of(OAuth2ResourceServerAutoConfiguration.class)) .withUserConfiguration(TestConfig.class); - private MockWebServer server; + private @Nullable MockWebServer server; private static final String JWK_SET = "{\"keys\":[{\"kty\":\"RSA\",\"e\":\"AQAB\",\"use\":\"sig\"," + "\"kid\":\"one\",\"n\":\"oXJ8OyOv_eRnce4akdanR4KYRfnC2zLV4uYNQpcFn6oHL0dj7D6kxQmsXoYgJV8ZVDn71KGm" @@ -228,6 +229,7 @@ class OAuth2ResourceServerAutoConfigurationTests { SupplierJwtDecoder supplierJwtDecoderBean = context.getBean(SupplierJwtDecoder.class); Supplier jwtDecoderSupplier = (Supplier) ReflectionTestUtils .getField(supplierJwtDecoderBean, "delegate"); + assertThat(jwtDecoderSupplier).isNotNull(); jwtDecoderSupplier.get(); assertJwkSetUriJwtDecoderBuilderCustomization(context); }); @@ -253,6 +255,7 @@ class OAuth2ResourceServerAutoConfigurationTests { SupplierJwtDecoder supplierJwtDecoderBean = context.getBean(SupplierJwtDecoder.class); Supplier jwtDecoderSupplier = (Supplier) ReflectionTestUtils .getField(supplierJwtDecoderBean, "delegate"); + assertThat(jwtDecoderSupplier).isNotNull(); jwtDecoderSupplier.get(); assertJwkSetUriJwtDecoderBuilderCustomization(context); }); @@ -279,6 +282,7 @@ class OAuth2ResourceServerAutoConfigurationTests { SupplierJwtDecoder supplierJwtDecoderBean = context.getBean(SupplierJwtDecoder.class); Supplier jwtDecoderSupplier = (Supplier) ReflectionTestUtils .getField(supplierJwtDecoderBean, "delegate"); + assertThat(jwtDecoderSupplier).isNotNull(); jwtDecoderSupplier.get(); assertJwkSetUriJwtDecoderBuilderCustomization(context); }); @@ -558,6 +562,7 @@ class OAuth2ResourceServerAutoConfigurationTests { SupplierJwtDecoder supplierJwtDecoderBean = context.getBean(SupplierJwtDecoder.class); Supplier jwtDecoderSupplier = (Supplier) ReflectionTestUtils .getField(supplierJwtDecoderBean, "delegate"); + assertThat(jwtDecoderSupplier).isNotNull(); JwtDecoder jwtDecoder = jwtDecoderSupplier.get(); validate( jwt().claim("iss", URI.create(issuerUri).toURL()) @@ -584,6 +589,7 @@ class OAuth2ResourceServerAutoConfigurationTests { SupplierJwtDecoder supplierJwtDecoderBean = context.getBean(SupplierJwtDecoder.class); Supplier jwtDecoderSupplier = (Supplier) ReflectionTestUtils .getField(supplierJwtDecoderBean, "delegate"); + assertThat(jwtDecoderSupplier).isNotNull(); JwtDecoder jwtDecoder = jwtDecoderSupplier.get(); assertThat(context).hasBean("customJwtClaimValidator"); OAuth2TokenValidator customValidator = (OAuth2TokenValidator) context @@ -633,6 +639,7 @@ class OAuth2ResourceServerAutoConfigurationTests { JwtDecoder jwtDecoder = context.getBean(JwtDecoder.class); DelegatingOAuth2TokenValidator jwtValidator = (DelegatingOAuth2TokenValidator) ReflectionTestUtils .getField(jwtDecoder, "jwtValidator"); + assertThat(jwtValidator).isNotNull(); Jwt jwt = jwt().claim("iss", new URL(issuerUri)) .claim("aud", Collections.singletonList("https://other-audience.com")) .build(); @@ -733,7 +740,7 @@ class OAuth2ResourceServerAutoConfigurationTests { .doesNotHaveBean(MANAGEMENT_SECURITY_FILTER_CHAIN_BEAN)); } - private Filter getBearerTokenFilter(AssertableWebApplicationContext context) { + private @Nullable Filter getBearerTokenFilter(AssertableWebApplicationContext context) { FilterChainProxy filterChain = (FilterChainProxy) context.getBean(BeanIds.SPRING_SECURITY_FILTER_CHAIN); List filterChains = filterChain.getFilterChains(); List filters = filterChains.get(0).getFilters(); @@ -751,12 +758,14 @@ class OAuth2ResourceServerAutoConfigurationTests { MockResponse mockResponse = new MockResponse().setResponseCode(HttpStatus.OK.value()) .setBody(new ObjectMapper().writeValueAsString(getResponse(issuer))) .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + assertThat(this.server).isNotNull(); this.server.enqueue(mockResponse); this.server.enqueue( new MockResponse().setResponseCode(200).setHeader("Content-Type", "application/json").setBody(JWK_SET)); } private void setupMockResponsesWithErrors(String issuer, int errorResponseCount) { + assertThat(this.server).isNotNull(); for (int i = 0; i < errorResponseCount; i++) { MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value()); this.server.enqueue(emptyResponse); @@ -799,6 +808,7 @@ class OAuth2ResourceServerAutoConfigurationTests { ThrowingConsumer>> validatorsConsumer) { DelegatingOAuth2TokenValidator jwtValidator = (DelegatingOAuth2TokenValidator) ReflectionTestUtils .getField(jwtDecoder, "jwtValidator"); + assertThat(jwtValidator).isNotNull(); assertThat(jwtValidator.validate(builder.build()).hasErrors()).isFalse(); validatorsConsumer.accept(extractValidators(jwtValidator)); } @@ -807,6 +817,7 @@ class OAuth2ResourceServerAutoConfigurationTests { private List> extractValidators(DelegatingOAuth2TokenValidator delegatingValidator) { Collection> delegates = (Collection>) ReflectionTestUtils .getField(delegatingValidator, "tokenValidators"); + assertThat(delegates).isNotNull(); List> extracted = new ArrayList<>(); for (OAuth2TokenValidator delegate : delegates) { if (delegate instanceof DelegatingOAuth2TokenValidator delegatingDelegate) {