Skip to content

Commit

Permalink
Polish ReactiveMethodSecurity Support
Browse files Browse the repository at this point in the history
- Changed annotation property to useAuthorizationManager
to match related XML support
- Moved support found in bean post-processors back into
interceptors directly. This reduces the number of components to
maintain and simplifies ongoing support
- Added @deprecated annotation to indicate that applications
should use AuthorizationManagerBeforeReactiveMethodInterceptor and
AuthorizationManagerAfterReactiveMethodInterceptor instead. While
true that the new support does not support coroutines, the existing
coroutine support is problematic since it cannot be reliably paired
with other method interceptors
- Moved expression handler configuration to the constructors
- Constrain all method security interceptors to require publisher types
- Use ReactiveAdapter to check for single-value types as well

Issue gh-9401

Polish
  • Loading branch information
jzheaux committed Aug 25, 2022
1 parent 6fd23d2 commit e990174
Show file tree
Hide file tree
Showing 30 changed files with 283 additions and 412 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,6 @@
* used.
* @since 5.8
*/
boolean authorizationManager() default false;
boolean useAuthorizationManager() default false;

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,35 +45,31 @@ final class ReactiveAuthorizationManagerMethodSecurityConfiguration {
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
PreFilterAuthorizationReactiveMethodInterceptor preFilterInterceptor(
MethodSecurityExpressionHandler expressionHandler) {
PreFilterAuthorizationReactiveMethodInterceptor preFilter = new PreFilterAuthorizationReactiveMethodInterceptor();
preFilter.setExpressionHandler(expressionHandler);
return preFilter;
return new PreFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
}

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorizeInterceptor(
MethodSecurityExpressionHandler expressionHandler) {
PreAuthorizeReactiveAuthorizationManager authorizationManager = new PreAuthorizeReactiveAuthorizationManager();
authorizationManager.setExpressionHandler(expressionHandler);
PreAuthorizeReactiveAuthorizationManager authorizationManager = new PreAuthorizeReactiveAuthorizationManager(
expressionHandler);
return AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize(authorizationManager);
}

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
PostFilterAuthorizationReactiveMethodInterceptor postFilterInterceptor(
MethodSecurityExpressionHandler expressionHandler) {
PostFilterAuthorizationReactiveMethodInterceptor postFilter = new PostFilterAuthorizationReactiveMethodInterceptor();
postFilter.setExpressionHandler(expressionHandler);
return postFilter;
return new PostFilterAuthorizationReactiveMethodInterceptor(expressionHandler);
}

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
AuthorizationManagerAfterReactiveMethodInterceptor postAuthorizeInterceptor(
MethodSecurityExpressionHandler expressionHandler) {
PostAuthorizeReactiveAuthorizationManager authorizationManager = new PostAuthorizeReactiveAuthorizationManager();
authorizationManager.setExpressionHandler(expressionHandler);
PostAuthorizeReactiveAuthorizationManager authorizationManager = new PostAuthorizeReactiveAuthorizationManager(
expressionHandler);
return AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize(authorizationManager);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public String[] selectImports(AnnotationMetadata importMetadata) {
EnableReactiveMethodSecurity annotation = importMetadata.getAnnotations()
.get(EnableReactiveMethodSecurity.class).synthesize();
List<String> imports = new ArrayList<>(Arrays.asList(this.autoProxy.selectImports(importMetadata)));
if (annotation.authorizationManager()) {
if (annotation.useAuthorizationManager()) {
imports.add(ReactiveAuthorizationManagerMethodSecurityConfiguration.class.getName());
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ public Flux<String> fluxManyAnnotations(Flux<String> flux) {
return flux;
}

@PostFilter("filterObject.length > 5")
public Flux<String> fluxPostFilter(Flux<String> flux) {
return flux;
}

@Override
public Publisher<String> publisherFindById(long id) {
return this.delegate.publisherFindById(id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@

/**
* Tests for {@link EnableReactiveMethodSecurity} with the
* {@link EnableReactiveMethodSecurity#authorizationManager()} flag set to true.
* {@link EnableReactiveMethodSecurity#useAuthorizationManager()} flag set to true.
*
* @author Evgeniy Cheban
*/
Expand Down Expand Up @@ -79,8 +79,7 @@ public void notPublisherPreAuthorizeFindByIdThenThrowsIllegalStateException() {
.withMessage("The returnType class java.lang.String on public abstract java.lang.String "
+ "org.springframework.security.config.annotation.method.configuration.ReactiveMessageService"
+ ".notPublisherPreAuthorizeFindById(long) must return an instance of org.reactivestreams"
+ ".Publisher (i.e. Mono / Flux) or the function must be a Kotlin coroutine "
+ "function in order to support Reactor Context");
+ ".Publisher (for example, a Mono or Flux) in order to support Reactor Context");
}

@Test
Expand Down Expand Up @@ -340,6 +339,13 @@ public void fluxManyAnnotationsWhenNameNotAllowedThenFails() {
StepVerifier.create(flux).expectNext("harold", "jonathan").expectError(AccessDeniedException.class).verify();
}

@Test
public void fluxPostFilterWhenFilteringThenWorks() {
Flux<String> flux = this.messageService.fluxPostFilter(Flux.just("harold", "jonathan", "michael", "pete", "bo"))
.contextWrite(this.withAdmin);
StepVerifier.create(flux).expectNext("harold", "jonathan", "michael").verifyComplete();
}

// Publisher tests
@Test
public void publisherWhenPermitAllThenAopDoesNotSubscribe() {
Expand Down Expand Up @@ -458,7 +464,7 @@ static <T> Publisher<T> publisherJust(T... data) {
return publisher(Flux.just(data));
}

@EnableReactiveMethodSecurity(authorizationManager = true)
@EnableReactiveMethodSecurity(useAuthorizationManager = true)
static class Config {

ReactiveMessageService delegate = mock(ReactiveMessageService.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public interface ReactiveMessageService {

Flux<String> fluxManyAnnotations(Flux<String> flux);

Flux<String> fluxPostFilter(Flux<String> flux);

Publisher<String> publisherFindById(long id);

Publisher<String> publisherPreAuthorizeHasRoleFindById(long id);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,12 @@
* @author Rob Winch
* @author Eleftheria Stein
* @since 5.0
* @deprecated Use
* {@link org.springframework.security.authorization.method.AuthorizationManagerBeforeReactiveMethodInterceptor}
* or
* {@link org.springframework.security.authorization.method.AuthorizationManagerAfterReactiveMethodInterceptor}
*/
@Deprecated
public class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor {

private Authentication anonymous = new AnonymousAuthenticationToken("key", "anonymous",
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit e990174

Please sign in to comment.