Polish ReactiveMethodSecurity Support

- 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
This commit is contained in:
Josh Cummings 2022-08-17 22:29:51 -06:00
parent 6fd23d2567
commit e990174c89
No known key found for this signature in database
GPG Key ID: A306A51F43B8E5A5
30 changed files with 290 additions and 419 deletions

View File

@ -75,6 +75,6 @@ public @interface EnableReactiveMethodSecurity {
* used.
* @since 5.8
*/
boolean authorizationManager() default false;
boolean useAuthorizationManager() default false;
}

View File

@ -45,17 +45,15 @@ 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);
}
@ -63,17 +61,15 @@ final class ReactiveAuthorizationManagerMethodSecurityConfiguration {
@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);
}

View File

@ -44,7 +44,7 @@ class ReactiveMethodSecuritySelector implements ImportSelector {
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 {

View File

@ -112,6 +112,11 @@ public class DelegatingReactiveMessageService implements ReactiveMessageService
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);

View File

@ -42,7 +42,7 @@ import static org.mockito.Mockito.reset;
/**
* Tests for {@link EnableReactiveMethodSecurity} with the
* {@link EnableReactiveMethodSecurity#authorizationManager()} flag set to true.
* {@link EnableReactiveMethodSecurity#useAuthorizationManager()} flag set to true.
*
* @author Evgeniy Cheban
*/
@ -79,8 +79,7 @@ public class EnableAuthorizationManagerReactiveMethodSecurityTests {
.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
@ -340,6 +339,13 @@ public class EnableAuthorizationManagerReactiveMethodSecurityTests {
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() {
@ -458,7 +464,7 @@ public class EnableAuthorizationManagerReactiveMethodSecurityTests {
return publisher(Flux.just(data));
}
@EnableReactiveMethodSecurity(authorizationManager = true)
@EnableReactiveMethodSecurity(useAuthorizationManager = true)
static class Config {
ReactiveMessageService delegate = mock(ReactiveMessageService.class);

View File

@ -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);

View File

@ -52,7 +52,12 @@ import org.springframework.util.Assert;
* @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",

View File

@ -1,76 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authorization.method;
import java.lang.reflect.Method;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.core.Ordered;
/**
* A {@link MethodInterceptor} that wraps a {@link Mono} or a {@link Flux} using
* <code>deffer</code> call.
*
* @author Evgeniy Cheban
* @since 5.8
*/
final class AuthorizationAfterReactiveMethodInterceptor
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
private final Pointcut pointcut = AuthorizationMethodPointcuts.forAllAnnotations();
private final int order = AuthorizationInterceptorsOrder.LAST.getOrder();
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Method method = mi.getMethod();
Class<?> returnType = method.getReturnType();
if (Mono.class.isAssignableFrom(returnType)) {
return Mono.defer(() -> ReactiveMethodInvocationUtils.proceed(mi));
}
return Flux.defer(() -> ReactiveMethodInvocationUtils.proceed(mi));
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public Advice getAdvice() {
return this;
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public int getOrder() {
return this.order;
}
}

View File

@ -1,59 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authorization.method;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
/**
* Adds {@link AuthorizationBeforeReactiveMethodInterceptor} and
* {@link AuthorizationAfterReactiveMethodInterceptor} bean definitions to the
* {@link BeanDefinitionRegistry} if they have not already been added.
*
* @author Evgeniy Cheban
* @since 5.8
*/
final class AuthorizationBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
private static final String BEFORE_INTERCEPTOR_BEAN_NAME = "org.springframework.security.authorization.method.authorizationBeforeReactiveMethodInterceptor";
private static final String AFTER_INTERCEPTOR_BEAN_NAME = "org.springframework.security.authorization.method.authorizationAfterReactiveMethodInterceptor";
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (!registry.containsBeanDefinition(BEFORE_INTERCEPTOR_BEAN_NAME)) {
RootBeanDefinition beforeInterceptor = new RootBeanDefinition(
AuthorizationBeforeReactiveMethodInterceptor.class);
beforeInterceptor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEFORE_INTERCEPTOR_BEAN_NAME, beforeInterceptor);
}
if (!registry.containsBeanDefinition(AFTER_INTERCEPTOR_BEAN_NAME)) {
RootBeanDefinition afterInterceptor = new RootBeanDefinition(
AuthorizationAfterReactiveMethodInterceptor.class);
afterInterceptor.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(AFTER_INTERCEPTOR_BEAN_NAME, afterInterceptor);
}
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}
}

View File

@ -1,82 +0,0 @@
/*
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.security.authorization.method;
import java.lang.reflect.Method;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.reactivestreams.Publisher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.core.Ordered;
import org.springframework.core.ReactiveAdapter;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.util.Assert;
/**
* A {@link MethodInterceptor} which validates and transforms the return type for methods
* that return a {@link Publisher}.
*
* @author Evgeniy Cheban
* @since 5.8
*/
final class AuthorizationBeforeReactiveMethodInterceptor
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
private final Pointcut pointcut = AuthorizationMethodPointcuts.forAllAnnotations();
private final int order = AuthorizationInterceptorsOrder.FIRST.getOrder();
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Method method = mi.getMethod();
Class<?> returnType = method.getReturnType();
Assert.state(Publisher.class.isAssignableFrom(returnType),
() -> "The returnType " + returnType + " on " + method
+ " 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<?> publisher = ReactiveMethodInvocationUtils.proceed(mi);
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(returnType);
return (adapter != null) ? adapter.fromPublisher(publisher) : publisher;
}
@Override
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public Advice getAdvice() {
return this;
}
@Override
public boolean isPerInstance() {
return true;
}
@Override
public int getOrder() {
return this.order;
}
}

