Browse Source

Document Reactive Method security requires Publisher return types

Fixes: gh-4988
Rob Winch 7 years ago
parent
commit
964a14b224

+ 6 - 0
config/src/test/java/org/springframework/security/config/annotation/method/configuration/DelegatingReactiveMessageService.java

@@ -28,6 +28,12 @@ public class DelegatingReactiveMessageService implements ReactiveMessageService
 		this.delegate = delegate;
 	}
 
+	@Override
+	@PreAuthorize("denyAll")
+	public String notPublisherPreAuthorizeFindById(long id) {
+		return this.delegate.notPublisherPreAuthorizeFindById(id);
+	}
+
 	@Override
 	public Mono<String> monoFindById(long id) {
 		return delegate.monoFindById(id);

+ 9 - 0
config/src/test/java/org/springframework/security/config/annotation/method/configuration/EnableReactiveMethodSecurityTests.java

@@ -34,6 +34,7 @@ import reactor.test.StepVerifier;
 import reactor.test.publisher.TestPublisher;
 import reactor.util.context.Context;
 
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.mockito.Mockito.*;
 
 /**
@@ -60,6 +61,14 @@ public class EnableReactiveMethodSecurityTests {
 		this.delegate = config.delegate;
 	}
 
+	@Test
+	public void notPublisherPreAuthorizeFindByIdThenThrowsIllegalStateException() {
+		assertThatThrownBy(() -> this.messageService.notPublisherPreAuthorizeFindById(1L))
+			.isInstanceOf(IllegalStateException.class)
+			.extracting(Throwable::getMessage)
+			.contains("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) in order to support Reactor Context");
+	}
+
 	@Test
 	public void monoWhenPermitAllThenAopDoesNotSubscribe() {
 		when(this.delegate.monoFindById(1L)).thenReturn(Mono.from(result));

+ 2 - 0
config/src/test/java/org/springframework/security/config/annotation/method/configuration/ReactiveMessageService.java

@@ -20,6 +20,8 @@ import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
 public interface ReactiveMessageService {
+	String notPublisherPreAuthorizeFindById(long id);
+
 	Mono<String> monoFindById(long id);
 	Mono<String> monoPreAuthorizeHasRoleFindById(long id);
 	Mono<String> monoPostAuthorizeFindById(long id);

+ 3 - 0
core/src/main/java/org/springframework/security/access/prepost/PrePostAdviceReactiveMethodInterceptor.java

@@ -64,6 +64,9 @@ public class PrePostAdviceReactiveMethodInterceptor implements MethodInterceptor
 		throws Throwable {
 		Method method = invocation.getMethod();
 		Class<?> returnType = method.getReturnType();
+		if (!Publisher.class.isAssignableFrom(returnType)) {
+			throw new IllegalStateException("The returnType " + returnType + " on " + method + " must return an instance of org.reactivestreams.Publisher (i.e. Mono / Flux) in order to support Reactor Context");
+		}
 		Class<?> targetClass = invocation.getThis().getClass();
 		Collection<ConfigAttribute> attributes = this.attributeSource
 			.getAttributes(method, targetClass);

+ 6 - 0
docs/manual/src/docs/asciidoc/index.adoc

@@ -1718,6 +1718,12 @@ For additional information about methods that can be overridden, refer to the `G
 Spring Security supports method security using https://projectreactor.io/docs/core/release/reference/#context[Reactor's Context] which is setup using `ReactiveSecurityContextHolder`.
 For example, this demonstrates how to retrieve the currently logged in user's message.
 
+[NOTE]
+====
+For this to work the return type of the method must be a `org.reactivestreams.Publisher` (i.e. `Mono`/`Flux`).
+This is necessary to integrate with Reactor's `Context`.
+====
+
 [source,java]
 ----
 Authentication authentication = new TestingAuthenticationToken("user", "password", "ROLE_USER");