Enable Null checking in spring-security-test via JSpecify

Closes gh-17840
This commit is contained in:
Rob Winch 2025-09-03 12:59:46 -05:00
parent 194be8ffb6
commit 6a84f96930
No known key found for this signature in database
19 changed files with 324 additions and 44 deletions

View File

@ -1,3 +1,7 @@
plugins {
id 'security-nullability'
}
apply plugin: 'io.spring.convention.spring-module'
dependencies {

View File

@ -16,6 +16,8 @@
package org.springframework.security.test.aot.hint;
import org.jspecify.annotations.Nullable;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
@ -36,7 +38,7 @@ import org.springframework.util.ClassUtils;
class WebTestUtilsRuntimeHints implements RuntimeHintsRegistrar {
@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
public void registerHints(RuntimeHints hints, @Nullable ClassLoader classLoader) {
if (!ClassUtils.isPresent("jakarta.servlet.Filter", classLoader)) {
return;
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* AOT Support for Spring Security test.
*/
@NullMarked
package org.springframework.security.test.aot.hint;
import org.jspecify.annotations.NullMarked;

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Support for Framework's Test annotations.
*/
@NullMarked
package org.springframework.security.test.context.annotation;
import org.jspecify.annotations.NullMarked;

View File

@ -0,0 +1,24 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security support managing the
* {@link org.springframework.security.core.context.SecurityContext}.
*/
@NullMarked
package org.springframework.security.test.context;
import org.jspecify.annotations.NullMarked;

View File

@ -20,6 +20,9 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.function.Supplier;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.BeanUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.core.GenericTypeResolver;
@ -76,6 +79,7 @@ public class WithSecurityContextTestExecutionListener extends AbstractTestExecut
* {@link WithSecurityContext} on it. If that is not found, the class is inspected. If
* still not found, then no {@link SecurityContext} is populated.
*/
@NullUnmarked
@Override
public void beforeTestMethod(TestContext testContext) {
TestSecurityContext testSecurityContext = createTestSecurityContext(testContext.getTestMethod(), testContext);
@ -98,6 +102,7 @@ public class WithSecurityContextTestExecutionListener extends AbstractTestExecut
* If configured before test execution sets the SecurityContext
* @since 5.1
*/
@NullUnmarked
@Override
public void beforeTestExecution(TestContext testContext) {
Supplier<SecurityContext> supplier = (Supplier<SecurityContext>) testContext
@ -107,13 +112,13 @@ public class WithSecurityContextTestExecutionListener extends AbstractTestExecut
}
}
private TestSecurityContext createTestSecurityContext(AnnotatedElement annotated, TestContext context) {
private @Nullable TestSecurityContext createTestSecurityContext(AnnotatedElement annotated, TestContext context) {
WithSecurityContext withSecurityContext = AnnotatedElementUtils.findMergedAnnotation(annotated,
WithSecurityContext.class);
return createTestSecurityContext(annotated, withSecurityContext, context);
}
private TestSecurityContext createTestSecurityContext(Class<?> annotated, TestContext context) {
private @Nullable TestSecurityContext createTestSecurityContext(Class<?> annotated, TestContext context) {
TestContextAnnotationUtils.AnnotationDescriptor<WithSecurityContext> withSecurityContextDescriptor = TestContextAnnotationUtils
.findAnnotationDescriptor(annotated, WithSecurityContext.class);
if (withSecurityContextDescriptor == null) {
@ -124,9 +129,10 @@ public class WithSecurityContextTestExecutionListener extends AbstractTestExecut
return createTestSecurityContext(rootDeclaringClass, withSecurityContext, context);
}
@NullUnmarked
@SuppressWarnings({ "rawtypes", "unchecked" })
private TestSecurityContext createTestSecurityContext(AnnotatedElement annotated,
WithSecurityContext withSecurityContext, TestContext context) {
private @Nullable TestSecurityContext createTestSecurityContext(AnnotatedElement annotated,
@Nullable WithSecurityContext withSecurityContext, TestContext context) {
if (withSecurityContext == null) {
return null;
}
@ -147,7 +153,9 @@ public class WithSecurityContextTestExecutionListener extends AbstractTestExecut
return new TestSecurityContext(supplier, initialize);
}
private Annotation findAnnotation(AnnotatedElement annotated, Class<? extends Annotation> type) {
@NullUnmarked
private @Nullable Annotation findAnnotation(AnnotatedElement annotated,
@Nullable Class<? extends Annotation> type) {
Annotation findAnnotation = AnnotatedElementUtils.findMergedAnnotation(annotated, type);
if (findAnnotation != null) {
return findAnnotation;
@ -181,6 +189,7 @@ public class WithSecurityContextTestExecutionListener extends AbstractTestExecut
* Clears out the {@link TestSecurityContextHolder} and the
* {@link SecurityContextHolder} after each test method.
*/
@NullUnmarked
@Override
public void afterTestMethod(TestContext testContext) {
this.securityContextHolderStrategyConverter.convert(testContext).clearContext();

View File

@ -16,6 +16,8 @@
package org.springframework.security.test.context.support;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanNotOfRequiredTypeException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
@ -89,7 +91,7 @@ final class WithUserDetailsSecurityContextFactory implements WithSecurityContext
: this.beans.getBean(UserDetailsService.class);
}
UserDetailsService findAndAdaptReactiveUserDetailsService(String beanName) {
@Nullable UserDetailsService findAndAdaptReactiveUserDetailsService(String beanName) {
try {
ReactiveUserDetailsService reactiveUserDetailsService = StringUtils.hasLength(beanName)
? this.beans.getBean(beanName, ReactiveUserDetailsService.class)
@ -110,6 +112,7 @@ final class WithUserDetailsSecurityContextFactory implements WithSecurityContext
}
@Override
@SuppressWarnings("NullAway") // Dataflow analysis limitation
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return this.userDetailsService.findByUsername(username).block();
}

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security support classes for the Spring TestContext Framework.
*/
@NullMarked
package org.springframework.security.test.context.support;
import org.jspecify.annotations.NullMarked;

View File

@ -29,6 +29,7 @@ import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import org.jspecify.annotations.NullUnmarked;
import reactor.core.publisher.Mono;
import org.springframework.context.ApplicationContext;
@ -190,6 +191,7 @@ public final class SecurityMockServerConfigurers {
* @return the {@link OAuth2LoginMutator} to further configure or use
* @since 5.3
*/
@NullUnmarked
public static OAuth2LoginMutator mockOAuth2Login() {
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", null,
null, Collections.singleton("read"));
@ -203,6 +205,7 @@ public final class SecurityMockServerConfigurers {
* @return the {@link OidcLoginMutator} to further configure or use
* @since 5.3
*/
@NullUnmarked
public static OidcLoginMutator mockOidcLogin() {
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", null,
null, Collections.singleton("read"));
@ -252,6 +255,7 @@ public final class SecurityMockServerConfigurers {
private CsrfMutator() {
}
@NullUnmarked
@Override
public void afterConfigurerAdded(WebTestClient.Builder builder,
@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {
@ -394,6 +398,7 @@ public final class SecurityMockServerConfigurers {
builder.filters(addSetupMutatorFilter());
}
@NullUnmarked
@Override
public void afterConfigurerAdded(WebTestClient.Builder builder,
@Nullable WebHttpHandlerBuilder webHttpHandlerBuilder,
@ -537,6 +542,7 @@ public final class SecurityMockServerConfigurers {
configurer().afterConfigureAdded(serverSpec);
}
@NullUnmarked
@Override
public void afterConfigurerAdded(WebTestClient.Builder builder,
@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {
@ -547,6 +553,7 @@ public final class SecurityMockServerConfigurers {
configurer().afterConfigurerAdded(builder, httpHandlerBuilder, connector);
}
@NullUnmarked
private <T extends WebTestClientConfigurer & MockServerConfigurer> T configurer() {
return mockAuthentication(
new JwtAuthenticationToken(this.jwt, this.authoritiesConverter.convert(this.jwt)));
@ -631,6 +638,7 @@ public final class SecurityMockServerConfigurers {
configurer().afterConfigureAdded(serverSpec);
}
@NullUnmarked
@Override
public void afterConfigurerAdded(WebTestClient.Builder builder,
@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {
@ -688,6 +696,7 @@ public final class SecurityMockServerConfigurers {
return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", issuedAt, expiresAt);
}
@NullUnmarked
private Instant getInstant(Map<String, Object> attributes, String name) {
Object value = attributes.get(name);
if (value == null) {
@ -865,12 +874,15 @@ public final class SecurityMockServerConfigurers {
private OAuth2AccessToken accessToken;
@Nullable
private OidcIdToken idToken;
@SuppressWarnings("NullAway.Init")
private OidcUserInfo userInfo;
private Supplier<OidcUser> oidcUser = this::defaultPrincipal;
@Nullable
private Collection<GrantedAuthority> authorities;
private OidcLoginMutator(OAuth2AccessToken accessToken) {
@ -1015,6 +1027,7 @@ public final class SecurityMockServerConfigurers {
return authorities;
}
@NullUnmarked
private OidcIdToken getOidcIdToken() {
if (this.idToken != null) {
return this.idToken;
@ -1036,10 +1049,12 @@ public final class SecurityMockServerConfigurers {
* @author Josh Cummings
* @since 5.3
*/
@NullUnmarked
public static final class OAuth2ClientMutator implements WebTestClientConfigurer, MockServerConfigurer {
private String registrationId = "test";
@Nullable
private ClientRegistration clientRegistration;
private String principalName = "user";
@ -1115,12 +1130,14 @@ public final class SecurityMockServerConfigurers {
public void afterConfigureAdded(WebTestClient.MockServerSpec<?> serverSpec) {
}
@NullUnmarked
@Override
public void afterConfigurerAdded(WebTestClient.Builder builder,
@Nullable WebHttpHandlerBuilder httpHandlerBuilder, @Nullable ClientHttpConnector connector) {
httpHandlerBuilder.filters(addAuthorizedClientFilter());
}
@NullUnmarked
private Consumer<List<WebFilter>> addAuthorizedClientFilter() {
OAuth2AuthorizedClient client = getClient();
return (filters) -> filters.add(0, (exchange, chain) -> {
@ -1136,6 +1153,7 @@ public final class SecurityMockServerConfigurers {
});
}
@NullUnmarked
private OAuth2AuthorizedClient getClient() {
Assert.notNull(this.clientRegistration,
"Please specify a ClientRegistration via one of the clientRegistration methods");
@ -1163,12 +1181,14 @@ public final class SecurityMockServerConfigurers {
private final ReactiveOAuth2AuthorizedClientManager delegate;
@Nullable
private ServerOAuth2AuthorizedClientRepository authorizedClientRepository;
TestOAuth2AuthorizedClientManager(ReactiveOAuth2AuthorizedClientManager delegate) {
this.delegate = delegate;
}
@NullUnmarked
@Override
public Mono<OAuth2AuthorizedClient> authorize(OAuth2AuthorizeRequest authorizeRequest) {
ServerWebExchange exchange = authorizeRequest.getAttribute(ServerWebExchange.class.getName());
@ -1183,7 +1203,8 @@ public final class SecurityMockServerConfigurers {
exchange.getAttributes().put(ENABLED_ATTR_NAME, Boolean.TRUE);
}
boolean isEnabled(ServerWebExchange exchange) {
@NullUnmarked
boolean isEnabled(@Nullable ServerWebExchange exchange) {
return Boolean.TRUE.equals(exchange.getAttribute(ENABLED_ATTR_NAME));
}
@ -1202,7 +1223,8 @@ public final class SecurityMockServerConfigurers {
private final ServerOAuth2AuthorizedClientRepository delegate;
TestOAuth2AuthorizedClientRepository(ServerOAuth2AuthorizedClientRepository delegate) {
@NullUnmarked
TestOAuth2AuthorizedClientRepository(@Nullable ServerOAuth2AuthorizedClientRepository delegate) {
this.delegate = delegate;
}
@ -1261,7 +1283,8 @@ public final class SecurityMockServerConfigurers {
* @return the {@link ReactiveOAuth2AuthorizedClientManager} for the specified
* {@link ServerWebExchange}
*/
static ServerOAuth2AuthorizedClientRepository getAuthorizedClientRepository(ServerWebExchange exchange) {
static @Nullable ServerOAuth2AuthorizedClientRepository getAuthorizedClientRepository(
ServerWebExchange exchange) {
ReactiveOAuth2AuthorizedClientManager manager = getOAuth2AuthorizedClientManager(exchange);
if (manager == null) {
return DEFAULT_CLIENT_REPO;
@ -1294,7 +1317,8 @@ public final class SecurityMockServerConfigurers {
((TestOAuth2AuthorizedClientManager) manager).authorizedClientRepository = repository;
}
static ReactiveOAuth2AuthorizedClientManager getOAuth2AuthorizedClientManager(ServerWebExchange exchange) {
static @Nullable ReactiveOAuth2AuthorizedClientManager getOAuth2AuthorizedClientManager(
ServerWebExchange exchange) {
OAuth2AuthorizedClientArgumentResolver resolver = findResolver(exchange,
OAuth2AuthorizedClientArgumentResolver.class);
if (resolver == null) {
@ -1323,7 +1347,7 @@ public final class SecurityMockServerConfigurers {
}
@SuppressWarnings("unchecked")
static <T extends HandlerMethodArgumentResolver> T findResolver(ServerWebExchange exchange,
static <T extends HandlerMethodArgumentResolver> @Nullable T findResolver(ServerWebExchange exchange,
Class<T> resolverClass) {
if (!ClassUtils.isPresent(
"org.springframework.web.reactive.result.method.annotation.RequestMappingHandlerAdapter",
@ -1335,7 +1359,7 @@ public final class SecurityMockServerConfigurers {
private static class WebFluxClasspathGuard {
static <T extends HandlerMethodArgumentResolver> T findResolver(ServerWebExchange exchange,
static <T extends HandlerMethodArgumentResolver> @Nullable T findResolver(ServerWebExchange exchange,
Class<T> resolverClass) {
RequestMappingHandlerAdapter handlerAdapter = getRequestMappingHandlerAdapter(exchange);
if (handlerAdapter == null) {
@ -1358,7 +1382,7 @@ public final class SecurityMockServerConfigurers {
return null;
}
private static RequestMappingHandlerAdapter getRequestMappingHandlerAdapter(
private static @Nullable RequestMappingHandlerAdapter getRequestMappingHandlerAdapter(
ServerWebExchange exchange) {
ApplicationContext context = exchange.getApplicationContext();
if (context != null) {

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security upport for testing Spring WebFlux server endpoints via WebTestClient.
*/
@NullMarked
package org.springframework.security.test.web.reactive.server;
import org.jspecify.annotations.NullMarked;

View File

@ -17,6 +17,7 @@
package org.springframework.security.test.web.servlet.request;
import jakarta.servlet.ServletContext;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.Mergeable;
import org.springframework.http.MediaType;
@ -91,7 +92,7 @@ public final class SecurityMockMvcRequestBuilders {
private RequestPostProcessor postProcessor = csrf();
private Mergeable parent;
private @Nullable Mergeable parent;
private LogoutRequestBuilder() {
}
@ -135,7 +136,7 @@ public final class SecurityMockMvcRequestBuilders {
}
@Override
public Object merge(Object parent) {
public Object merge(@Nullable Object parent) {
if (parent == null) {
return this;
}
@ -168,7 +169,7 @@ public final class SecurityMockMvcRequestBuilders {
private MediaType acceptMediaType = MediaType.APPLICATION_FORM_URLENCODED;
private Mergeable parent;
private @Nullable Mergeable parent;
private RequestPostProcessor postProcessor = csrf();
@ -297,7 +298,7 @@ public final class SecurityMockMvcRequestBuilders {
}
@Override
public Object merge(Object parent) {
public Object merge(@Nullable Object parent) {
if (parent == null) {
return this;
}

View File

@ -40,6 +40,8 @@ import java.util.stream.Collectors;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.io.DefaultResourceLoader;
@ -397,6 +399,7 @@ public final class SecurityMockMvcRequestPostProcessors {
* @return the {@link OidcLoginRequestPostProcessor} for additional customization
* @since 5.3
*/
@NullUnmarked
public static OAuth2LoginRequestPostProcessor oauth2Login() {
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", null,
null, Collections.singleton("read"));
@ -425,6 +428,7 @@ public final class SecurityMockMvcRequestPostProcessors {
* @return the {@link OidcLoginRequestPostProcessor} for additional customization
* @since 5.3
*/
@NullUnmarked
public static OidcLoginRequestPostProcessor oidcLogin() {
OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", null,
null, Collections.singleton("read"));
@ -513,6 +517,7 @@ public final class SecurityMockMvcRequestPostProcessors {
private CsrfRequestPostProcessor() {
}
@NullUnmarked
@Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
CsrfTokenRepository repository = WebTestUtils.getCsrfTokenRepository(request);
@ -577,7 +582,7 @@ public final class SecurityMockMvcRequestPostProcessors {
}
@Override
public void saveToken(CsrfToken token, HttpServletRequest request, HttpServletResponse response) {
public void saveToken(@Nullable CsrfToken token, HttpServletRequest request, HttpServletResponse response) {
if (isEnabled(request)) {
request.setAttribute(TOKEN_ATTR_NAME, token);
}
@ -587,7 +592,7 @@ public final class SecurityMockMvcRequestPostProcessors {
}
@Override
public CsrfToken loadToken(HttpServletRequest request) {
public @Nullable CsrfToken loadToken(HttpServletRequest request) {
if (isEnabled(request)) {
return (CsrfToken) request.getAttribute(TOKEN_ATTR_NAME);
}
@ -697,8 +702,9 @@ public final class SecurityMockMvcRequestPostProcessors {
* @return the MD5 of the digest authentication response, encoded in hex
* @throws IllegalArgumentException if the supplied qop value is unsupported.
*/
private static String generateDigest(String username, String realm, String password, String httpMethod,
String uri, String qop, String nonce, String nc, String cnonce) throws IllegalArgumentException {
private static String generateDigest(String username, String realm, String password,
@Nullable String httpMethod, @Nullable String uri, String qop, String nonce, String nc, String cnonce)
throws IllegalArgumentException {
String a1Md5 = encodePasswordInA1Format(username, realm, password);
String a2 = httpMethod + ":" + uri;
String a2Md5 = md5Hex(a2);
@ -1129,6 +1135,7 @@ public final class SecurityMockMvcRequestPostProcessors {
return this;
}
@NullUnmarked
@Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
CsrfFilter.skipRequest(request);
@ -1255,6 +1262,7 @@ public final class SecurityMockMvcRequestPostProcessors {
return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", issuedAt, expiresAt);
}
@NullUnmarked
private Instant getInstant(Map<String, Object> attributes, String name) {
Object value = attributes.get(name);
if (value == null) {
@ -1407,13 +1415,14 @@ public final class SecurityMockMvcRequestPostProcessors {
private OAuth2AccessToken accessToken;
private OidcIdToken idToken;
private @Nullable OidcIdToken idToken;
@SuppressWarnings("NullAway.Init")
private OidcUserInfo userInfo;
private Supplier<OidcUser> oidcUser = this::defaultPrincipal;
private Collection<GrantedAuthority> authorities;
private @Nullable Collection<GrantedAuthority> authorities;
private OidcLoginRequestPostProcessor(OAuth2AccessToken accessToken) {
this.accessToken = accessToken;
@ -1525,6 +1534,7 @@ public final class SecurityMockMvcRequestPostProcessors {
return authorities;
}
@NullUnmarked
private OidcIdToken getOidcIdToken() {
if (this.idToken != null) {
return this.idToken;
@ -1546,11 +1556,12 @@ public final class SecurityMockMvcRequestPostProcessors {
* @author Josh Cummings
* @since 5.3
*/
@NullUnmarked
public static final class OAuth2ClientRequestPostProcessor implements RequestPostProcessor {
private String registrationId = "test";
private ClientRegistration clientRegistration;
private @Nullable ClientRegistration clientRegistration;
private String principalName = "user";
@ -1610,6 +1621,7 @@ public final class SecurityMockMvcRequestPostProcessors {
return this;
}
@NullUnmarked
@Override
public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
if (this.clientRegistration == null) {
@ -1650,14 +1662,15 @@ public final class SecurityMockMvcRequestPostProcessors {
private final OAuth2AuthorizedClientManager delegate;
private OAuth2AuthorizedClientRepository authorizedClientRepository;
private @Nullable OAuth2AuthorizedClientRepository authorizedClientRepository;
TestOAuth2AuthorizedClientManager(OAuth2AuthorizedClientManager delegate) {
this.delegate = delegate;
}
@NullUnmarked
@Override
public OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest) {
public @Nullable OAuth2AuthorizedClient authorize(OAuth2AuthorizeRequest authorizeRequest) {
HttpServletRequest request = authorizeRequest.getAttribute(HttpServletRequest.class.getName());
if (isEnabled(request)) {
return this.authorizedClientRepository.loadAuthorizedClient(
@ -1670,7 +1683,8 @@ public final class SecurityMockMvcRequestPostProcessors {
request.setAttribute(ENABLED_ATTR_NAME, Boolean.TRUE);
}
boolean isEnabled(HttpServletRequest request) {
@NullUnmarked
boolean isEnabled(@Nullable HttpServletRequest request) {
return Boolean.TRUE.equals(request.getAttribute(ENABLED_ATTR_NAME));
}
@ -1689,7 +1703,8 @@ public final class SecurityMockMvcRequestPostProcessors {
private final OAuth2AuthorizedClientRepository delegate;
TestOAuth2AuthorizedClientRepository(OAuth2AuthorizedClientRepository delegate) {
@NullUnmarked
TestOAuth2AuthorizedClientRepository(@Nullable OAuth2AuthorizedClientRepository delegate) {
this.delegate = delegate;
}
@ -1748,7 +1763,8 @@ public final class SecurityMockMvcRequestPostProcessors {
* @return the {@link OAuth2AuthorizedClientManager} for the specified
* {@link HttpServletRequest}
*/
static OAuth2AuthorizedClientRepository getAuthorizedClientRepository(HttpServletRequest request) {
static @Nullable OAuth2AuthorizedClientRepository getAuthorizedClientRepository(
HttpServletRequest request) {
OAuth2AuthorizedClientManager manager = getOAuth2AuthorizedClientManager(request);
if (manager == null) {
return DEFAULT_CLIENT_REPO;
@ -1781,7 +1797,8 @@ public final class SecurityMockMvcRequestPostProcessors {
((TestOAuth2AuthorizedClientManager) manager).authorizedClientRepository = repository;
}
static OAuth2AuthorizedClientManager getOAuth2AuthorizedClientManager(HttpServletRequest request) {
static @Nullable OAuth2AuthorizedClientManager getOAuth2AuthorizedClientManager(
HttpServletRequest request) {
OAuth2AuthorizedClientArgumentResolver resolver = findResolver(request,
OAuth2AuthorizedClientArgumentResolver.class);
if (resolver == null) {
@ -1809,7 +1826,7 @@ public final class SecurityMockMvcRequestPostProcessors {
}
@SuppressWarnings("unchecked")
static <T extends HandlerMethodArgumentResolver> T findResolver(HttpServletRequest request,
static <T extends HandlerMethodArgumentResolver> @Nullable T findResolver(HttpServletRequest request,
Class<T> resolverClass) {
if (!ClassUtils.isPresent(
"org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter", null)) {
@ -1820,7 +1837,7 @@ public final class SecurityMockMvcRequestPostProcessors {
private static class WebMvcClasspathGuard {
static <T extends HandlerMethodArgumentResolver> T findResolver(HttpServletRequest request,
static <T extends HandlerMethodArgumentResolver> @Nullable T findResolver(HttpServletRequest request,
Class<T> resolverClass) {
ServletContext servletContext = request.getServletContext();
RequestMappingHandlerAdapter mapping = getRequestMappingHandlerAdapter(servletContext);
@ -1839,7 +1856,7 @@ public final class SecurityMockMvcRequestPostProcessors {
return null;
}
private static RequestMappingHandlerAdapter getRequestMappingHandlerAdapter(
private static @Nullable RequestMappingHandlerAdapter getRequestMappingHandlerAdapter(
ServletContext servletContext) {
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
if (context != null) {

View File

@ -0,0 +1,24 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security built-in org.springframework.test.web.servlet.RequestBuilder
* implementations.
*/
@NullMarked
package org.springframework.security.test.web.servlet.request;
import org.jspecify.annotations.NullMarked;

View File

@ -20,6 +20,9 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.function.Consumer;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.core.Authentication;
@ -81,21 +84,22 @@ public final class SecurityMockMvcResultMatchers {
*/
public static final class AuthenticatedMatcher extends AuthenticationMatcher<AuthenticatedMatcher> {
private SecurityContext expectedContext;
private @Nullable SecurityContext expectedContext;
private Authentication expectedAuthentication;
private @Nullable Authentication expectedAuthentication;
private Object expectedAuthenticationPrincipal;
private @Nullable Object expectedAuthenticationPrincipal;
private String expectedAuthenticationName;
private @Nullable String expectedAuthenticationName;
private Collection<? extends GrantedAuthority> expectedGrantedAuthorities;
private @Nullable Collection<? extends GrantedAuthority> expectedGrantedAuthorities;
private Consumer<Authentication> assertAuthentication;
private @Nullable Consumer<Authentication> assertAuthentication;
AuthenticatedMatcher() {
}
@NullUnmarked
@Override
public void match(MvcResult result) {
SecurityContext context = load(result);

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security server-side support for testing Spring MVC applications.
*/
@NullMarked
package org.springframework.security.test.web.servlet.response;
import org.jspecify.annotations.NullMarked;

View File

@ -24,6 +24,7 @@ import jakarta.servlet.FilterConfig;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import org.jspecify.annotations.NullUnmarked;
import org.springframework.security.config.BeanIds;
import org.springframework.test.web.servlet.request.RequestPostProcessor;
@ -66,6 +67,7 @@ final class SecurityMockMvcConfigurer extends MockMvcConfigurerAdapter {
builder.addFilters(this.delegateFilter);
}
@NullUnmarked
@Override
public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder<?> builder,
WebApplicationContext context) {
@ -100,6 +102,7 @@ final class SecurityMockMvcConfigurer extends MockMvcConfigurerAdapter {
*/
static class DelegateFilter implements Filter {
@SuppressWarnings("NullAway.Init")
private Filter delegate;
DelegateFilter() {

View File

@ -0,0 +1,23 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security built-in MockMvcBuilder implementations.
*/
@NullMarked
package org.springframework.security.test.web.servlet.setup;
import org.jspecify.annotations.NullMarked;

View File

@ -21,6 +21,8 @@ import java.util.List;
import jakarta.servlet.Filter;
import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletRequest;
import org.jspecify.annotations.NullUnmarked;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.security.config.BeanIds;
@ -64,6 +66,7 @@ public abstract class WebTestUtils {
* @return the {@link SecurityContextRepository} for the specified
* {@link HttpServletRequest}
*/
@NullUnmarked
public static SecurityContextRepository getSecurityContextRepository(HttpServletRequest request) {
SecurityContextPersistenceFilter filter = findFilter(request, SecurityContextPersistenceFilter.class);
if (filter != null) {
@ -103,7 +106,7 @@ public abstract class WebTestUtils {
* @return the {@link CsrfTokenRepository} for the specified
* {@link HttpServletRequest}
*/
public static CsrfTokenRepository getCsrfTokenRepository(HttpServletRequest request) {
public static @Nullable CsrfTokenRepository getCsrfTokenRepository(HttpServletRequest request) {
CsrfFilter filter = findFilter(request, CsrfFilter.class);
if (filter == null) {
return DEFAULT_TOKEN_REPO;
@ -120,7 +123,7 @@ public abstract class WebTestUtils {
* @return the {@link CsrfTokenRequestHandler} for the specified
* {@link HttpServletRequest}
*/
public static CsrfTokenRequestHandler getCsrfTokenRequestHandler(HttpServletRequest request) {
public static @Nullable CsrfTokenRequestHandler getCsrfTokenRequestHandler(HttpServletRequest request) {
CsrfFilter filter = findFilter(request, CsrfFilter.class);
if (filter == null) {
return DEFAULT_CSRF_HANDLER;
@ -142,7 +145,7 @@ public abstract class WebTestUtils {
}
@SuppressWarnings("unchecked")
static <T extends Filter> T findFilter(HttpServletRequest request, Class<T> filterClass) {
static <T extends Filter> @Nullable T findFilter(HttpServletRequest request, Class<T> filterClass) {
ServletContext servletContext = request.getServletContext();
Filter springSecurityFilterChain = getSpringSecurityFilterChain(servletContext);
if (springSecurityFilterChain == null) {
@ -160,7 +163,7 @@ public abstract class WebTestUtils {
return null;
}
private static Filter getSpringSecurityFilterChain(ServletContext servletContext) {
private static @Nullable Filter getSpringSecurityFilterChain(ServletContext servletContext) {
Filter result = (Filter) servletContext.getAttribute(BeanIds.SPRING_SECURITY_FILTER_CHAIN);
if (result != null) {
return result;

View File

@ -0,0 +1,24 @@
/*
* Copyright 2004-present the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Spring Security supporting the org.springframework.web.context package, such as
* WebApplicationContext implementations and various utility classes.
*/
@NullMarked
package org.springframework.security.test.web.support;
import org.jspecify.annotations.NullMarked;