Skip to content

Commit a4a4908

Browse files
committed
Enable Null checking in spring-security-cas via JSpecify
Closes gh-16882
1 parent be64c67 commit a4a4908

18 files changed

+112
-25
lines changed

cas/spring-security-cas.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
plugins {
2+
id 'security-nullability'
3+
}
4+
15
apply plugin: 'io.spring.convention.spring-module'
26

37
dependencies {

cas/src/main/java/org/springframework/security/cas/ServiceProperties.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.security.cas;
1818

19+
import org.jspecify.annotations.Nullable;
20+
1921
import org.springframework.beans.factory.InitializingBean;
2022
import org.springframework.util.Assert;
2123

@@ -34,7 +36,7 @@ public class ServiceProperties implements InitializingBean {
3436

3537
public static final String DEFAULT_CAS_SERVICE_PARAMETER = "service";
3638

37-
private String service;
39+
private @Nullable String service;
3840

3941
private boolean authenticateAllArtifacts;
4042

@@ -62,7 +64,7 @@ public void afterPropertiesSet() {
6264
* </pre>
6365
* @return the URL of the service the user is authenticating to
6466
*/
65-
public final String getService() {
67+
public final @Nullable String getService() {
6668
return this.service;
6769
}
6870

cas/src/main/java/org/springframework/security/cas/authentication/CasAuthenticationProvider.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import org.apereo.cas.client.validation.Assertion;
2222
import org.apereo.cas.client.validation.TicketValidationException;
2323
import org.apereo.cas.client.validation.TicketValidator;
24+
import org.jspecify.annotations.NullUnmarked;
25+
import org.jspecify.annotations.Nullable;
2426

2527
import org.springframework.beans.factory.InitializingBean;
2628
import org.springframework.context.MessageSource;
@@ -62,6 +64,7 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
6264

6365
private static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);
6466

67+
@SuppressWarnings("NullAway.Init")
6568
private AuthenticationUserDetailsService<CasAssertionAuthenticationToken> authenticationUserDetailsService;
6669

6770
private UserDetailsChecker userDetailsChecker = new AccountStatusUserDetailsChecker();
@@ -70,11 +73,13 @@ public class CasAuthenticationProvider implements AuthenticationProvider, Initia
7073

7174
private StatelessTicketCache statelessTicketCache = new NullStatelessTicketCache();
7275

76+
@SuppressWarnings("NullAway.Init")
7377
private String key;
7478

79+
@SuppressWarnings("NullAway.Init")
7580
private TicketValidator ticketValidator;
7681

77-
private ServiceProperties serviceProperties;
82+
private @Nullable ServiceProperties serviceProperties;
7883

7984
private GrantedAuthoritiesMapper authoritiesMapper = new NullAuthoritiesMapper();
8085

@@ -89,7 +94,7 @@ public void afterPropertiesSet() {
8994
}
9095

9196
@Override
92-
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
97+
public @Nullable Authentication authenticate(Authentication authentication) throws AuthenticationException {
9398
if (!supports(authentication.getClass())) {
9499
return null;
95100
}
@@ -129,11 +134,14 @@ public Authentication authenticate(Authentication authentication) throws Authent
129134

130135
private CasAuthenticationToken authenticateNow(final Authentication authentication) throws AuthenticationException {
131136
try {
132-
Assertion assertion = this.ticketValidator.validate(authentication.getCredentials().toString(),
133-
getServiceUrl(authentication));
137+
Object credentials = authentication.getCredentials();
138+
if (credentials == null) {
139+
throw new BadCredentialsException("Authentication.getCredentials() cannot be null");
140+
}
141+
Assertion assertion = this.ticketValidator.validate(credentials.toString(), getServiceUrl(authentication));
134142
UserDetails userDetails = loadUserByAssertion(assertion);
135143
this.userDetailsChecker.check(userDetails);
136-
return new CasAuthenticationToken(this.key, userDetails, authentication.getCredentials(),
144+
return new CasAuthenticationToken(this.key, userDetails, credentials,
137145
this.authoritiesMapper.mapAuthorities(userDetails.getAuthorities()), userDetails, assertion);
138146
}
139147
catch (TicketValidationException ex) {
@@ -149,7 +157,8 @@ private CasAuthenticationToken authenticateNow(final Authentication authenticati
149157
* @param authentication
150158
* @return
151159
*/
152-
private String getServiceUrl(Authentication authentication) {
160+
@NullUnmarked
161+
private @Nullable String getServiceUrl(Authentication authentication) {
153162
String serviceUrl;
154163
if (authentication.getDetails() instanceof ServiceAuthenticationDetails) {
155164
return ((ServiceAuthenticationDetails) authentication.getDetails()).getServiceUrl();
@@ -215,7 +224,7 @@ public StatelessTicketCache getStatelessTicketCache() {
215224
return this.statelessTicketCache;
216225
}
217226

218-
protected TicketValidator getTicketValidator() {
227+
protected @Nullable TicketValidator getTicketValidator() {
219228
return this.ticketValidator;
220229
}
221230

cas/src/main/java/org/springframework/security/cas/authentication/CasServiceTicketAuthenticationToken.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.io.Serial;
2020
import java.util.Collection;
2121

22+
import org.jspecify.annotations.Nullable;
23+
2224
import org.springframework.security.authentication.AbstractAuthenticationToken;
2325
import org.springframework.security.core.GrantedAuthority;
2426
import org.springframework.util.Assert;
@@ -41,7 +43,7 @@ public class CasServiceTicketAuthenticationToken extends AbstractAuthenticationT
4143

4244
private final String identifier;
4345

44-
private Object credentials;
46+
private @Nullable Object credentials;
4547

4648
/**
4749
* This constructor can be safely used by any code that wishes to create a
@@ -86,7 +88,7 @@ public boolean isStateless() {
8688
}
8789

8890
@Override
89-
public Object getCredentials() {
91+
public @Nullable Object getCredentials() {
9092
return this.credentials;
9193
}
9294

cas/src/main/java/org/springframework/security/cas/authentication/NullStatelessTicketCache.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.security.cas.authentication;
1818

19+
import org.jspecify.annotations.Nullable;
20+
1921
/**
2022
* Implementation of @link {@link StatelessTicketCache} that has no backing cache. Useful
2123
* in instances where storing of tickets for stateless session management is not required.
@@ -33,7 +35,7 @@ public final class NullStatelessTicketCache implements StatelessTicketCache {
3335
* @return null since we are not storing any tickets.
3436
*/
3537
@Override
36-
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
38+
public @Nullable CasAuthenticationToken getByTicketId(final String serviceTicket) {
3739
return null;
3840
}
3941

cas/src/main/java/org/springframework/security/cas/authentication/SpringCacheBasedTicketCache.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import org.apache.commons.logging.Log;
2020
import org.apache.commons.logging.LogFactory;
21+
import org.jspecify.annotations.Nullable;
2122

2223
import org.springframework.cache.Cache;
2324
import org.springframework.core.log.LogMessage;
@@ -42,7 +43,7 @@ public SpringCacheBasedTicketCache(Cache cache) {
4243
}
4344

4445
@Override
45-
public CasAuthenticationToken getByTicketId(final String serviceTicket) {
46+
public @Nullable CasAuthenticationToken getByTicketId(final String serviceTicket) {
4647
final Cache.ValueWrapper element = (serviceTicket != null) ? this.cache.get(serviceTicket) : null;
4748
logger.debug(LogMessage.of(() -> "Cache hit: " + (element != null) + "; service ticket: " + serviceTicket));
4849
return (element != null) ? (CasAuthenticationToken) element.get() : null;

cas/src/main/java/org/springframework/security/cas/authentication/StatelessTicketCache.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.security.cas.authentication;
1818

19+
import org.jspecify.annotations.Nullable;
20+
1921
/**
2022
* Caches CAS service tickets and CAS proxy tickets for stateless connections.
2123
*
@@ -69,7 +71,7 @@ public interface StatelessTicketCache {
6971
* </p>
7072
* @return the fully populated authentication token
7173
*/
72-
CasAuthenticationToken getByTicketId(String serviceTicket);
74+
@Nullable CasAuthenticationToken getByTicketId(String serviceTicket);
7375

7476
/**
7577
* Adds the specified <code>CasAuthenticationToken</code> to the cache.

cas/src/main/java/org/springframework/security/cas/authentication/package-info.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,7 @@
1818
* An {@code AuthenticationProvider} that can process CAS service tickets and proxy
1919
* tickets.
2020
*/
21+
@NullMarked
2122
package org.springframework.security.cas.authentication;
23+
24+
import org.jspecify.annotations.NullMarked;
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
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+
/**
18+
* Jackson support for CAS.
19+
*/
20+
@NullMarked
21+
package org.springframework.security.cas.jackson2;
22+
23+
import org.jspecify.annotations.NullMarked;

cas/src/main/java/org/springframework/security/cas/package-info.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,7 @@
1818
* Spring Security support for Apereo's Central Authentication Service
1919
* (<a href="https://github.com/apereo/cas">CAS</a>).
2020
*/
21+
@NullMarked
2122
package org.springframework.security.cas;
23+
24+
import org.jspecify.annotations.NullMarked;

0 commit comments

Comments
 (0)