Skip to content

Commit

Permalink
Add Not Support
Browse files Browse the repository at this point in the history
  • Loading branch information
jzheaux committed Dec 7, 2023
1 parent e49ae09 commit 11589cd
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 1 deletion.
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();
}

}

0 comments on commit 11589cd

Please sign in to comment.