Merge branch '6.1.x' into 6.2.x

This commit is contained in:
Marcus Hert Da Coregio 2024-02-16 13:21:15 -03:00
commit 15306c1007
16 changed files with 123 additions and 26 deletions

View File

@ -27,8 +27,9 @@ import org.apereo.cas.client.util.WebUtils;
import org.apereo.cas.client.validation.TicketValidator; import org.apereo.cas.client.validation.TicketValidator;
import org.springframework.core.log.LogMessage; import org.springframework.core.log.LogMessage;
import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.authentication.AuthenticationDetailsSource; import org.springframework.security.authentication.AuthenticationDetailsSource;
import org.springframework.security.authentication.AuthenticationTrustResolver;
import org.springframework.security.authentication.AuthenticationTrustResolverImpl;
import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent; import org.springframework.security.authentication.event.InteractiveAuthenticationSuccessEvent;
import org.springframework.security.cas.ServiceProperties; import org.springframework.security.cas.ServiceProperties;
import org.springframework.security.cas.authentication.CasServiceTicketAuthenticationToken; import org.springframework.security.cas.authentication.CasServiceTicketAuthenticationToken;
@ -199,6 +200,8 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy(); .getContextHolderStrategy();
private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
public CasAuthenticationFilter() { public CasAuthenticationFilter() {
super("/login/cas"); super("/login/cas");
setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler()); setAuthenticationFailureHandler(new SimpleUrlAuthenticationFailureHandler());
@ -348,8 +351,7 @@ public class CasAuthenticationFilter extends AbstractAuthenticationProcessingFil
*/ */
private boolean authenticated() { private boolean authenticated() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
return authentication != null && authentication.isAuthenticated() return this.trustResolver.isAuthenticated(authentication);
&& !(authentication instanceof AnonymousAuthenticationToken);
} }
/** /**

View File

@ -29,6 +29,7 @@ import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession; import jakarta.servlet.http.HttpSession;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
@ -82,6 +83,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy; import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.withSettings;
import static org.springframework.security.config.Customizer.withDefaults; import static org.springframework.security.config.Customizer.withDefaults;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.csrf;
import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic; import static org.springframework.security.test.web.servlet.request.SecurityMockMvcRequestPostProcessors.httpBasic;
@ -304,7 +306,8 @@ public class SessionManagementConfigurerTests {
@Test @Test
public void getWhenAnonymousRequestAndTrustResolverSharedObjectReturnsAnonymousFalseThenSessionIsSaved() public void getWhenAnonymousRequestAndTrustResolverSharedObjectReturnsAnonymousFalseThenSessionIsSaved()
throws Exception { throws Exception {
SharedTrustResolverConfig.TR = mock(AuthenticationTrustResolver.class); SharedTrustResolverConfig.TR = mock(AuthenticationTrustResolver.class,
withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS));
given(SharedTrustResolverConfig.TR.isAnonymous(any())).willReturn(false); given(SharedTrustResolverConfig.TR.isAnonymous(any())).willReturn(false);
this.spring.register(SharedTrustResolverConfig.class).autowire(); this.spring.register(SharedTrustResolverConfig.class).autowire();
MvcResult mvcResult = this.mvc.perform(get("/")).andReturn(); MvcResult mvcResult = this.mvc.perform(get("/")).andReturn();

View File

@ -28,6 +28,7 @@ import org.springframework.beans.factory.annotation.Autowired
import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration import org.springframework.context.annotation.Configuration
import org.springframework.mock.web.MockHttpSession import org.springframework.mock.web.MockHttpSession
import org.springframework.security.authentication.TestingAuthenticationToken
import org.springframework.security.config.annotation.web.builders.HttpSecurity import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.config.http.SessionCreationPolicy import org.springframework.security.config.http.SessionCreationPolicy
@ -118,7 +119,7 @@ class SessionManagementDslTests {
@Test @Test
fun `session management when session authentication error url then redirected to url`() { fun `session management when session authentication error url then redirected to url`() {
this.spring.register(SessionAuthenticationErrorUrlConfig::class.java).autowire() this.spring.register(SessionAuthenticationErrorUrlConfig::class.java).autowire()
val authentication: Authentication = mockk() val authentication: Authentication = TestingAuthenticationToken("user", "password", "ROLE_USER")
val session: MockHttpSession = mockk(relaxed = true) val session: MockHttpSession = mockk(relaxed = true)
every { session.changeSessionId() } throws SessionAuthenticationException("any SessionAuthenticationException") every { session.changeSessionId() } throws SessionAuthenticationException("any SessionAuthenticationException")
every<Any?> { session.getAttribute(any()) } returns null every<Any?> { session.getAttribute(any()) } returns null
@ -150,7 +151,7 @@ class SessionManagementDslTests {
@Test @Test
fun `session management when session authentication failure handler then handler used`() { fun `session management when session authentication failure handler then handler used`() {
this.spring.register(SessionAuthenticationFailureHandlerConfig::class.java).autowire() this.spring.register(SessionAuthenticationFailureHandlerConfig::class.java).autowire()
val authentication: Authentication = mockk() val authentication: Authentication = TestingAuthenticationToken("user", "password", "ROLE_USER")
val session: MockHttpSession = mockk(relaxed = true) val session: MockHttpSession = mockk(relaxed = true)
every { session.changeSessionId() } throws SessionAuthenticationException("any SessionAuthenticationException") every { session.changeSessionId() } throws SessionAuthenticationException("any SessionAuthenticationException")
every<Any?> { session.getAttribute(any()) } returns null every<Any?> { session.getAttribute(any()) } returns null
@ -210,7 +211,7 @@ class SessionManagementDslTests {
fun `session management when session authentication strategy then strategy used`() { fun `session management when session authentication strategy then strategy used`() {
this.spring.register(SessionAuthenticationStrategyConfig::class.java).autowire() this.spring.register(SessionAuthenticationStrategyConfig::class.java).autowire()
mockkObject(SessionAuthenticationStrategyConfig.STRATEGY) mockkObject(SessionAuthenticationStrategyConfig.STRATEGY)
val authentication: Authentication = mockk(relaxed = true) val authentication: Authentication = TestingAuthenticationToken("user", "password", "ROLE_USER")
val session: MockHttpSession = mockk(relaxed = true) val session: MockHttpSession = mockk(relaxed = true)
every { session.changeSessionId() } throws SessionAuthenticationException("any SessionAuthenticationException") every { session.changeSessionId() } throws SessionAuthenticationException("any SessionAuthenticationException")
every<Any?> { session.getAttribute(any()) } returns null every<Any?> { session.getAttribute(any()) } returns null

View File

@ -147,7 +147,7 @@ public abstract class SecurityExpressionRoot implements SecurityExpressionOperat
@Override @Override
public final boolean isAuthenticated() { public final boolean isAuthenticated() {
return !isAnonymous(); return this.trustResolver.isAuthenticated(getAuthentication());
} }
@Override @Override

View File

@ -61,13 +61,25 @@ public interface AuthenticationTrustResolver {
* <p> * <p>
* @param authentication to test (may be <code>null</code> in which case the method * @param authentication to test (may be <code>null</code> in which case the method
* will always return <code>false</code>) * will always return <code>false</code>)
* @return <code>true</code> the passed authentication token represented an anonymous * @return <code>true</code> the passed authentication token represented an
* principal and is authenticated using a remember-me token, <code>false</code> * authenticated user ({@link #isAuthenticated(Authentication)} and not
* otherwise * {@link #isRememberMe(Authentication)}, <code>false</code> otherwise
* @since 6.1 * @since 6.1
*/ */
default boolean isFullyAuthenticated(Authentication authentication) { default boolean isFullyAuthenticated(Authentication authentication) {
return !isAnonymous(authentication) && !isRememberMe(authentication); return isAuthenticated(authentication) && !isRememberMe(authentication);
}
/**
* Checks if the {@link Authentication} is not null, authenticated, and not anonymous.
* @param authentication the {@link Authentication} to check.
* @return true if the {@link Authentication} is not null,
* {@link #isAnonymous(Authentication)} returns false, &
* {@link Authentication#isAuthenticated()} is true.
* @since 6.1.7
*/
default boolean isAuthenticated(Authentication authentication) {
return authentication != null && authentication.isAuthenticated() && !isAnonymous(authentication);
} }
} }

