1
0
mirror of synced 2026-08-06 10:18:52 +00:00

Add Not Support

Closes gh-14058
This commit is contained in:
Josh Cummings
2023-12-01 17:15:19 -07:00
parent e49ae096e6
commit bb6b55aca3
4 changed files with 100 additions and 1 deletions
@@ -30,6 +30,7 @@ import org.springframework.security.authorization.AuthorityAuthorizationManager;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.AuthorizationEventPublisher;
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.AuthorizationManagers;
import org.springframework.security.authorization.ObservationAuthorizationManager;
import org.springframework.security.authorization.SpringAuthorizationEventPublisher;
import org.springframework.security.config.annotation.ObjectPostProcessor;
@@ -244,11 +245,14 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
* {@link RequestMatcher}s.
*
* @author Evgeniy Cheban
* @author Josh Cummings
*/
public class AuthorizedUrl {
private final List<? extends RequestMatcher> matchers;
private boolean not;
/**
* Creates an instance.
* @param matchers the {@link RequestMatcher} instances to map
@@ -261,6 +265,16 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
return this.matchers;
}
/**
* Negates the following authorization rule.
* @return the {@link AuthorizedUrl} for further customization
* @since 6.3
*/
public AuthorizedUrl not() {
this.not = true;
return this;
}
/**
* Specify that URLs are allowed by anyone.
* @return the {@link AuthorizationManagerRequestMatcherRegistry} for further
@@ -382,7 +396,9 @@ public final class AuthorizeHttpRequestsConfigurer<H extends HttpSecurityBuilder
public AuthorizationManagerRequestMatcherRegistry access(
AuthorizationManager<RequestAuthorizationContext> manager) {
Assert.notNull(manager, "manager cannot be null");
return AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);
return (this.not)
? AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, AuthorizationManagers.not(manager))
: AuthorizeHttpRequestsConfigurer.this.addMapping(this.matchers, manager);
}
}
@@ -596,6 +596,20 @@ public class AuthorizeHttpRequestsConfigurerTests {
this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
}
@Test
public void getWhenNotConfigAndAuthenticatedThenRespondsWithForbidden() throws Exception {
this.spring.register(NotConfig.class, BasicController.class).autowire();
MockHttpServletRequestBuilder requestWithUser = get("/").with(user("user"));
this.mvc.perform(requestWithUser).andExpect(status().isForbidden());
}
@Test
public void getWhenNotConfigAndNotAuthenticatedThenRespondsWithOk() throws Exception {
this.spring.register(NotConfig.class, BasicController.class).autowire();
MockHttpServletRequestBuilder requestWithUser = get("/");
this.mvc.perform(requestWithUser).andExpect(status().isOk());
}
@Configuration
@EnableWebSecurity
static class GrantedAuthorityDefaultHasRoleConfig {
@@ -1136,6 +1150,24 @@ public class AuthorizeHttpRequestsConfigurerTests {
}
@Configuration
@EnableWebSecurity
static class NotConfig {
@Bean
SecurityFilterChain chain(HttpSecurity http) throws Exception {
// @formatter:off
http
.httpBasic(withDefaults())
.authorizeHttpRequests((requests) -> requests
.anyRequest().not().authenticated()
);
// @formatter:on
return http.build();
}
}
@Configuration
static class AuthorizationEventPublisherConfig {
@@ -23,6 +23,7 @@ import java.util.List;
* A factory class to create an {@link AuthorizationManager} instances.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.8
*/
public final class AuthorizationManagers {
@@ -119,6 +120,25 @@ public final class AuthorizationManagers {
};
}
/**
* Creates an {@link AuthorizationManager} that reverses whatever decision the given
* {@link AuthorizationManager} granted. If the given {@link AuthorizationManager}
* abstains, then the returned manager also abstains.
* @param <T> the type of object that is being authorized
* @param manager the {@link AuthorizationManager} to reverse
* @return the reversing {@link AuthorizationManager}
* @since 6.3
*/
public static <T> AuthorizationManager<T> not(AuthorizationManager<T> manager) {
return (authentication, object) -> {
AuthorizationDecision decision = manager.check(authentication, object);
if (decision == null) {
return null;
}
return new NotAuthorizationDecision(decision);
};
}
private AuthorizationManagers() {
}
@@ -138,4 +158,20 @@ public final class AuthorizationManagers {
}
private static final class NotAuthorizationDecision extends AuthorizationDecision {
private final AuthorizationDecision decision;
private NotAuthorizationDecision(AuthorizationDecision decision) {
super(!decision.isGranted());
this.decision = decision;
}
@Override
public String toString() {
return "NotAuthorizationDecision [decision=" + this.decision + ']';
}
}
}
@@ -224,4 +224,19 @@ class AuthorizationManagersTests {
assertThat(decision).isNull();
}
@Test
void checkNotWhenEmptyThenAbstainedDecision() {
AuthorizationManager<?> negated = AuthorizationManagers.not((a, o) -> null);
AuthorizationDecision decision = negated.check(null, null);
assertThat(decision).isNull();
}
@Test
void checkNotWhenGrantedThenDeniedDecision() {
AuthorizationManager<?> negated = AuthorizationManagers.not((a, o) -> new AuthorizationDecision(true));
AuthorizationDecision decision = negated.check(null, null);
assertThat(decision).isNotNull();
assertThat(decision.isGranted()).isFalse();
}
}