From c4b0e9bd7424dea00d38f77d930c89d4e12546c4 Mon Sep 17 00:00:00 2001 From: Evgeniy Cheban Date: Tue, 12 Jul 2022 23:32:34 +0200 Subject: [PATCH] Add remaining methods from ExpressionUrlAuthorizationConfigurer to AuthorizeHttpRequestsConfigurer - Added fullyAuthenticated - Added rememberMe - Added anonymous Closes gh-11360 --- .../AuthorizeHttpRequestsConfigurer.java | 33 +++++ .../AuthorizeHttpRequestsConfigurerTests.java | 118 +++++++++++++++++ .../AuthenticatedAuthorizationManager.java | 121 ++++++++++++++++-- ...uthenticatedAuthorizationManagerTests.java | 84 +++++++++++- 4 files changed, 346 insertions(+), 10 deletions(-) diff --git a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java index 4bb7d682b5..820aa324be 100644 --- a/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java +++ b/config/src/main/java/org/springframework/security/config/annotation/web/configurers/AuthorizeHttpRequestsConfigurer.java @@ -317,6 +317,39 @@ public final class AuthorizeHttpRequestsConfigurer requests + .anyRequest().fullyAuthenticated() + ); + // @formatter:on + return http.build(); + } + + @Bean + UserDetailsService userDetailsService() { + return new InMemoryUserDetailsManager(TestAuthentication.user()); + } + + } + + @EnableWebSecurity + static class RememberMeConfig { + + @Bean + SecurityFilterChain chain(HttpSecurity http) throws Exception { + // @formatter:off + http + .httpBasic() + .and() + .rememberMe() + .and() + .authorizeHttpRequests((requests) -> requests + .anyRequest().rememberMe() + ); + // @formatter:on + return http.build(); + } + + @Bean + UserDetailsService userDetailsService() { + return new InMemoryUserDetailsManager(TestAuthentication.user()); + } + + } + + @EnableWebSecurity + static class AnonymousConfig { + + @Bean + SecurityFilterChain chain(HttpSecurity http) throws Exception { + // @formatter:off + http + .httpBasic() + .and() + .authorizeHttpRequests((requests) -> requests + .anyRequest().anonymous() + ); + // @formatter:on + return http.build(); + } + + } + @Configuration static class AuthorizationEventPublisherConfig { diff --git a/core/src/main/java/org/springframework/security/authorization/AuthenticatedAuthorizationManager.java b/core/src/main/java/org/springframework/security/authorization/AuthenticatedAuthorizationManager.java index baa65ac041..b9236eae3d 100644 --- a/core/src/main/java/org/springframework/security/authorization/AuthenticatedAuthorizationManager.java +++ b/core/src/main/java/org/springframework/security/authorization/AuthenticatedAuthorizationManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2022 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. @@ -21,6 +21,7 @@ import java.util.function.Supplier; import org.springframework.security.authentication.AuthenticationTrustResolver; import org.springframework.security.authentication.AuthenticationTrustResolverImpl; import org.springframework.security.core.Authentication; +import org.springframework.util.Assert; /** * An {@link AuthorizationManager} that determines if the current user is authenticated. @@ -31,7 +32,35 @@ import org.springframework.security.core.Authentication; */ public final class AuthenticatedAuthorizationManager implements AuthorizationManager { - private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl(); + private final AbstractAuthorizationStrategy authorizationStrategy; + + /** + * Creates an instance that determines if the current user is authenticated, this is + * the same as calling {@link #authenticated()} factory method. + * + * @since 5.8 + * @see #authenticated() + * @see #fullyAuthenticated() + * @see #rememberMe() + * @see #anonymous() + */ + public AuthenticatedAuthorizationManager() { + this(new AuthenticatedAuthorizationStrategy()); + } + + private AuthenticatedAuthorizationManager(AbstractAuthorizationStrategy authorizationStrategy) { + this.authorizationStrategy = authorizationStrategy; + } + + /** + * Sets the {@link AuthenticationTrustResolver} to be used. Default is + * {@link AuthenticationTrustResolverImpl}. Cannot be null. + * @param trustResolver the {@link AuthenticationTrustResolver} to use + * @since 5.8 + */ + public void setTrustResolver(AuthenticationTrustResolver trustResolver) { + this.authorizationStrategy.setTrustResolver(trustResolver); + } /** * Creates an instance of {@link AuthenticatedAuthorizationManager}. @@ -43,24 +72,98 @@ public final class AuthenticatedAuthorizationManager implements Authorization } /** - * Determines if the current user is authorized by evaluating if the - * {@link Authentication} is not anonymous and authenticated. + * Creates an instance of {@link AuthenticatedAuthorizationManager} that determines if + * the {@link Authentication} is authenticated without using remember me. + * @param the type of object being authorized + * @return the new instance + * @since 5.8 + */ + public static AuthenticatedAuthorizationManager fullyAuthenticated() { + return new AuthenticatedAuthorizationManager<>(new FullyAuthenticatedAuthorizationStrategy()); + } + + /** + * Creates an instance of {@link AuthenticatedAuthorizationManager} that determines if + * the {@link Authentication} is authenticated using remember me. + * @param the type of object being authorized + * @return the new instance + * @since 5.8 + */ + public static AuthenticatedAuthorizationManager rememberMe() { + return new AuthenticatedAuthorizationManager<>(new RememberMeAuthorizationStrategy()); + } + + /** + * Creates an instance of {@link AuthenticatedAuthorizationManager} that determines if + * the {@link Authentication} is anonymous. + * @param the type of object being authorized + * @return the new instance + * @since 5.8 + */ + public static AuthenticatedAuthorizationManager anonymous() { + return new AuthenticatedAuthorizationManager<>(new AnonymousAuthorizationStrategy()); + } + + /** + * Determines if the current user is authorized according to the given strategy. * @param authentication the {@link Supplier} of the {@link Authentication} to check * @param object the {@link T} object to check * @return an {@link AuthorizationDecision} */ @Override public AuthorizationDecision check(Supplier authentication, T object) { - boolean granted = isGranted(authentication.get()); + boolean granted = this.authorizationStrategy.isGranted(authentication.get()); return new AuthorizationDecision(granted); } - private boolean isGranted(Authentication authentication) { - return authentication != null && isNotAnonymous(authentication) && authentication.isAuthenticated(); + private abstract static class AbstractAuthorizationStrategy { + + AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl(); + + private void setTrustResolver(AuthenticationTrustResolver trustResolver) { + Assert.notNull(trustResolver, "trustResolver cannot be null"); + this.trustResolver = trustResolver; + } + + abstract boolean isGranted(Authentication authentication); + } - private boolean isNotAnonymous(Authentication authentication) { - return !this.trustResolver.isAnonymous(authentication); + private static class AuthenticatedAuthorizationStrategy extends AbstractAuthorizationStrategy { + + @Override + boolean isGranted(Authentication authentication) { + return authentication != null && !this.trustResolver.isAnonymous(authentication) + && authentication.isAuthenticated(); + } + + } + + private static final class FullyAuthenticatedAuthorizationStrategy extends AuthenticatedAuthorizationStrategy { + + @Override + boolean isGranted(Authentication authentication) { + return super.isGranted(authentication) && !this.trustResolver.isRememberMe(authentication); + } + + } + + private static final class AnonymousAuthorizationStrategy extends AbstractAuthorizationStrategy { + + @Override + boolean isGranted(Authentication authentication) { + return this.trustResolver.isAnonymous(authentication); + } + + } + + private static final class RememberMeAuthorizationStrategy extends AbstractAuthorizationStrategy { + + @Override + boolean isGranted(Authentication authentication) { + return this.trustResolver.isRememberMe(authentication); + } + } } diff --git a/core/src/test/java/org/springframework/security/authorization/AuthenticatedAuthorizationManagerTests.java b/core/src/test/java/org/springframework/security/authorization/AuthenticatedAuthorizationManagerTests.java index 5bcfed5c51..98914bffc5 100644 --- a/core/src/test/java/org/springframework/security/authorization/AuthenticatedAuthorizationManagerTests.java +++ b/core/src/test/java/org/springframework/security/authorization/AuthenticatedAuthorizationManagerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2022 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. @@ -16,11 +16,13 @@ package org.springframework.security.authorization; +import java.util.Collections; import java.util.function.Supplier; import org.junit.jupiter.api.Test; import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.RememberMeAuthenticationToken; import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.authority.AuthorityUtils; @@ -74,4 +76,84 @@ public class AuthenticatedAuthorizationManagerTests { assertThat(manager.check(() -> authentication, object).isGranted()).isFalse(); } + @Test + public void authenticatedWhenUserRememberMeThenGrantedDecision() { + AuthenticatedAuthorizationManager manager = AuthenticatedAuthorizationManager.authenticated(); + Supplier authentication = () -> new RememberMeAuthenticationToken("user", "password", + Collections.emptyList()); + Object object = new Object(); + assertThat(manager.check(authentication, object).isGranted()).isTrue(); + } + + @Test + public void fullyAuthenticatedWhenUserNotAnonymousAndNotRememberMeThenGrantedDecision() { + AuthenticatedAuthorizationManager manager = AuthenticatedAuthorizationManager.fullyAuthenticated(); + Supplier authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN", + "ROLE_USER"); + Object object = new Object(); + assertThat(manager.check(authentication, object).isGranted()).isTrue(); + } + + @Test + public void fullyAuthenticatedWhenUserNullThenDeniedDecision() { + AuthenticatedAuthorizationManager manager = AuthenticatedAuthorizationManager.fullyAuthenticated(); + Supplier authentication = () -> null; + Object object = new Object(); + assertThat(manager.check(authentication, object).isGranted()).isFalse(); + } + + @Test + public void fullyAuthenticatedWhenUserRememberMeThenDeniedDecision() { + AuthenticatedAuthorizationManager manager = AuthenticatedAuthorizationManager.fullyAuthenticated(); + Supplier authentication = () -> new RememberMeAuthenticationToken("user", "password", + Collections.emptyList()); + Object object = new Object(); + assertThat(manager.check(authentication, object).isGranted()).isFalse(); + } + + @Test + public void fullyAuthenticatedWhenUserAnonymousThenDeniedDecision() { + AuthenticatedAuthorizationManager manager = AuthenticatedAuthorizationManager.fullyAuthenticated(); + Supplier authentication = () -> new AnonymousAuthenticationToken("key", "principal", + AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + Object object = new Object(); + assertThat(manager.check(authentication, object).isGranted()).isFalse(); + } + + @Test + public void anonymousWhenUserAnonymousThenGrantedDecision() { + AuthenticatedAuthorizationManager manager = AuthenticatedAuthorizationManager.anonymous(); + Supplier authentication = () -> new AnonymousAuthenticationToken("key", "principal", + AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + Object object = new Object(); + assertThat(manager.check(authentication, object).isGranted()).isTrue(); + } + + @Test + public void anonymousWhenUserNotAnonymousThenDeniedDecision() { + AuthenticatedAuthorizationManager manager = AuthenticatedAuthorizationManager.anonymous(); + Supplier authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN", + "ROLE_USER"); + Object object = new Object(); + assertThat(manager.check(authentication, object).isGranted()).isFalse(); + } + + @Test + public void rememberMeWhenUserRememberMeThenGrantedDecision() { + AuthenticatedAuthorizationManager manager = AuthenticatedAuthorizationManager.rememberMe(); + Supplier authentication = () -> new RememberMeAuthenticationToken("user", "password", + Collections.emptyList()); + Object object = new Object(); + assertThat(manager.check(authentication, object).isGranted()).isTrue(); + } + + @Test + public void rememberMeWhenUserNotRememberMeThenDeniedDecision() { + AuthenticatedAuthorizationManager manager = AuthenticatedAuthorizationManager.rememberMe(); + Supplier authentication = () -> new TestingAuthenticationToken("user", "password", "ROLE_ADMIN", + "ROLE_USER"); + Object object = new Object(); + assertThat(manager.check(authentication, object).isGranted()).isFalse(); + } + }