View File

@ -133,8 +133,7 @@ public final class AuthenticatedAuthorizationManager<T> implements Authorization
@Override @Override
boolean isGranted(Authentication authentication) { boolean isGranted(Authentication authentication) {
return authentication != null && !this.trustResolver.isAnonymous(authentication) return this.trustResolver.isAuthenticated(authentication);
&& authentication.isAuthenticated();
} }
} }
@ -143,7 +142,7 @@ public final class AuthenticatedAuthorizationManager<T> implements Authorization
@Override @Override
boolean isGranted(Authentication authentication) { boolean isGranted(Authentication authentication) {
return authentication != null && this.trustResolver.isFullyAuthenticated(authentication); return this.trustResolver.isFullyAuthenticated(authentication);
} }
} }

View File

@ -25,6 +25,7 @@ import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.AuthorityUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.BDDMockito.given; import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
@ -134,4 +135,27 @@ public class SecurityExpressionRootTests {
assertThat(this.root.hasAnyAuthority("ROLE_A", "NOT")).isTrue(); assertThat(this.root.hasAnyAuthority("ROLE_A", "NOT")).isTrue();
} }
@Test
void isAuthenticatedWhenAuthenticatedNullThenException() {
this.root = new SecurityExpressionRoot((Authentication) null) {
};
assertThatIllegalArgumentException().isThrownBy(() -> this.root.isAuthenticated());
}
@Test
void isAuthenticatedWhenTrustResolverFalseThenFalse() {
AuthenticationTrustResolver atr = mock(AuthenticationTrustResolver.class);
given(atr.isAuthenticated(JOE)).willReturn(false);
this.root.setTrustResolver(atr);
assertThat(this.root.isAuthenticated()).isFalse();
}
@Test
void isAuthenticatedWhenTrustResolverTrueThenTrue() {
AuthenticationTrustResolver atr = mock(AuthenticationTrustResolver.class);
given(atr.isAuthenticated(JOE)).willReturn(true);
this.root.setTrustResolver(atr);
assertThat(this.root.isAuthenticated()).isTrue();
}
} }

