Null safety via JSpecify spring-security-kerberos-client

Closes gh-18552
This commit is contained in:
Robert Winch 2026-01-21 17:46:40 -06:00
parent 8247d18122
commit 91aee30906
No known key found for this signature in database
7 changed files with 114 additions and 37 deletions

View File

@ -1,4 +1,5 @@
plugins {
id 'security-nullability'
id 'io.spring.convention.spring-module'
id 'javadoc-warnings-error'
}

View File

@ -51,6 +51,7 @@ import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.apache.hc.core5.http.config.Lookup;
import org.apache.hc.core5.http.config.RegistryBuilder;
import org.jspecify.annotations.Nullable;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
@ -82,13 +83,13 @@ public class KerberosRestTemplate extends RestTemplate {
private static final Credentials credentials = new NullCredentials();
private final String keyTabLocation;
private final @Nullable String keyTabLocation;
private final String userPrincipal;
private final @Nullable String userPrincipal;
private final String password;
private final @Nullable String password;
private final Map<String, Object> loginOptions;
private final @Nullable Map<String, Object> loginOptions;
/**
* Instantiates a new kerberos rest template.
@ -110,7 +111,7 @@ public class KerberosRestTemplate extends RestTemplate {
* @param keyTabLocation the key tab location
* @param userPrincipal the user principal
*/
public KerberosRestTemplate(String keyTabLocation, String userPrincipal) {
public KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal) {
this(keyTabLocation, userPrincipal, buildHttpClient());
}
@ -120,7 +121,8 @@ public class KerberosRestTemplate extends RestTemplate {
* @param userPrincipal the user principal
* @param httpClient the http client
*/
public KerberosRestTemplate(String keyTabLocation, String userPrincipal, HttpClient httpClient) {
public KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal,
HttpClient httpClient) {
this(keyTabLocation, userPrincipal, null, null, httpClient);
}
@ -128,7 +130,7 @@ public class KerberosRestTemplate extends RestTemplate {
* Instantiates a new kerberos rest template.
* @param loginOptions the login options
*/
public KerberosRestTemplate(Map<String, Object> loginOptions) {
public KerberosRestTemplate(@Nullable Map<String, Object> loginOptions) {
this(null, null, null, loginOptions, buildHttpClient());
}
@ -137,7 +139,7 @@ public class KerberosRestTemplate extends RestTemplate {
* @param loginOptions the login options
* @param httpClient the http client
*/
public KerberosRestTemplate(Map<String, Object> loginOptions, HttpClient httpClient) {
public KerberosRestTemplate(@Nullable Map<String, Object> loginOptions, HttpClient httpClient) {
this(null, null, null, loginOptions, httpClient);
}
@ -147,7 +149,8 @@ public class KerberosRestTemplate extends RestTemplate {
* @param userPrincipal the user principal
* @param loginOptions the login options
*/
public KerberosRestTemplate(String keyTabLocation, String userPrincipal, Map<String, Object> loginOptions) {
public KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal,
@Nullable Map<String, Object> loginOptions) {
this(keyTabLocation, userPrincipal, null, loginOptions, buildHttpClient());
}
@ -158,8 +161,8 @@ public class KerberosRestTemplate extends RestTemplate {
* @param password the password
* @param loginOptions the login options
*/
public KerberosRestTemplate(String keyTabLocation, String userPrincipal, String password,
Map<String, Object> loginOptions) {
public KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal,
@Nullable String password, @Nullable Map<String, Object> loginOptions) {
this(keyTabLocation, userPrincipal, password, loginOptions, buildHttpClient());
}
@ -171,8 +174,8 @@ public class KerberosRestTemplate extends RestTemplate {
* @param loginOptions the login options
* @param httpClient the http client
*/
private KerberosRestTemplate(String keyTabLocation, String userPrincipal, String password,
Map<String, Object> loginOptions, HttpClient httpClient) {
private KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal,
@Nullable String password, @Nullable Map<String, Object> loginOptions, HttpClient httpClient) {
super(new HttpComponentsClientHttpRequestFactory(httpClient));
this.keyTabLocation = keyTabLocation;
this.userPrincipal = userPrincipal;
@ -226,9 +229,9 @@ public class KerberosRestTemplate extends RestTemplate {
}
@Override
protected final <T> T doExecute(final URI url, final String uriTemplate, final HttpMethod method,
final RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor)
throws RestClientException {
protected final <T> T doExecute(final URI url, final @Nullable String uriTemplate,
final @Nullable HttpMethod method, final @Nullable RequestCallback requestCallback,
final @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
try {
LoginContext lc = buildLoginContext();
@ -249,23 +252,28 @@ public class KerberosRestTemplate extends RestTemplate {
}
}
private <T> T doExecuteSubject(URI url, String uriTemplate, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor) throws RestClientException {
return super.doExecute(url, uriTemplate, method, requestCallback, responseExtractor);
private <T> T doExecuteSubject(URI url, @Nullable String uriTemplate, @Nullable HttpMethod method,
@Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor)
throws RestClientException {
T result = super.doExecute(url, uriTemplate, method, requestCallback, responseExtractor);
if (result == null) {
throw new RestClientException("doExecute returned null");
}
return result;
}
private static final class ClientLoginConfig extends Configuration {
private final String keyTabLocation;
private final @Nullable String keyTabLocation;
private final String userPrincipal;
private final @Nullable String userPrincipal;
private final String password;
private final @Nullable String password;
private final Map<String, Object> loginOptions;
private final @Nullable Map<String, Object> loginOptions;
private ClientLoginConfig(String keyTabLocation, String userPrincipal, String password,
Map<String, Object> loginOptions) {
private ClientLoginConfig(@Nullable String keyTabLocation, @Nullable String userPrincipal,
@Nullable String password, @Nullable Map<String, Object> loginOptions) {
super();
this.keyTabLocation = keyTabLocation;
this.userPrincipal = userPrincipal;
@ -309,12 +317,12 @@ public class KerberosRestTemplate extends RestTemplate {
private static class NullCredentials implements Credentials {
@Override
public Principal getUserPrincipal() {
public @Nullable Principal getUserPrincipal() {
return null;
}
@Override
public char[] getPassword() {
public char @Nullable [] getPassword() {
return null;
}
@ -322,11 +330,11 @@ public class KerberosRestTemplate extends RestTemplate {
private static final class CallbackHandlerImpl implements CallbackHandler {
private final String userPrincipal;
private final @Nullable String userPrincipal;
private final String password;
private final @Nullable String password;
private CallbackHandlerImpl(String userPrincipal, String password) {
private CallbackHandlerImpl(@Nullable String userPrincipal, @Nullable String password) {
super();
this.userPrincipal = userPrincipal;
this.password = password;
@ -342,7 +350,9 @@ public class KerberosRestTemplate extends RestTemplate {
}
else if (callback instanceof PasswordCallback) {
PasswordCallback pc = (PasswordCallback) callback;
pc.setPassword(this.password.toCharArray());
if (this.password != null) {
pc.setPassword(this.password.toCharArray());
}
}
else {
throw new UnsupportedCallbackException(callback, "Unknown Callback");

View File

@ -23,6 +23,7 @@ import javax.security.auth.login.Configuration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.ClassPathResource;
@ -40,9 +41,9 @@ public class SunJaasKrb5LoginConfig extends Configuration implements Initializin
private static final Log LOG = LogFactory.getLog(SunJaasKrb5LoginConfig.class);
private String servicePrincipal;
private @Nullable String servicePrincipal;
private Resource keyTabLocation;
private @Nullable Resource keyTabLocation;
private Boolean useTicketCache = false;
@ -50,7 +51,7 @@ public class SunJaasKrb5LoginConfig extends Configuration implements Initializin
private Boolean debug = false;
private String keyTabLocationAsString;
private @Nullable String keyTabLocationAsString;
public void setServicePrincipal(String servicePrincipal) {
this.servicePrincipal = servicePrincipal;

View File

@ -0,0 +1,20 @@
/*
* 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.
*/
@NullMarked
package org.springframework.security.kerberos.client.config;
import org.jspecify.annotations.NullMarked;

View File

@ -29,6 +29,8 @@ import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import org.jspecify.annotations.Nullable;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig;
@ -65,7 +67,7 @@ import org.springframework.util.Assert;
*/
public class KerberosLdapContextSource extends DefaultSpringSecurityContextSource implements InitializingBean {
private Configuration loginConfig;
private @Nullable Configuration loginConfig;
/**
* Instantiates a new kerberos ldap context source.
@ -108,10 +110,10 @@ public class KerberosLdapContextSource extends DefaultSpringSecurityContextSourc
Subject serviceSubject = login();
final NamingException[] suppressedException = new NamingException[] { null };
DirContext dirContext = Subject.doAs(serviceSubject, new PrivilegedAction<>() {
DirContext dirContext = Subject.doAs(serviceSubject, new PrivilegedAction<@Nullable DirContext>() {
@Override
public DirContext run() {
public @Nullable DirContext run() {
try {
return KerberosLdapContextSource.super.getDirContextInstance(environment);
}
@ -125,6 +127,9 @@ public class KerberosLdapContextSource extends DefaultSpringSecurityContextSourc
if (suppressedException[0] != null) {
throw suppressedException[0];
}
if (dirContext == null) {
throw new NamingException("Failed to obtain DirContext");
}
return dirContext;
}

View File

@ -0,0 +1,20 @@
/*
* 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.
*/
@NullMarked
package org.springframework.security.kerberos.client.ldap;
import org.jspecify.annotations.NullMarked;

View File

@ -0,0 +1,20 @@
/*
* 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.
*/
@NullMarked
package org.springframework.security.kerberos.client;
import org.jspecify.annotations.NullMarked;