Skip to content

Commit 91aee30

Browse files
committed
Null safety via JSpecify spring-security-kerberos-client
Closes gh-18552
1 parent 8247d18 commit 91aee30

7 files changed

Lines changed: 114 additions & 37 deletions

File tree

kerberos/kerberos-client/spring-security-kerberos-client.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
plugins {
2+
id 'security-nullability'
23
id 'io.spring.convention.spring-module'
34
id 'javadoc-warnings-error'
45
}

kerberos/kerberos-client/src/main/java/org/springframework/security/kerberos/client/KerberosRestTemplate.java

Lines changed: 41 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
5252
import org.apache.hc.core5.http.config.Lookup;
5353
import org.apache.hc.core5.http.config.RegistryBuilder;
54+
import org.jspecify.annotations.Nullable;
5455

5556
import org.springframework.http.HttpMethod;
5657
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
@@ -82,13 +83,13 @@ public class KerberosRestTemplate extends RestTemplate {
8283

8384
private static final Credentials credentials = new NullCredentials();
8485

85-
private final String keyTabLocation;
86+
private final @Nullable String keyTabLocation;
8687

87-
private final String userPrincipal;
88+
private final @Nullable String userPrincipal;
8889

89-
private final String password;
90+
private final @Nullable String password;
9091

91-
private final Map<String, Object> loginOptions;
92+
private final @Nullable Map<String, Object> loginOptions;
9293

9394
/**
9495
* Instantiates a new kerberos rest template.
@@ -110,7 +111,7 @@ public KerberosRestTemplate(HttpClient httpClient) {
110111
* @param keyTabLocation the key tab location
111112
* @param userPrincipal the user principal
112113
*/
113-
public KerberosRestTemplate(String keyTabLocation, String userPrincipal) {
114+
public KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal) {
114115
this(keyTabLocation, userPrincipal, buildHttpClient());
115116
}
116117

@@ -120,15 +121,16 @@ public KerberosRestTemplate(String keyTabLocation, String userPrincipal) {
120121
* @param userPrincipal the user principal
121122
* @param httpClient the http client
122123
*/
123-
public KerberosRestTemplate(String keyTabLocation, String userPrincipal, HttpClient httpClient) {
124+
public KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal,
125+
HttpClient httpClient) {
124126
this(keyTabLocation, userPrincipal, null, null, httpClient);
125127
}
126128

127129
/**
128130
* Instantiates a new kerberos rest template.
129131
* @param loginOptions the login options
130132
*/
131-
public KerberosRestTemplate(Map<String, Object> loginOptions) {
133+
public KerberosRestTemplate(@Nullable Map<String, Object> loginOptions) {
132134
this(null, null, null, loginOptions, buildHttpClient());
133135
}
134136

@@ -137,7 +139,7 @@ public KerberosRestTemplate(Map<String, Object> loginOptions) {
137139
* @param loginOptions the login options
138140
* @param httpClient the http client
139141
*/
140-
public KerberosRestTemplate(Map<String, Object> loginOptions, HttpClient httpClient) {
142+
public KerberosRestTemplate(@Nullable Map<String, Object> loginOptions, HttpClient httpClient) {
141143
this(null, null, null, loginOptions, httpClient);
142144
}
143145

@@ -147,7 +149,8 @@ public KerberosRestTemplate(Map<String, Object> loginOptions, HttpClient httpCli
147149
* @param userPrincipal the user principal
148150
* @param loginOptions the login options
149151
*/
150-
public KerberosRestTemplate(String keyTabLocation, String userPrincipal, Map<String, Object> loginOptions) {
152+
public KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal,
153+
@Nullable Map<String, Object> loginOptions) {
151154
this(keyTabLocation, userPrincipal, null, loginOptions, buildHttpClient());
152155
}
153156

@@ -158,8 +161,8 @@ public KerberosRestTemplate(String keyTabLocation, String userPrincipal, Map<Str
158161
* @param password the password
159162
* @param loginOptions the login options
160163
*/
161-
public KerberosRestTemplate(String keyTabLocation, String userPrincipal, String password,
162-
Map<String, Object> loginOptions) {
164+
public KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal,
165+
@Nullable String password, @Nullable Map<String, Object> loginOptions) {
163166
this(keyTabLocation, userPrincipal, password, loginOptions, buildHttpClient());
164167
}
165168

@@ -171,8 +174,8 @@ public KerberosRestTemplate(String keyTabLocation, String userPrincipal, String
171174
* @param loginOptions the login options
172175
* @param httpClient the http client
173176
*/
174-
private KerberosRestTemplate(String keyTabLocation, String userPrincipal, String password,
175-
Map<String, Object> loginOptions, HttpClient httpClient) {
177+
private KerberosRestTemplate(@Nullable String keyTabLocation, @Nullable String userPrincipal,
178+
@Nullable String password, @Nullable Map<String, Object> loginOptions, HttpClient httpClient) {
176179
super(new HttpComponentsClientHttpRequestFactory(httpClient));
177180
this.keyTabLocation = keyTabLocation;
178181
this.userPrincipal = userPrincipal;
@@ -226,9 +229,9 @@ private LoginContext buildLoginContext() throws LoginException {
226229
}
227230

228231
@Override
229-
protected final <T> T doExecute(final URI url, final String uriTemplate, final HttpMethod method,
230-
final RequestCallback requestCallback, final ResponseExtractor<T> responseExtractor)
231-
throws RestClientException {
232+
protected final <T> T doExecute(final URI url, final @Nullable String uriTemplate,
233+
final @Nullable HttpMethod method, final @Nullable RequestCallback requestCallback,
234+
final @Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
232235

233236
try {
234237
LoginContext lc = buildLoginContext();
@@ -249,23 +252,28 @@ public T run() {
249252
}
250253
}
251254

252-
private <T> T doExecuteSubject(URI url, String uriTemplate, HttpMethod method, RequestCallback requestCallback,
253-
ResponseExtractor<T> responseExtractor) throws RestClientException {
254-
return super.doExecute(url, uriTemplate, method, requestCallback, responseExtractor);
255+
private <T> T doExecuteSubject(URI url, @Nullable String uriTemplate, @Nullable HttpMethod method,
256+
@Nullable RequestCallback requestCallback, @Nullable ResponseExtractor<T> responseExtractor)
257+
throws RestClientException {
258+
T result = super.doExecute(url, uriTemplate, method, requestCallback, responseExtractor);
259+
if (result == null) {
260+
throw new RestClientException("doExecute returned null");
261+
}
262+
return result;
255263
}
256264

257265
private static final class ClientLoginConfig extends Configuration {
258266

259-
private final String keyTabLocation;
267+
private final @Nullable String keyTabLocation;
260268

261-
private final String userPrincipal;
269+
private final @Nullable String userPrincipal;
262270

263-
private final String password;
271+
private final @Nullable String password;
264272

265-
private final Map<String, Object> loginOptions;
273+
private final @Nullable Map<String, Object> loginOptions;
266274

267-
private ClientLoginConfig(String keyTabLocation, String userPrincipal, String password,
268-
Map<String, Object> loginOptions) {
275+
private ClientLoginConfig(@Nullable String keyTabLocation, @Nullable String userPrincipal,
276+
@Nullable String password, @Nullable Map<String, Object> loginOptions) {
269277
super();
270278
this.keyTabLocation = keyTabLocation;
271279
this.userPrincipal = userPrincipal;
@@ -309,24 +317,24 @@ public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
309317
private static class NullCredentials implements Credentials {
310318

311319
@Override
312-
public Principal getUserPrincipal() {
320+
public @Nullable Principal getUserPrincipal() {
313321
return null;
314322
}
315323

316324
@Override
317-
public char[] getPassword() {
325+
public char @Nullable [] getPassword() {
318326
return null;
319327
}
320328

321329
}
322330

323331
private static final class CallbackHandlerImpl implements CallbackHandler {
324332

325-
private final String userPrincipal;
333+
private final @Nullable String userPrincipal;
326334

327-
private final String password;
335+
private final @Nullable String password;
328336

329-
private CallbackHandlerImpl(String userPrincipal, String password) {
337+
private CallbackHandlerImpl(@Nullable String userPrincipal, @Nullable String password) {
330338
super();
331339
this.userPrincipal = userPrincipal;
332340
this.password = password;
@@ -342,7 +350,9 @@ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallback
342350
}
343351
else if (callback instanceof PasswordCallback) {
344352
PasswordCallback pc = (PasswordCallback) callback;
345-
pc.setPassword(this.password.toCharArray());
353+
if (this.password != null) {
354+
pc.setPassword(this.password.toCharArray());
355+
}
346356
}
347357
else {
348358
throw new UnsupportedCallbackException(callback, "Unknown Callback");

kerberos/kerberos-client/src/main/java/org/springframework/security/kerberos/client/config/SunJaasKrb5LoginConfig.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
import org.apache.commons.logging.Log;
2525
import org.apache.commons.logging.LogFactory;
26+
import org.jspecify.annotations.Nullable;
2627

2728
import org.springframework.beans.factory.InitializingBean;
2829
import org.springframework.core.io.ClassPathResource;
@@ -40,17 +41,17 @@ public class SunJaasKrb5LoginConfig extends Configuration implements Initializin
4041

4142
private static final Log LOG = LogFactory.getLog(SunJaasKrb5LoginConfig.class);
4243

43-
private String servicePrincipal;
44+
private @Nullable String servicePrincipal;
4445

45-
private Resource keyTabLocation;
46+
private @Nullable Resource keyTabLocation;
4647

4748
private Boolean useTicketCache = false;
4849

4950
private Boolean isInitiator = false;
5051

5152
private Boolean debug = false;
5253

53-
private String keyTabLocationAsString;
54+
private @Nullable String keyTabLocationAsString;
5455

5556
public void setServicePrincipal(String servicePrincipal) {
5657
this.servicePrincipal = servicePrincipal;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
@NullMarked
18+
package org.springframework.security.kerberos.client.config;
19+
20+
import org.jspecify.annotations.NullMarked;

kerberos/kerberos-client/src/main/java/org/springframework/security/kerberos/client/ldap/KerberosLdapContextSource.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import javax.security.auth.login.LoginContext;
3030
import javax.security.auth.login.LoginException;
3131

32+
import org.jspecify.annotations.Nullable;
33+
3234
import org.springframework.beans.factory.InitializingBean;
3335
import org.springframework.ldap.core.support.LdapContextSource;
3436
import org.springframework.security.kerberos.client.config.SunJaasKrb5LoginConfig;
@@ -65,7 +67,7 @@
6567
*/
6668
public class KerberosLdapContextSource extends DefaultSpringSecurityContextSource implements InitializingBean {
6769

68-
private Configuration loginConfig;
70+
private @Nullable Configuration loginConfig;
6971

7072
/**
7173
* Instantiates a new kerberos ldap context source.
@@ -108,10 +110,10 @@ protected DirContext getDirContextInstance(final @SuppressWarnings("rawtypes") H
108110
Subject serviceSubject = login();
109111

110112
final NamingException[] suppressedException = new NamingException[] { null };
111-
DirContext dirContext = Subject.doAs(serviceSubject, new PrivilegedAction<>() {
113+
DirContext dirContext = Subject.doAs(serviceSubject, new PrivilegedAction<@Nullable DirContext>() {
112114

113115
@Override
114-
public DirContext run() {
116+
public @Nullable DirContext run() {
115117
try {
116118
return KerberosLdapContextSource.super.getDirContextInstance(environment);
117119
}
@@ -125,6 +127,9 @@ public DirContext run() {
125127
if (suppressedException[0] != null) {
126128
throw suppressedException[0];
127129
}
130+
if (dirContext == null) {
131+
throw new NamingException("Failed to obtain DirContext");
132+
}
128133

129134
return dirContext;
130135
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
@NullMarked
18+
package org.springframework.security.kerberos.client.ldap;
19+
20+
import org.jspecify.annotations.NullMarked;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2004-present the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
@NullMarked
18+
package org.springframework.security.kerberos.client;
19+
20+
import org.jspecify.annotations.NullMarked;

0 commit comments

Comments
 (0)