View File

@ -18,6 +18,7 @@ package org.springframework.security.authentication;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils; import org.springframework.security.core.authority.AuthorityUtils;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
@ -63,4 +64,56 @@ public class AuthenticationTrustResolverImplTests {
assertThat(trustResolver.getRememberMeClass()).isEqualTo(TestingAuthenticationToken.class); assertThat(trustResolver.getRememberMeClass()).isEqualTo(TestingAuthenticationToken.class);
} }
@Test
void isAuthenticatedWhenAuthenticationNullThenFalse() {
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
Authentication authentication = null;
assertThat(trustResolver.isAuthenticated(authentication)).isFalse();
}
@Test
void isAuthenticatedWhenAuthenticationNotAuthenticatedThenFalse() {
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "password");
assertThat(trustResolver.isAuthenticated(authentication)).isFalse();
}
@Test
void isAuthenticatedWhenAnonymousThenFalse() {
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
AnonymousAuthenticationToken authentication = new AnonymousAuthenticationToken("key", "principal",
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
assertThat(trustResolver.isAuthenticated(authentication)).isFalse();
}
@Test
void isFullyAuthenticatedWhenAuthenticationNullThenFalse() {
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
Authentication authentication = null;
assertThat(trustResolver.isFullyAuthenticated(authentication)).isFalse();
}
@Test
void isFullyAuthenticatedWhenAuthenticationNotAuthenticatedThenFalse() {
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
TestingAuthenticationToken authentication = new TestingAuthenticationToken("user", "password");
assertThat(trustResolver.isFullyAuthenticated(authentication)).isFalse();
}
@Test
void isFullyAuthenticatedWhenAnonymousThenFalse() {
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
AnonymousAuthenticationToken authentication = new AnonymousAuthenticationToken("key", "principal",
AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
assertThat(trustResolver.isFullyAuthenticated(authentication)).isFalse();
}
@Test
void isFullyAuthenticatedWhenRememberMeThenFalse() {
AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
RememberMeAuthenticationToken authentication = new RememberMeAuthenticationToken("key", "user",
AuthorityUtils.createAuthorityList("ROLE_USER"));
assertThat(trustResolver.isFullyAuthenticated(authentication)).isFalse();
}
} }

View File

@ -22,6 +22,7 @@ import org.assertj.core.api.InstanceOfAssertFactories;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Answers;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension; import org.mockito.junit.jupiter.MockitoExtension;
@ -50,7 +51,7 @@ import static org.mockito.Mockito.verifyNoInteractions;
@ExtendWith(MockitoExtension.class) @ExtendWith(MockitoExtension.class)
public class DefaultMessageSecurityExpressionHandlerTests { public class DefaultMessageSecurityExpressionHandlerTests {
@Mock @Mock(answer = Answers.CALLS_REAL_METHODS)
AuthenticationTrustResolver trustResolver; AuthenticationTrustResolver trustResolver;
@Mock @Mock

View File

@ -107,8 +107,7 @@ public final class AuthenticatedPrincipalOAuth2AuthorizedClientRepository implem
} }
private boolean isPrincipalAuthenticated(Authentication authentication) { private boolean isPrincipalAuthenticated(Authentication authentication) {
return authentication != null && !this.authenticationTrustResolver.isAnonymous(authentication) return this.authenticationTrustResolver.isAuthenticated(authentication);
&& authentication.isAuthenticated();
} }
} }

View File

@ -106,8 +106,7 @@ public final class AuthenticatedPrincipalServerOAuth2AuthorizedClientRepository
} }
private boolean isPrincipalAuthenticated(Authentication authentication) { private boolean isPrincipalAuthenticated(Authentication authentication) {
return authentication != null && !this.authenticationTrustResolver.isAnonymous(authentication) return this.authenticationTrustResolver.isAuthenticated(authentication);
&& authentication.isAuthenticated();
} }
} }