View File

@ -16,6 +16,9 @@
package org.springframework.security.authorization.method;
import java.lang.reflect.Method;
import java.util.function.Function;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@ -26,10 +29,9 @@ import reactor.core.publisher.Mono;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.ReactiveAdapter;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
@ -43,16 +45,14 @@ import org.springframework.util.Assert;
* @author Evgeniy Cheban
* @since 5.8
*/
public final class AuthorizationManagerAfterReactiveMethodInterceptor implements Ordered, MethodInterceptor,
PointcutAdvisor, AopInfrastructureBean, BeanDefinitionRegistryPostProcessor {
private final AuthorizationBeanFactoryPostProcessor beanFactoryPostProcessor = new AuthorizationBeanFactoryPostProcessor();
public final class AuthorizationManagerAfterReactiveMethodInterceptor
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
private final Pointcut pointcut;
private final ReactiveAuthorizationManager<MethodInvocationResult> authorizationManager;
private int order = AuthorizationInterceptorsOrder.POST_AUTHORIZE.getOrder();
private int order = AuthorizationInterceptorsOrder.LAST.getOrder();
/**
* Creates an instance for the {@link PostAuthorize} annotation.
@ -69,8 +69,10 @@ public final class AuthorizationManagerAfterReactiveMethodInterceptor implements
*/
public static AuthorizationManagerAfterReactiveMethodInterceptor postAuthorize(
ReactiveAuthorizationManager<MethodInvocationResult> authorizationManager) {
return new AuthorizationManagerAfterReactiveMethodInterceptor(
AuthorizationManagerAfterReactiveMethodInterceptor interceptor = new AuthorizationManagerAfterReactiveMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(PostAuthorize.class), authorizationManager);
interceptor.setOrder(AuthorizationInterceptorsOrder.POST_AUTHORIZE.getOrder());
return interceptor;
}
/**
@ -95,13 +97,28 @@ public final class AuthorizationManagerAfterReactiveMethodInterceptor implements
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Publisher<?> publisher = ReactiveMethodInvocationUtils.proceed(mi);
Method method = mi.getMethod();
Class<?> type = method.getReturnType();
Assert.state(Publisher.class.isAssignableFrom(type),
() -> String.format("The returnType %s on %s must return an instance of org.reactivestreams.Publisher "
+ "(for example, a Mono or Flux) in order to support Reactor Context", type, method));
Mono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();
if (publisher instanceof Mono<?>) {
Mono<?> mono = (Mono<?>) publisher;
return mono.flatMap((result) -> postAuthorize(authentication, mi, result));
Function<Object, Mono<?>> postAuthorize = (result) -> postAuthorize(authentication, mi, result);
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(type);
Publisher<?> publisher = ReactiveMethodInvocationUtils.proceed(mi);
if (isMultiValue(type, adapter)) {
Flux<?> flux = Flux.from(publisher).flatMap(postAuthorize);
return (adapter != null) ? adapter.fromPublisher(flux) : flux;
}
return Flux.from(publisher).flatMap((result) -> postAuthorize(authentication, mi, result));
Mono<?> mono = Mono.from(publisher).flatMap(postAuthorize);
return (adapter != null) ? adapter.fromPublisher(mono) : mono;
}
private boolean isMultiValue(Class<?> returnType, ReactiveAdapter adapter) {
if (Flux.class.isAssignableFrom(returnType)) {
return true;
}
return adapter == null || adapter.isMultiValue();
}
private Mono<?> postAuthorize(Mono<Authentication> authentication, MethodInvocation mi, Object result) {
@ -133,14 +150,4 @@ public final class AuthorizationManagerAfterReactiveMethodInterceptor implements
this.order = order;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
this.beanFactoryPostProcessor.postProcessBeanDefinitionRegistry(registry);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
this.beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
}
}

View File

@ -16,19 +16,21 @@
package org.springframework.security.authorization.method;
import java.lang.reflect.Method;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.ReactiveAdapter;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
@ -40,18 +42,17 @@ import org.springframework.util.Assert;
* {@link ReactiveAuthorizationManager}.
*
* @author Evgeniy Cheban
* @author Josh Cummings
* @since 5.8
*/
public final class AuthorizationManagerBeforeReactiveMethodInterceptor implements Ordered, MethodInterceptor,
PointcutAdvisor, AopInfrastructureBean, BeanDefinitionRegistryPostProcessor {
private final AuthorizationBeanFactoryPostProcessor beanFactoryPostProcessor = new AuthorizationBeanFactoryPostProcessor();
public final class AuthorizationManagerBeforeReactiveMethodInterceptor
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
private final Pointcut pointcut;
private final ReactiveAuthorizationManager<MethodInvocation> authorizationManager;
private int order = AuthorizationInterceptorsOrder.PRE_AUTHORIZE.getOrder();
private int order = AuthorizationInterceptorsOrder.FIRST.getOrder();
/**
* Creates an instance for the {@link PreAuthorize} annotation.
@ -68,8 +69,10 @@ public final class AuthorizationManagerBeforeReactiveMethodInterceptor implement
*/
public static AuthorizationManagerBeforeReactiveMethodInterceptor preAuthorize(
ReactiveAuthorizationManager<MethodInvocation> authorizationManager) {
return new AuthorizationManagerBeforeReactiveMethodInterceptor(
AuthorizationManagerBeforeReactiveMethodInterceptor interceptor = new AuthorizationManagerBeforeReactiveMethodInterceptor(
AuthorizationMethodPointcuts.forAnnotations(PreAuthorize.class), authorizationManager);
interceptor.setOrder(AuthorizationInterceptorsOrder.PRE_AUTHORIZE.getOrder());
return interceptor;
}
/**
@ -94,13 +97,29 @@ public final class AuthorizationManagerBeforeReactiveMethodInterceptor implement
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Publisher<?> publisher = ReactiveMethodInvocationUtils.proceed(mi);
Method method = mi.getMethod();
Class<?> type = method.getReturnType();
Assert.state(Publisher.class.isAssignableFrom(type),
() -> String.format("The returnType %s on %s must return an instance of org.reactivestreams.Publisher "
+ "(for example, a Mono or Flux) in order to support Reactor Context", type, method));
Mono<Authentication> authentication = ReactiveAuthenticationUtils.getAuthentication();
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(type);
Mono<Void> preAuthorize = this.authorizationManager.verify(authentication, mi);
if (publisher instanceof Mono<?>) {
return preAuthorize.then((Mono<?>) publisher);
if (isMultiValue(type, adapter)) {
Publisher<?> publisher = Flux.defer(() -> ReactiveMethodInvocationUtils.proceed(mi));
Flux<?> result = preAuthorize.thenMany(publisher);
return (adapter != null) ? adapter.fromPublisher(result) : result;
}
return preAuthorize.thenMany(publisher);
Mono<?> publisher = Mono.defer(() -> ReactiveMethodInvocationUtils.proceed(mi));
Mono<?> result = preAuthorize.then(publisher);
return (adapter != null) ? adapter.fromPublisher(result) : result;
}
private boolean isMultiValue(Class<?> returnType, ReactiveAdapter adapter) {
if (Flux.class.isAssignableFrom(returnType)) {
return true;
}
return adapter == null || adapter.isMultiValue();
}
@Override
@ -127,14 +146,4 @@ public final class AuthorizationManagerBeforeReactiveMethodInterceptor implement
this.order = order;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
this.beanFactoryPostProcessor.postProcessBeanDefinitionRegistry(registry);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
this.beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
}
}

View File

@ -39,14 +39,14 @@ import org.springframework.security.core.Authentication;
*/
public final class PostAuthorizeAuthorizationManager implements AuthorizationManager<MethodInvocationResult> {
private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
private PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
/**
* Use this the {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
this.registry.setExpressionHandler(expressionHandler);
this.registry = new PostAuthorizeExpressionAttributeRegistry(expressionHandler);
}
/**

View File

@ -35,17 +35,21 @@ import org.springframework.util.Assert;
*/
final class PostAuthorizeExpressionAttributeRegistry extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
private final MethodSecurityExpressionHandler expressionHandler;
PostAuthorizeExpressionAttributeRegistry() {
this(new DefaultMethodSecurityExpressionHandler());
}
PostAuthorizeExpressionAttributeRegistry(MethodSecurityExpressionHandler expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.expressionHandler = expressionHandler;
}
MethodSecurityExpressionHandler getExpressionHandler() {
return this.expressionHandler;
}
void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.expressionHandler = expressionHandler;
}
@NonNull
@Override
ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {

View File

@ -19,11 +19,13 @@ package org.springframework.security.authorization.method;
import org.aopalliance.intercept.MethodInvocation;
import reactor.core.publisher.Mono;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* A {@link ReactiveAuthorizationManager} which can determine if an {@link Authentication}
@ -36,14 +38,15 @@ import org.springframework.security.core.Authentication;
public final class PostAuthorizeReactiveAuthorizationManager
implements ReactiveAuthorizationManager<MethodInvocationResult> {
private final PostAuthorizeExpressionAttributeRegistry registry = new PostAuthorizeExpressionAttributeRegistry();
private final PostAuthorizeExpressionAttributeRegistry registry;
/**
* Sets the {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
this.registry.setExpressionHandler(expressionHandler);
public PostAuthorizeReactiveAuthorizationManager() {
this(new DefaultMethodSecurityExpressionHandler());
}
public PostAuthorizeReactiveAuthorizationManager(MethodSecurityExpressionHandler expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.registry = new PostAuthorizeExpressionAttributeRegistry(expressionHandler);
}
/**

View File

@ -49,7 +49,7 @@ public final class PostFilterAuthorizationMethodInterceptor
private Supplier<Authentication> authentication = getAuthentication(
SecurityContextHolder.getContextHolderStrategy());
private final PostFilterExpressionAttributeRegistry registry = new PostFilterExpressionAttributeRegistry();
private PostFilterExpressionAttributeRegistry registry = new PostFilterExpressionAttributeRegistry();
private int order = AuthorizationInterceptorsOrder.POST_FILTER.getOrder();
@ -68,7 +68,7 @@ public final class PostFilterAuthorizationMethodInterceptor
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
this.registry.setExpressionHandler(expressionHandler);
this.registry = new PostFilterExpressionAttributeRegistry(expressionHandler);
}
/**

View File

@ -16,6 +16,8 @@
package org.springframework.security.authorization.method;
import java.lang.reflect.Method;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@ -26,14 +28,15 @@ import reactor.core.publisher.Mono;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.ReactiveAdapter;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.expression.EvaluationContext;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
import org.springframework.security.access.prepost.PostFilter;
import org.springframework.util.Assert;
/**
* A {@link MethodInterceptor} which filters the returned object from the
@ -43,14 +46,12 @@ import org.springframework.security.access.prepost.PostFilter;
* @author Evgeniy Cheban
* @since 5.8
*/
public final class PostFilterAuthorizationReactiveMethodInterceptor implements Ordered, MethodInterceptor,
PointcutAdvisor, AopInfrastructureBean, BeanDefinitionRegistryPostProcessor {
public final class PostFilterAuthorizationReactiveMethodInterceptor
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
private final AuthorizationBeanFactoryPostProcessor beanFactoryPostProcessor = new AuthorizationBeanFactoryPostProcessor();
private final PostFilterExpressionAttributeRegistry registry;
private final PostFilterExpressionAttributeRegistry registry = new PostFilterExpressionAttributeRegistry();
private final Pointcut pointcut;
private final Pointcut pointcut = AuthorizationMethodPointcuts.forAnnotations(PostFilter.class);
private int order = AuthorizationInterceptorsOrder.POST_FILTER.getOrder();
@ -58,15 +59,15 @@ public final class PostFilterAuthorizationReactiveMethodInterceptor implements O
* Creates an instance.
*/
public PostFilterAuthorizationReactiveMethodInterceptor() {
this.pointcut = AuthorizationMethodPointcuts.forAnnotations(PostFilter.class);
this(new DefaultMethodSecurityExpressionHandler());
}
/**
* Sets the {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
* Creates an instance.
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
this.registry.setExpressionHandler(expressionHandler);
public PostFilterAuthorizationReactiveMethodInterceptor(MethodSecurityExpressionHandler expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.registry = new PostFilterExpressionAttributeRegistry(expressionHandler);
}
/**
@ -77,25 +78,41 @@ public final class PostFilterAuthorizationReactiveMethodInterceptor implements O
*/
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
Publisher<?> publisher = ReactiveMethodInvocationUtils.proceed(mi);
ExpressionAttribute attribute = this.registry.getAttribute(mi);
if (attribute == ExpressionAttribute.NULL_ATTRIBUTE) {
return publisher;
return ReactiveMethodInvocationUtils.proceed(mi);
}
Mono<EvaluationContext> toInvoke = ReactiveAuthenticationUtils.getAuthentication()
.map((auth) -> this.registry.getExpressionHandler().createEvaluationContext(auth, mi));
if (publisher instanceof Mono<?>) {
return toInvoke.flatMap((ctx) -> filterMono((Mono<?>) publisher, ctx, attribute));
Method method = mi.getMethod();
Class<?> type = method.getReturnType();
Assert.state(Publisher.class.isAssignableFrom(type),
() -> String.format("The parameter type %s on %s must be an instance of org.reactivestreams.Publisher "
+ "(for example, a Mono or Flux) in order to support Reactor Context", type, method));
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(type);
if (isMultiValue(type, adapter)) {
Publisher<?> publisher = Flux.defer(() -> ReactiveMethodInvocationUtils.proceed(mi));
Flux<?> flux = toInvoke.flatMapMany((ctx) -> filterMultiValue(publisher, ctx, attribute));
return (adapter != null) ? adapter.fromPublisher(flux) : flux;
}
return toInvoke.flatMapMany((ctx) -> filterPublisher(publisher, ctx, attribute));
Publisher<?> publisher = Mono.defer(() -> ReactiveMethodInvocationUtils.proceed(mi));
Mono<?> mono = toInvoke.flatMap((ctx) -> filterSingleValue(publisher, ctx, attribute));
return (adapter != null) ? adapter.fromPublisher(mono) : mono;
}
private Mono<?> filterMono(Mono<?> mono, EvaluationContext ctx, ExpressionAttribute attribute) {
return mono.doOnNext((result) -> setFilterObject(ctx, result))
private boolean isMultiValue(Class<?> returnType, ReactiveAdapter adapter) {
if (Flux.class.isAssignableFrom(returnType)) {
return true;
}
return adapter == null || adapter.isMultiValue();
}
private Mono<?> filterSingleValue(Publisher<?> publisher, EvaluationContext ctx, ExpressionAttribute attribute) {
return Mono.from(publisher).doOnNext((result) -> setFilterObject(ctx, result))
.flatMap((result) -> postFilter(ctx, result, attribute));
}
private Flux<?> filterPublisher(Publisher<?> publisher, EvaluationContext ctx, ExpressionAttribute attribute) {
private Flux<?> filterMultiValue(Publisher<?> publisher, EvaluationContext ctx, ExpressionAttribute attribute) {
return Flux.from(publisher).doOnNext((result) -> setFilterObject(ctx, result))
.flatMap((result) -> postFilter(ctx, result, attribute));
}
@ -133,14 +150,4 @@ public final class PostFilterAuthorizationReactiveMethodInterceptor implements O
this.order = order;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
this.beanFactoryPostProcessor.postProcessBeanDefinitionRegistry(registry);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
this.beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
}
}

View File

@ -34,17 +34,21 @@ import org.springframework.util.Assert;
*/
final class PostFilterExpressionAttributeRegistry extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
private final MethodSecurityExpressionHandler expressionHandler;
PostFilterExpressionAttributeRegistry() {
this.expressionHandler = new DefaultMethodSecurityExpressionHandler();
}
PostFilterExpressionAttributeRegistry(MethodSecurityExpressionHandler expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.expressionHandler = expressionHandler;
}
MethodSecurityExpressionHandler getExpressionHandler() {
return this.expressionHandler;
}
void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.expressionHandler = expressionHandler;
}
@NonNull
@Override
ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {

View File

@ -39,14 +39,14 @@ import org.springframework.security.core.Authentication;
*/
public final class PreAuthorizeAuthorizationManager implements AuthorizationManager<MethodInvocation> {
private final PreAuthorizeExpressionAttributeRegistry registry = new PreAuthorizeExpressionAttributeRegistry();
private PreAuthorizeExpressionAttributeRegistry registry = new PreAuthorizeExpressionAttributeRegistry();
/**
* Sets the {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
this.registry.setExpressionHandler(expressionHandler);
this.registry = new PreAuthorizeExpressionAttributeRegistry(expressionHandler);
}
/**

View File

@ -35,7 +35,16 @@ import org.springframework.util.Assert;
*/
final class PreAuthorizeExpressionAttributeRegistry extends AbstractExpressionAttributeRegistry<ExpressionAttribute> {
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
private final MethodSecurityExpressionHandler expressionHandler;
PreAuthorizeExpressionAttributeRegistry() {
this.expressionHandler = new DefaultMethodSecurityExpressionHandler();
}
PreAuthorizeExpressionAttributeRegistry(MethodSecurityExpressionHandler expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.expressionHandler = expressionHandler;
}
/**
* Returns the {@link MethodSecurityExpressionHandler}.
@ -45,15 +54,6 @@ final class PreAuthorizeExpressionAttributeRegistry extends AbstractExpressionAt
return this.expressionHandler;
}
/**
* Sets the {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.expressionHandler = expressionHandler;
}
@NonNull
@Override
ExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {

View File

@ -19,11 +19,13 @@ package org.springframework.security.authorization.method;
import org.aopalliance.intercept.MethodInvocation;
import reactor.core.publisher.Mono;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.util.Assert;
/**
* A {@link ReactiveAuthorizationManager} which can determine if an {@link Authentication}
@ -35,14 +37,15 @@ import org.springframework.security.core.Authentication;
*/
public final class PreAuthorizeReactiveAuthorizationManager implements ReactiveAuthorizationManager<MethodInvocation> {
private final PreAuthorizeExpressionAttributeRegistry registry = new PreAuthorizeExpressionAttributeRegistry();
private final PreAuthorizeExpressionAttributeRegistry registry;
/**
* Sets the {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
this.registry.setExpressionHandler(expressionHandler);
public PreAuthorizeReactiveAuthorizationManager() {
this(new DefaultMethodSecurityExpressionHandler());
}
public PreAuthorizeReactiveAuthorizationManager(MethodSecurityExpressionHandler expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.registry = new PreAuthorizeExpressionAttributeRegistry(expressionHandler);
}
/**

View File

@ -50,7 +50,7 @@ public final class PreFilterAuthorizationMethodInterceptor
private Supplier<Authentication> authentication = getAuthentication(
SecurityContextHolder.getContextHolderStrategy());
private final PreFilterExpressionAttributeRegistry registry = new PreFilterExpressionAttributeRegistry();
private PreFilterExpressionAttributeRegistry registry = new PreFilterExpressionAttributeRegistry();
private int order = AuthorizationInterceptorsOrder.PRE_FILTER.getOrder();
@ -69,7 +69,7 @@ public final class PreFilterAuthorizationMethodInterceptor
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
this.registry.setExpressionHandler(expressionHandler);
this.registry = new PreFilterExpressionAttributeRegistry(expressionHandler);
}
/**

View File

@ -29,15 +29,13 @@ import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.framework.AopInfrastructureBean;
import org.springframework.aop.support.AopUtils;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.core.Ordered;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.core.ReactiveAdapter;
import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations;
import org.springframework.security.access.prepost.PreFilter;
@ -52,12 +50,10 @@ import org.springframework.util.StringUtils;
* @author Evgeniy Cheban
* @since 5.8
*/
public final class PreFilterAuthorizationReactiveMethodInterceptor implements Ordered, MethodInterceptor,
PointcutAdvisor, AopInfrastructureBean, BeanDefinitionRegistryPostProcessor {
public final class PreFilterAuthorizationReactiveMethodInterceptor
implements Ordered, MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {
private final AuthorizationBeanFactoryPostProcessor beanFactoryPostProcessor = new AuthorizationBeanFactoryPostProcessor();
private final PreFilterExpressionAttributeRegistry registry = new PreFilterExpressionAttributeRegistry();
private final PreFilterExpressionAttributeRegistry registry;
private final Pointcut pointcut = AuthorizationMethodPointcuts.forAnnotations(PreFilter.class);
@ -65,12 +61,16 @@ public final class PreFilterAuthorizationReactiveMethodInterceptor implements Or
private int order = AuthorizationInterceptorsOrder.PRE_FILTER.getOrder();
public PreFilterAuthorizationReactiveMethodInterceptor() {
this(new DefaultMethodSecurityExpressionHandler());
}
/**
* Sets the {@link MethodSecurityExpressionHandler}.
* @param expressionHandler the {@link MethodSecurityExpressionHandler} to use
* Creates an instance.
*/
public void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
this.registry.setExpressionHandler(expressionHandler);
public PreFilterAuthorizationReactiveMethodInterceptor(MethodSecurityExpressionHandler expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.registry = new PreFilterExpressionAttributeRegistry(expressionHandler);
}
/**
@ -92,23 +92,28 @@ public final class PreFilterAuthorizationReactiveMethodInterceptor implements Or
public Object invoke(MethodInvocation mi) throws Throwable {
PreFilterExpressionAttributeRegistry.PreFilterExpressionAttribute attribute = this.registry.getAttribute(mi);
if (attribute == PreFilterExpressionAttributeRegistry.PreFilterExpressionAttribute.NULL_ATTRIBUTE) {
return ReactiveMethodInvocationUtils.<Publisher<?>>proceed(mi);
return ReactiveMethodInvocationUtils.proceed(mi);
}
FilterTarget filterTarget = findFilterTarget(attribute.getFilterTarget(), mi);
Mono<EvaluationContext> toInvoke = ReactiveAuthenticationUtils.getAuthentication()
.map((auth) -> this.registry.getExpressionHandler().createEvaluationContext(auth, mi));
if (filterTarget.value instanceof Mono<?>) {
mi.getArguments()[filterTarget.index] = toInvoke
.flatMap((ctx) -> filterMono((Mono<?>) filterTarget.value, attribute.getExpression(), ctx));
}
else {
Method method = mi.getMethod();
Class<?> type = filterTarget.value.getClass();
Assert.state(Publisher.class.isAssignableFrom(type),
() -> String.format("The parameter type %s on %s must be an instance of org.reactivestreams.Publisher "
+ "(for example, a Mono or Flux) in order to support Reactor Context", type, method));
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance().getAdapter(type);
if (isMultiValue(type, adapter)) {
Flux<?> result = toInvoke
.flatMapMany((ctx) -> filterPublisher(filterTarget.value, attribute.getExpression(), ctx));
ReactiveAdapter adapter = ReactiveAdapterRegistry.getSharedInstance()
.getAdapter(filterTarget.value.getClass());
.flatMapMany((ctx) -> filterMultiValue(filterTarget.value, attribute.getExpression(), ctx));
mi.getArguments()[filterTarget.index] = (adapter != null) ? adapter.fromPublisher(result) : result;
}
return ReactiveMethodInvocationUtils.<Publisher<?>>proceed(mi);
else {
Mono<?> result = toInvoke
.flatMap((ctx) -> filterSingleValue(filterTarget.value, attribute.getExpression(), ctx));
mi.getArguments()[filterTarget.index] = (adapter != null) ? adapter.fromPublisher(result) : result;
}
return ReactiveMethodInvocationUtils.proceed(mi);
}
private FilterTarget findFilterTarget(String name, MethodInvocation mi) {
@ -143,16 +148,23 @@ public final class PreFilterAuthorizationReactiveMethodInterceptor implements Or
return new FilterTarget((Publisher<?>) value, index);
}
private Mono<?> filterMono(Mono<?> filterTarget, Expression filterExpression, EvaluationContext ctx) {
private boolean isMultiValue(Class<?> returnType, ReactiveAdapter adapter) {
if (Flux.class.isAssignableFrom(returnType)) {
return true;
}
return adapter == null || adapter.isMultiValue();
}
private Mono<?> filterSingleValue(Publisher<?> filterTarget, Expression filterExpression, EvaluationContext ctx) {
MethodSecurityExpressionOperations rootObject = (MethodSecurityExpressionOperations) ctx.getRootObject()
.getValue();
return filterTarget.filterWhen((filterObject) -> {
return Mono.from(filterTarget).filterWhen((filterObject) -> {
rootObject.setFilterObject(filterObject);
return ReactiveExpressionUtils.evaluateAsBoolean(filterExpression, ctx);
});
}
private Flux<?> filterPublisher(Publisher<?> filterTarget, Expression filterExpression, EvaluationContext ctx) {
private Flux<?> filterMultiValue(Publisher<?> filterTarget, Expression filterExpression, EvaluationContext ctx) {
MethodSecurityExpressionOperations rootObject = (MethodSecurityExpressionOperations) ctx.getRootObject()
.getValue();
return Flux.from(filterTarget).filterWhen((filterObject) -> {
@ -185,16 +197,6 @@ public final class PreFilterAuthorizationReactiveMethodInterceptor implements Or
this.order = order;
}
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
this.beanFactoryPostProcessor.postProcessBeanDefinitionRegistry(registry);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
this.beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
}
private static final class FilterTarget {
private final Publisher<?> value;

View File

@ -35,17 +35,21 @@ import org.springframework.util.Assert;
final class PreFilterExpressionAttributeRegistry
extends AbstractExpressionAttributeRegistry<PreFilterExpressionAttributeRegistry.PreFilterExpressionAttribute> {
private MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
private final MethodSecurityExpressionHandler expressionHandler;
PreFilterExpressionAttributeRegistry() {
this.expressionHandler = new DefaultMethodSecurityExpressionHandler();
}
PreFilterExpressionAttributeRegistry(MethodSecurityExpressionHandler expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.expressionHandler = expressionHandler;
}
MethodSecurityExpressionHandler getExpressionHandler() {
return this.expressionHandler;
}
void setExpressionHandler(MethodSecurityExpressionHandler expressionHandler) {
Assert.notNull(expressionHandler, "expressionHandler cannot be null");
this.expressionHandler = expressionHandler;
}
@NonNull
@Override
PreFilterExpressionAttribute resolveAttribute(Method method, Class<?> targetClass) {

View File

@ -24,6 +24,7 @@ import reactor.core.publisher.Mono;
import org.springframework.aop.Pointcut;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import static org.assertj.core.api.Assertions.assertThat;
@ -32,6 +33,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@ -59,7 +61,8 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
@Test
public void invokeMonoWhenMockReactiveAuthorizationManagerThenVerify() throws Throwable {
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
MethodInvocation mockMethodInvocation = spy(
new MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod("mono")));
given(mockMethodInvocation.proceed()).willReturn(Mono.just("john"));
ReactiveAuthorizationManager<MethodInvocationResult> mockReactiveAuthorizationManager = mock(
ReactiveAuthorizationManager.class);
@ -74,7 +77,8 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
@Test
public void invokeFluxWhenMockReactiveAuthorizationManagerThenVerify() throws Throwable {
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
MethodInvocation mockMethodInvocation = spy(
new MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod("flux")));
given(mockMethodInvocation.proceed()).willReturn(Flux.just("john", "bob"));
ReactiveAuthorizationManager<MethodInvocationResult> mockReactiveAuthorizationManager = mock(
ReactiveAuthorizationManager.class);
@ -89,7 +93,8 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
@Test
public void invokeWhenMockReactiveAuthorizationManagerDeniedThenAccessDeniedException() throws Throwable {
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
MethodInvocation mockMethodInvocation = spy(
new MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod("mono")));
given(mockMethodInvocation.proceed()).willReturn(Mono.just("john"));
ReactiveAuthorizationManager<MethodInvocationResult> mockReactiveAuthorizationManager = mock(
ReactiveAuthorizationManager.class);
@ -104,4 +109,16 @@ public class AuthorizationManagerAfterReactiveMethodInterceptorTests {
verify(mockReactiveAuthorizationManager).verify(any(), any());
}
class Sample {
Mono<String> mono() {
return Mono.just("john");
}
Flux<String> flux() {
return Flux.just("john", "bob");
}
}
}

View File

@ -24,6 +24,7 @@ import reactor.core.publisher.Mono;
import org.springframework.aop.Pointcut;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.intercept.method.MockMethodInvocation;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import static org.assertj.core.api.Assertions.assertThat;
@ -33,6 +34,7 @@ import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
/**
@ -60,7 +62,8 @@ public class AuthorizationManagerBeforeReactiveMethodInterceptorTests {
@Test
public void invokeMonoWhenMockReactiveAuthorizationManagerThenVerify() throws Throwable {
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
MethodInvocation mockMethodInvocation = spy(
new MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod("mono")));
given(mockMethodInvocation.proceed()).willReturn(Mono.just("john"));
ReactiveAuthorizationManager<MethodInvocation> mockReactiveAuthorizationManager = mock(
ReactiveAuthorizationManager.class);
@ -75,7 +78,8 @@ public class AuthorizationManagerBeforeReactiveMethodInterceptorTests {
@Test
public void invokeFluxWhenMockReactiveAuthorizationManagerThenVerify() throws Throwable {
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
MethodInvocation mockMethodInvocation = spy(
new MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod("flux")));
given(mockMethodInvocation.proceed()).willReturn(Flux.just("john", "bob"));
ReactiveAuthorizationManager<MethodInvocation> mockReactiveAuthorizationManager = mock(
ReactiveAuthorizationManager.class);
@ -90,7 +94,8 @@ public class AuthorizationManagerBeforeReactiveMethodInterceptorTests {
@Test
public void invokeWhenMockReactiveAuthorizationManagerDeniedThenAccessDeniedException() throws Throwable {
MethodInvocation mockMethodInvocation = mock(MethodInvocation.class);
MethodInvocation mockMethodInvocation = spy(
new MockMethodInvocation(new Sample(), Sample.class.getDeclaredMethod("mono")));
given(mockMethodInvocation.proceed()).willReturn(Mono.just("john"));
ReactiveAuthorizationManager<MethodInvocation> mockReactiveAuthorizationManager = mock(
ReactiveAuthorizationManager.class);
@ -105,4 +110,16 @@ public class AuthorizationManagerBeforeReactiveMethodInterceptorTests {
verify(mockReactiveAuthorizationManager).verify(any(), eq(mockMethodInvocation));
}
class Sample {
Mono<String> mono() {
return Mono.just("john");
}
Flux<String> flux() {
return Flux.just("john", "bob");
}
}
}

View File

@ -48,15 +48,14 @@ public class PostAuthorizeReactiveAuthorizationManagerTests {
@Test
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PostAuthorizeReactiveAuthorizationManager manager = new PostAuthorizeReactiveAuthorizationManager();
manager.setExpressionHandler(expressionHandler);
PostAuthorizeReactiveAuthorizationManager manager = new PostAuthorizeReactiveAuthorizationManager(
expressionHandler);
assertThat(manager).extracting("registry").extracting("expressionHandler").isEqualTo(expressionHandler);
}
@Test
public void setExpressionHandlerWhenNullThenException() {
PostAuthorizeReactiveAuthorizationManager manager = new PostAuthorizeReactiveAuthorizationManager();
assertThatIllegalArgumentException().isThrownBy(() -> manager.setExpressionHandler(null))
assertThatIllegalArgumentException().isThrownBy(() -> new PostAuthorizeReactiveAuthorizationManager(null))
.withMessage("expressionHandler cannot be null");
}

View File

@ -44,15 +44,15 @@ public class PostFilterAuthorizationReactiveMethodInterceptorTests {
@Test
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PostFilterAuthorizationReactiveMethodInterceptor interceptor = new PostFilterAuthorizationReactiveMethodInterceptor();
interceptor.setExpressionHandler(expressionHandler);
PostFilterAuthorizationReactiveMethodInterceptor interceptor = new PostFilterAuthorizationReactiveMethodInterceptor(
expressionHandler);
assertThat(interceptor).extracting("registry").extracting("expressionHandler").isEqualTo(expressionHandler);
}
@Test
public void setExpressionHandlerWhenNullThenException() {
PostFilterAuthorizationReactiveMethodInterceptor interceptor = new PostFilterAuthorizationReactiveMethodInterceptor();
assertThatIllegalArgumentException().isThrownBy(() -> interceptor.setExpressionHandler(null))
assertThatIllegalArgumentException()
.isThrownBy(() -> new PostFilterAuthorizationReactiveMethodInterceptor(null))
.withMessage("expressionHandler cannot be null");
}

View File

@ -45,15 +45,14 @@ public class PreAuthorizeReactiveAuthorizationManagerTests {
@Test
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PreAuthorizeReactiveAuthorizationManager manager = new PreAuthorizeReactiveAuthorizationManager();
manager.setExpressionHandler(expressionHandler);
PreAuthorizeReactiveAuthorizationManager manager = new PreAuthorizeReactiveAuthorizationManager(
expressionHandler);
assertThat(manager).extracting("registry").extracting("expressionHandler").isEqualTo(expressionHandler);
}
@Test
public void setExpressionHandlerWhenNullThenException() {
PreAuthorizeReactiveAuthorizationManager manager = new PreAuthorizeReactiveAuthorizationManager();
assertThatIllegalArgumentException().isThrownBy(() -> manager.setExpressionHandler(null))
assertThatIllegalArgumentException().isThrownBy(() -> new PreAuthorizeReactiveAuthorizationManager(null))
.withMessage("expressionHandler cannot be null");
}

View File

@ -46,15 +46,14 @@ public class PreFilterAuthorizationReactiveMethodInterceptorTests {
@Test
public void setExpressionHandlerWhenNotNullThenSetsExpressionHandler() {
MethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
PreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor();
interceptor.setExpressionHandler(expressionHandler);
PreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor(
expressionHandler);
assertThat(interceptor).extracting("registry").extracting("expressionHandler").isEqualTo(expressionHandler);
}
@Test
public void setExpressionHandlerWhenNullThenException() {
PreFilterAuthorizationReactiveMethodInterceptor interceptor = new PreFilterAuthorizationReactiveMethodInterceptor();
assertThatIllegalArgumentException().isThrownBy(() -> interceptor.setExpressionHandler(null))
assertThatIllegalArgumentException().isThrownBy(() -> new PreFilterAuthorizationReactiveMethodInterceptor(null))
.withMessage("expressionHandler cannot be null");
}