Skip to content

Add Not Support #14236

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
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;
Expand Down Expand Up @@ -244,11 +245,14 @@ public H and() {
* {@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
Expand All @@ -261,6 +265,16 @@ protected List<? extends RequestMatcher> getMatchers() {
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
Expand Down Expand Up @@ -382,7 +396,9 @@ public AuthorizationManagerRequestMatcherRegistry anonymous() {
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);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,20 @@ public void getWhenAnonymousConfiguredAndLoggedInUserThenRespondsWithForbidden()
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 {
Expand Down Expand Up @@ -1136,6 +1150,24 @@ SecurityFilterChain chain(HttpSecurity http) throws Exception {

}

@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 {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
* A factory class to create an {@link AuthorizationManager} instances.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.8
*/
public final class AuthorizationManagers {
Expand Down Expand Up @@ -119,6 +120,25 @@ public static <T> AuthorizationManager<T> allOf(AuthorizationDecision allAbstain
};
}

/**
* 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() {
}

Expand All @@ -138,4 +158,20 @@ public String toString() {

}

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 + ']';
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,19 @@ void checkAllOfWhenAllAbstainDefaultDecisionIsAbstainAndAllManagersAbstainThenAb
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();
}

}