View File

@ -52,7 +52,7 @@ public class ExceptionTranslationWebFilter implements WebFilter {
return chain.filter(exchange) return chain.filter(exchange)
.onErrorResume(AccessDeniedException.class, (denied) -> exchange.getPrincipal() .onErrorResume(AccessDeniedException.class, (denied) -> exchange.getPrincipal()
.filter((principal) -> (!(principal instanceof Authentication) || (principal instanceof Authentication .filter((principal) -> (!(principal instanceof Authentication) || (principal instanceof Authentication
&& !(this.authenticationTrustResolver.isAnonymous((Authentication) principal))))) && (this.authenticationTrustResolver.isAuthenticated((Authentication) principal)))))
.switchIfEmpty(commenceAuthentication(exchange, .switchIfEmpty(commenceAuthentication(exchange,
new InsufficientAuthenticationException( new InsufficientAuthenticationException(
"Full authentication is required to access this resource"))) "Full authentication is required to access this resource")))

View File

@ -93,7 +93,7 @@ public class SecurityContextHolderAwareRequestWrapper extends HttpServletRequest
*/ */
private Authentication getAuthentication() { private Authentication getAuthentication() {
Authentication auth = this.securityContextHolderStrategy.getContext().getAuthentication(); Authentication auth = this.securityContextHolderStrategy.getContext().getAuthentication();
return (!this.trustResolver.isAnonymous(auth)) ? auth : null; return (this.trustResolver.isAuthenticated(auth)) ? auth : null;
} }
/** /**

View File

@ -94,7 +94,7 @@ public class SessionManagementFilter extends GenericFilterBean {
request.setAttribute(FILTER_APPLIED, Boolean.TRUE); request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
if (!this.securityContextRepository.containsContext(request)) { if (!this.securityContextRepository.containsContext(request)) {
Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication(); Authentication authentication = this.securityContextHolderStrategy.getContext().getAuthentication();
if (authentication != null && !this.trustResolver.isAnonymous(authentication)) { if (this.trustResolver.isAuthenticated(authentication)) {
// The user has been authenticated during the current request, so call the // The user has been authenticated during the current request, so call the
// session strategy // session strategy
try { try {

View File

@ -140,7 +140,7 @@ public class SecurityContextHolderAwareRequestWrapperTests {
String username = "authPrincipalUsername"; String username = "authPrincipalUsername";
AuthenticatedPrincipal principal = mock(AuthenticatedPrincipal.class); AuthenticatedPrincipal principal = mock(AuthenticatedPrincipal.class);
given(principal.getName()).willReturn(username); given(principal.getName()).willReturn(username);
Authentication auth = new TestingAuthenticationToken(principal, "user"); Authentication auth = new TestingAuthenticationToken(principal, "user", "ROLE_USER");
SecurityContextHolder.getContext().setAuthentication(auth); SecurityContextHolder.getContext().setAuthentication(auth);
MockHttpServletRequest request = new MockHttpServletRequest(); MockHttpServletRequest request = new MockHttpServletRequest();
request.setRequestURI("/"); request.setRequestURI("/");

View File

@ -22,6 +22,7 @@ import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.mockito.Answers;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.mock.web.MockFilterChain; import org.springframework.mock.web.MockFilterChain;
@ -45,6 +46,7 @@ import static org.mockito.BDDMockito.willThrow;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.withSettings;
/** /**
* @author Luke Taylor * @author Luke Taylor
@ -244,7 +246,8 @@ public class SessionManagementFilterTests {
@Test @Test
public void customAuthenticationTrustResolver() throws Exception { public void customAuthenticationTrustResolver() throws Exception {
AuthenticationTrustResolver trustResolver = mock(AuthenticationTrustResolver.class); AuthenticationTrustResolver trustResolver = mock(AuthenticationTrustResolver.class,
withSettings().defaultAnswer(Answers.CALLS_REAL_METHODS));
SecurityContextRepository repo = mock(SecurityContextRepository.class); SecurityContextRepository repo = mock(SecurityContextRepository.class);
SessionManagementFilter filter = new SessionManagementFilter(repo); SessionManagementFilter filter = new SessionManagementFilter(repo);
filter.setTrustResolver(trustResolver); filter.setTrustResolver(trustResolver);
@ -262,7 +265,8 @@ public class SessionManagementFilterTests {
} }
private void authenticateUser() { private void authenticateUser() {
SecurityContextHolder.getContext().setAuthentication(new TestingAuthenticationToken("user", "pass")); SecurityContextHolder.getContext()
.setAuthentication(new TestingAuthenticationToken("user", "pass", "ROLE_USER"));
} }
} }