From 68f81b8d2194c40ef643b2aff81484ecfe8eb9cb Mon Sep 17 00:00:00 2001 From: Andrey Litvitski Date: Mon, 6 Oct 2025 19:04:15 +0300 Subject: [PATCH] Mark `GrantedAuthority#getAuthority` as `@Nullable` Closes: gh-17999 Signed-off-by: Andrey Litvitski --- .../AuthorityReactiveAuthorizationManager.java | 5 +++-- .../security/core/GrantedAuthority.java | 4 +++- .../core/authority/mapping/SimpleAuthorityMapper.java | 5 ++++- .../response/SecurityMockMvcResultMatchers.java | 3 ++- .../DelegatingMissingAuthorityAccessDeniedHandler.java | 10 +++++----- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java index f9661578284..d04e9efbfd9 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/AuthorityReactiveAuthorizationManager.java @@ -17,6 +17,7 @@ package org.springframework.security.authorization; import java.util.List; +import java.util.Objects; import reactor.core.publisher.Mono; @@ -47,8 +48,8 @@ public Mono authorize(Mono authentication, // @formatter:off return authentication.filter(Authentication::isAuthenticated) .flatMapIterable(Authentication::getAuthorities) - .map(GrantedAuthority::getAuthority) - .any((grantedAuthority) -> this.authorities.stream().anyMatch((authority) -> authority.getAuthority().equals(grantedAuthority))) + .mapNotNull(GrantedAuthority::getAuthority) + .any((grantedAuthority) -> this.authorities.stream().anyMatch((authority) -> Objects.equals(authority.getAuthority(), grantedAuthority))) .map((granted) -> ((AuthorizationResult) new AuthorityAuthorizationDecision(granted, this.authorities))) .defaultIfEmpty(new AuthorityAuthorizationDecision(false, this.authorities)); // @formatter:on diff --git a/core/src/main/java/org/springframework/security/core/GrantedAuthority.java b/core/src/main/java/org/springframework/security/core/GrantedAuthority.java index 143b254b854..cc2b379acda 100644 --- a/core/src/main/java/org/springframework/security/core/GrantedAuthority.java +++ b/core/src/main/java/org/springframework/security/core/GrantedAuthority.java @@ -18,6 +18,8 @@ import java.io.Serializable; +import org.jspecify.annotations.Nullable; + import org.springframework.security.authorization.AuthorizationManager; /** @@ -46,6 +48,6 @@ public interface GrantedAuthority extends Serializable { * granted authority cannot be expressed as a String with sufficient * precision). */ - String getAuthority(); + @Nullable String getAuthority(); } diff --git a/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java b/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java index 69f85d0b668..218e760d922 100644 --- a/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java +++ b/core/src/main/java/org/springframework/security/core/authority/mapping/SimpleAuthorityMapper.java @@ -64,7 +64,10 @@ public void afterPropertiesSet() { public Set mapAuthorities(Collection authorities) { HashSet mapped = new HashSet<>(authorities.size()); for (GrantedAuthority authority : authorities) { - mapped.add(mapAuthority(authority.getAuthority())); + String authorityStr = authority.getAuthority(); + if (authorityStr != null) { + mapped.add(mapAuthority(authorityStr)); + } } if (this.defaultAuthority != null) { mapped.add(this.defaultAuthority); diff --git a/test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java b/test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java index c668a665d34..1b1e73badd0 100644 --- a/test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java +++ b/test/src/main/java/org/springframework/security/test/web/servlet/response/SecurityMockMvcResultMatchers.java @@ -281,7 +281,8 @@ public AuthenticatedMatcher withRoles(String rolePrefix, String[] roles) { for (String role : roles) { withPrefix.add(new SimpleGrantedAuthority(rolePrefix + role)); } - this.ignoreAuthorities = (authority) -> !authority.getAuthority().startsWith(rolePrefix); + this.ignoreAuthorities = (authority) -> (authority.getAuthority() != null + && !authority.getAuthority().startsWith(rolePrefix)); return withAuthorities(withPrefix); } diff --git a/web/src/main/java/org/springframework/security/web/access/DelegatingMissingAuthorityAccessDeniedHandler.java b/web/src/main/java/org/springframework/security/web/access/DelegatingMissingAuthorityAccessDeniedHandler.java index eee4e286aaa..6933162a53a 100644 --- a/web/src/main/java/org/springframework/security/web/access/DelegatingMissingAuthorityAccessDeniedHandler.java +++ b/web/src/main/java/org/springframework/security/web/access/DelegatingMissingAuthorityAccessDeniedHandler.java @@ -160,12 +160,12 @@ private List authorityErrors(AccessDeniedExce return authorityDecision.getAuthorities().stream() .map((grantedAuthority) -> { String authority = grantedAuthority.getAuthority(); - if (authority.startsWith("FACTOR_")) { + if (authority != null && authority.startsWith("FACTOR_")) { RequiredFactor required = RequiredFactor.withAuthority(authority).build(); return new AuthorityRequiredFactorErrorEntry(authority, RequiredFactorError.createMissing(required)); } else { - return new AuthorityRequiredFactorErrorEntry(authority, null); + return new AuthorityRequiredFactorErrorEntry(null, null); } }) .collect(Collectors.toList()); @@ -247,17 +247,17 @@ public DelegatingMissingAuthorityAccessDeniedHandler build() { */ private static final class AuthorityRequiredFactorErrorEntry { - private final String authority; + @Nullable private final String authority; private final @Nullable RequiredFactorError error; - private AuthorityRequiredFactorErrorEntry(String authority, @Nullable RequiredFactorError error) { + private AuthorityRequiredFactorErrorEntry(@Nullable String authority, @Nullable RequiredFactorError error) { Assert.notNull(authority, "authority cannot be null"); this.authority = authority; this.error = error; } - private String getAuthority() { + @Nullable private String getAuthority() { return this.authority; }