parent
b60e037005
commit
795e44d11f
|
@ -30,16 +30,19 @@ import org.springframework.context.annotation.Role;
|
|||
import org.springframework.security.authorization.AuthorizationAdvisorProxyFactory;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisor;
|
||||
import org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor;
|
||||
import org.springframework.security.config.Customizer;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
final class AuthorizationProxyConfiguration implements AopInfrastructureBean {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static AuthorizationAdvisorProxyFactory authorizationProxyFactory(ObjectProvider<AuthorizationAdvisor> provider) {
|
||||
static AuthorizationAdvisorProxyFactory authorizationProxyFactory(ObjectProvider<AuthorizationAdvisor> provider,
|
||||
ObjectProvider<Customizer<AuthorizationAdvisorProxyFactory>> customizers) {
|
||||
List<AuthorizationAdvisor> advisors = new ArrayList<>();
|
||||
provider.forEach(advisors::add);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
customizers.forEach((c) -> c.customize(factory));
|
||||
factory.setAdvisors(advisors);
|
||||
return factory;
|
||||
}
|
||||
|
|
|
@ -27,20 +27,22 @@ import org.springframework.beans.factory.config.BeanDefinition;
|
|||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.security.authorization.ReactiveAuthorizationAdvisorProxyFactory;
|
||||
import org.springframework.security.authorization.AuthorizationAdvisorProxyFactory;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisor;
|
||||
import org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor;
|
||||
import org.springframework.security.config.Customizer;
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
final class ReactiveAuthorizationProxyConfiguration implements AopInfrastructureBean {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static ReactiveAuthorizationAdvisorProxyFactory authorizationProxyFactory(
|
||||
ObjectProvider<AuthorizationAdvisor> provider) {
|
||||
static AuthorizationAdvisorProxyFactory authorizationProxyFactory(ObjectProvider<AuthorizationAdvisor> provider,
|
||||
ObjectProvider<Customizer<AuthorizationAdvisorProxyFactory>> customizers) {
|
||||
List<AuthorizationAdvisor> advisors = new ArrayList<>();
|
||||
provider.forEach(advisors::add);
|
||||
ReactiveAuthorizationAdvisorProxyFactory factory = new ReactiveAuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();
|
||||
customizers.forEach((c) -> c.customize(factory));
|
||||
factory.setAdvisors(advisors);
|
||||
return factory;
|
||||
}
|
||||
|
@ -48,7 +50,7 @@ final class ReactiveAuthorizationProxyConfiguration implements AopInfrastructure
|
|||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static MethodInterceptor authorizeReturnObjectMethodInterceptor(ObjectProvider<AuthorizationAdvisor> provider,
|
||||
ReactiveAuthorizationAdvisorProxyFactory authorizationProxyFactory) {
|
||||
AuthorizationAdvisorProxyFactory authorizationProxyFactory) {
|
||||
AuthorizeReturnObjectMethodInterceptor interceptor = new AuthorizeReturnObjectMethodInterceptor(
|
||||
authorizationProxyFactory);
|
||||
List<AuthorizationAdvisor> advisors = new ArrayList<>();
|
||||
|
|
|
@ -58,6 +58,8 @@ import org.springframework.security.access.prepost.PostAuthorize;
|
|||
import org.springframework.security.access.prepost.PostFilter;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.access.prepost.PreFilter;
|
||||
import org.springframework.security.authorization.AuthorizationAdvisorProxyFactory;
|
||||
import org.springframework.security.authorization.AuthorizationAdvisorProxyFactory.TargetVisitor;
|
||||
import org.springframework.security.authorization.AuthorizationDecision;
|
||||
import org.springframework.security.authorization.AuthorizationEventPublisher;
|
||||
import org.springframework.security.authorization.AuthorizationManager;
|
||||
|
@ -66,6 +68,7 @@ import org.springframework.security.authorization.method.AuthorizationManagerBef
|
|||
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
||||
import org.springframework.security.authorization.method.MethodInvocationResult;
|
||||
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.annotation.SecurityContextChangedListenerConfig;
|
||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
|
@ -1143,6 +1146,12 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||
@Configuration
|
||||
static class AuthorizeResultConfig {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static Customizer<AuthorizationAdvisorProxyFactory> skipValueTypes() {
|
||||
return (f) -> f.setTargetVisitor(TargetVisitor.defaultsSkipValueTypes());
|
||||
}
|
||||
|
||||
@Bean
|
||||
FlightRepository flights() {
|
||||
FlightRepository flights = new FlightRepository();
|
||||
|
@ -1186,6 +1195,7 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@AuthorizeReturnObject
|
||||
static class Flight {
|
||||
|
||||
private final String id;
|
||||
|
@ -1216,7 +1226,6 @@ public class PrePostMethodSecurityConfigurationTests {
|
|||
return this.seats;
|
||||
}
|
||||
|
||||
@AuthorizeReturnObject
|
||||
@PostAuthorize("hasAuthority('seating:read')")
|
||||
@PostFilter("filterObject.name != 'Kevin Mitnick'")
|
||||
List<Passenger> getPassengers() {
|
||||
|
|
|
@ -30,8 +30,10 @@ import reactor.core.publisher.Mono;
|
|||
import reactor.test.StepVerifier;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Role;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.expression.SecurityExpressionRoot;
|
||||
|
@ -42,7 +44,10 @@ import org.springframework.security.access.prepost.PostFilter;
|
|||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.access.prepost.PreFilter;
|
||||
import org.springframework.security.authentication.TestAuthentication;
|
||||
import org.springframework.security.authorization.AuthorizationAdvisorProxyFactory;
|
||||
import org.springframework.security.authorization.AuthorizationAdvisorProxyFactory.TargetVisitor;
|
||||
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
||||
import org.springframework.security.config.Customizer;
|
||||
import org.springframework.security.config.core.GrantedAuthorityDefaults;
|
||||
import org.springframework.security.config.test.SpringTestContext;
|
||||
import org.springframework.security.config.test.SpringTestContextExtension;
|
||||
|
@ -238,6 +243,12 @@ public class ReactiveMethodSecurityConfigurationTests {
|
|||
@Configuration
|
||||
static class AuthorizeResultConfig {
|
||||
|
||||
@Bean
|
||||
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
|
||||
static Customizer<AuthorizationAdvisorProxyFactory> skipValueTypes() {
|
||||
return (factory) -> factory.setTargetVisitor(TargetVisitor.defaultsSkipValueTypes());
|
||||
}
|
||||
|
||||
@Bean
|
||||
FlightRepository flights() {
|
||||
FlightRepository flights = new FlightRepository();
|
||||
|
@ -282,6 +293,7 @@ public class ReactiveMethodSecurityConfigurationTests {
|
|||
|
||||
}
|
||||
|
||||
@AuthorizeReturnObject
|
||||
static class Flight {
|
||||
|
||||
private final String id;
|
||||
|
@ -312,7 +324,6 @@ public class ReactiveMethodSecurityConfigurationTests {
|
|||
return Mono.just(this.seats);
|
||||
}
|
||||
|
||||
@AuthorizeReturnObject
|
||||
@PostAuthorize("hasAnyAuthority('seating:read', 'airplane:read')")
|
||||
@PostFilter("@isNotKevin.apply(filterObject)")
|
||||
Flux<Passenger> getPassengers() {
|
||||
|
|
|
@ -34,17 +34,28 @@ import java.util.SortedMap;
|
|||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.aop.Advisor;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisor;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerAfterReactiveMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeReactiveMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.AuthorizeReturnObject;
|
||||
import org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PostFilterAuthorizationReactiveMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PreFilterAuthorizationReactiveMethodInterceptor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
|
@ -63,8 +74,7 @@ import org.springframework.util.ClassUtils;
|
|||
* like so:
|
||||
*
|
||||
* <pre>
|
||||
* AuthorizationManagerBeforeMethodInterceptor preAuthorize = AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
|
||||
* AuthorizationProxyFactory proxyFactory = new AuthorizationProxyFactory(preAuthorize);
|
||||
* AuthorizationProxyFactory proxyFactory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
* Foo foo = new Foo();
|
||||
* foo.bar(); // passes
|
||||
* Foo securedFoo = proxyFactory.proxy(foo);
|
||||
|
@ -74,18 +84,56 @@ import org.springframework.util.ClassUtils;
|
|||
* @author Josh Cummings
|
||||
* @since 6.3
|
||||
*/
|
||||
public final class AuthorizationAdvisorProxyFactory implements AuthorizationProxyFactory {
|
||||
public final class AuthorizationAdvisorProxyFactory
|
||||
implements AuthorizationProxyFactory, Iterable<AuthorizationAdvisor> {
|
||||
|
||||
private List<AuthorizationAdvisor> advisors = new ArrayList<>();
|
||||
private static final boolean isReactivePresent = ClassUtils.isPresent("reactor.core.publisher.Mono", null);
|
||||
|
||||
public AuthorizationAdvisorProxyFactory() {
|
||||
private static final TargetVisitor DEFAULT_VISITOR = isReactivePresent
|
||||
? TargetVisitor.of(new ClassVisitor(), new ReactiveTypeVisitor(), new ContainerTypeVisitor())
|
||||
: TargetVisitor.of(new ClassVisitor(), new ContainerTypeVisitor());
|
||||
|
||||
private static final TargetVisitor DEFAULT_VISITOR_SKIP_VALUE_TYPES = TargetVisitor.of(new ClassVisitor(),
|
||||
new IgnoreValueTypeVisitor(), DEFAULT_VISITOR);
|
||||
|
||||
private List<AuthorizationAdvisor> advisors;
|
||||
|
||||
private TargetVisitor visitor = DEFAULT_VISITOR;
|
||||
|
||||
private AuthorizationAdvisorProxyFactory(List<AuthorizationAdvisor> advisors) {
|
||||
this.advisors = new ArrayList<>(advisors);
|
||||
this.advisors.add(new AuthorizeReturnObjectMethodInterceptor(this));
|
||||
setAdvisors(this.advisors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an {@link AuthorizationAdvisorProxyFactory} with the defaults needed for
|
||||
* wrapping objects in Spring Security's pre-post method security support.
|
||||
* @return an {@link AuthorizationAdvisorProxyFactory} for adding pre-post method
|
||||
* security support
|
||||
*/
|
||||
public static AuthorizationAdvisorProxyFactory withDefaults() {
|
||||
List<AuthorizationAdvisor> advisors = new ArrayList<>();
|
||||
advisors.add(AuthorizationManagerBeforeMethodInterceptor.preAuthorize());
|
||||
advisors.add(AuthorizationManagerAfterMethodInterceptor.postAuthorize());
|
||||
advisors.add(new PreFilterAuthorizationMethodInterceptor());
|
||||
advisors.add(new PostFilterAuthorizationMethodInterceptor());
|
||||
advisors.add(new AuthorizeReturnObjectMethodInterceptor(this));
|
||||
setAdvisors(advisors);
|
||||
return new AuthorizationAdvisorProxyFactory(advisors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct an {@link AuthorizationAdvisorProxyFactory} with the defaults needed for
|
||||
* wrapping objects in Spring Security's pre-post reactive method security support.
|
||||
* @return an {@link AuthorizationAdvisorProxyFactory} for adding pre-post reactive
|
||||
* method security support
|
||||
*/
|
||||
public static AuthorizationAdvisorProxyFactory withReactiveDefaults() {
|
||||
List<AuthorizationAdvisor> advisors = new ArrayList<>();
|
||||
advisors.add(AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize());
|
||||
advisors.add(AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize());
|
||||
advisors.add(new PreFilterAuthorizationReactiveMethodInterceptor());
|
||||
advisors.add(new PostFilterAuthorizationReactiveMethodInterceptor());
|
||||
return new AuthorizationAdvisorProxyFactory(advisors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -111,41 +159,9 @@ public final class AuthorizationAdvisorProxyFactory implements AuthorizationProx
|
|||
if (target == null) {
|
||||
return null;
|
||||
}
|
||||
if (target instanceof Class<?> targetClass) {
|
||||
return proxyClass(targetClass);
|
||||
}
|
||||
if (target instanceof Iterator<?> iterator) {
|
||||
return proxyIterator(iterator);
|
||||
}
|
||||
if (target instanceof Queue<?> queue) {
|
||||
return proxyQueue(queue);
|
||||
}
|
||||
if (target instanceof List<?> list) {
|
||||
return proxyList(list);
|
||||
}
|
||||
if (target instanceof SortedSet<?> set) {
|
||||
return proxySortedSet(set);
|
||||
}
|
||||
if (target instanceof Set<?> set) {
|
||||
return proxySet(set);
|
||||
}
|
||||
if (target.getClass().isArray()) {
|
||||
return proxyArray((Object[]) target);
|
||||
}
|
||||
if (target instanceof SortedMap<?, ?> map) {
|
||||
return proxySortedMap(map);
|
||||
}
|
||||
if (target instanceof Iterable<?> iterable) {
|
||||
return proxyIterable(iterable);
|
||||
}
|
||||
if (target instanceof Map<?, ?> map) {
|
||||
return proxyMap(map);
|
||||
}
|
||||
if (target instanceof Stream<?> stream) {
|
||||
return proxyStream(stream);
|
||||
}
|
||||
if (target instanceof Optional<?> optional) {
|
||||
return proxyOptional(optional);
|
||||
Object proxied = this.visitor.visit(this, target);
|
||||
if (proxied != null) {
|
||||
return proxied;
|
||||
}
|
||||
ProxyFactory factory = new ProxyFactory(target);
|
||||
for (Advisor advisor : this.advisors) {
|
||||
|
@ -179,27 +195,175 @@ public final class AuthorizationAdvisorProxyFactory implements AuthorizationProx
|
|||
AnnotationAwareOrderComparator.sort(this.advisors);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T proxyCast(T target) {
|
||||
return (T) proxy(target);
|
||||
/**
|
||||
* Use this visitor to navigate the proxy target's hierarchy.
|
||||
*
|
||||
* <p>
|
||||
* This can be helpful when you want a specialized behavior for a type or set of
|
||||
* types. For example, if you want to have this factory skip primitives and wrappers,
|
||||
* then you can do:
|
||||
*
|
||||
* <pre>
|
||||
* AuthorizationAdvisorProxyFactory proxyFactory = new AuthorizationAdvisorProxyFactory();
|
||||
* proxyFactory.setTargetVisitor(AuthorizationAdvisorProxyFactory.DEFAULT_VISITOR_IGNORE_VALUE_TYPES);
|
||||
* </pre>
|
||||
* @param visitor the visitor to use to introduce specialized behavior for a type
|
||||
*/
|
||||
public void setTargetVisitor(TargetVisitor visitor) {
|
||||
Assert.notNull(visitor, "delegate cannot be null");
|
||||
this.visitor = visitor;
|
||||
}
|
||||
|
||||
private Class<?> proxyClass(Class<?> targetClass) {
|
||||
@Override
|
||||
@NonNull
|
||||
public Iterator<AuthorizationAdvisor> iterator() {
|
||||
return this.advisors.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface to handle how the {@link AuthorizationAdvisorProxyFactory} should step
|
||||
* through the target's object hierarchy.
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.3
|
||||
* @see AuthorizationAdvisorProxyFactory#setTargetVisitor
|
||||
*/
|
||||
public interface TargetVisitor {
|
||||
|
||||
/**
|
||||
* Visit and possibly proxy this object.
|
||||
*
|
||||
* <p>
|
||||
* Visiting may take the form of walking down this object's hierarchy and proxying
|
||||
* sub-objects.
|
||||
*
|
||||
* <p>
|
||||
* An example is a visitor that proxies the elements of a {@link List} instead of
|
||||
* the list itself
|
||||
*
|
||||
* <p>
|
||||
* Returning {@code null} implies that this visitor does not want to proxy this
|
||||
* object
|
||||
* @param proxyFactory the proxy factory to delegate proxying to for any
|
||||
* sub-objects
|
||||
* @param target the object to proxy
|
||||
* @return the visited (and possibly proxied) object
|
||||
*/
|
||||
Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target);
|
||||
|
||||
/**
|
||||
* The default {@link TargetVisitor}, which will proxy {@link Class} instances as
|
||||
* well as instances contained in reactive types (if reactor is present),
|
||||
* collection types, and other container types like {@link Optional}
|
||||
*/
|
||||
static TargetVisitor defaults() {
|
||||
return AuthorizationAdvisorProxyFactory.DEFAULT_VISITOR;
|
||||
}
|
||||
|
||||
/**
|
||||
* The default {@link TargetVisitor} that also skips any value types (for example,
|
||||
* {@link String}, {@link Integer}). This is handy for annotations like
|
||||
* {@link AuthorizeReturnObject} when used at the class level
|
||||
*/
|
||||
static TargetVisitor defaultsSkipValueTypes() {
|
||||
return AuthorizationAdvisorProxyFactory.DEFAULT_VISITOR_SKIP_VALUE_TYPES;
|
||||
}
|
||||
|
||||
static TargetVisitor of(TargetVisitor... visitors) {
|
||||
return (proxyFactory, target) -> {
|
||||
for (TargetVisitor visitor : visitors) {
|
||||
Object result = visitor.visit(proxyFactory, target);
|
||||
if (result != null) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class IgnoreValueTypeVisitor implements TargetVisitor {
|
||||
|
||||
@Override
|
||||
public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object object) {
|
||||
if (ClassUtils.isSimpleValueType(object.getClass())) {
|
||||
return object;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static final class ClassVisitor implements TargetVisitor {
|
||||
|
||||
@Override
|
||||
public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object object) {
|
||||
if (object instanceof Class<?> targetClass) {
|
||||
ProxyFactory factory = new ProxyFactory();
|
||||
factory.setTargetClass(targetClass);
|
||||
factory.setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass));
|
||||
factory.setProxyTargetClass(!Modifier.isFinal(targetClass.getModifiers()));
|
||||
for (Advisor advisor : this.advisors) {
|
||||
for (Advisor advisor : proxyFactory) {
|
||||
factory.addAdvisors(advisor);
|
||||
}
|
||||
return factory.getProxyClass(getClass().getClassLoader());
|
||||
}
|
||||
|
||||
private <T> Iterable<T> proxyIterable(Iterable<T> iterable) {
|
||||
return () -> proxyIterator(iterable.iterator());
|
||||
return null;
|
||||
}
|
||||
|
||||
private <T> Iterator<T> proxyIterator(Iterator<T> iterator) {
|
||||
}
|
||||
|
||||
private static final class ContainerTypeVisitor implements TargetVisitor {
|
||||
|
||||
@Override
|
||||
public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {
|
||||
if (target instanceof Iterator<?> iterator) {
|
||||
return proxyIterator(proxyFactory, iterator);
|
||||
}
|
||||
if (target instanceof Queue<?> queue) {
|
||||
return proxyQueue(proxyFactory, queue);
|
||||
}
|
||||
if (target instanceof List<?> list) {
|
||||
return proxyList(proxyFactory, list);
|
||||
}
|
||||
if (target instanceof SortedSet<?> set) {
|
||||
return proxySortedSet(proxyFactory, set);
|
||||
}
|
||||
if (target instanceof Set<?> set) {
|
||||
return proxySet(proxyFactory, set);
|
||||
}
|
||||
if (target.getClass().isArray()) {
|
||||
return proxyArray(proxyFactory, (Object[]) target);
|
||||
}
|
||||
if (target instanceof SortedMap<?, ?> map) {
|
||||
return proxySortedMap(proxyFactory, map);
|
||||
}
|
||||
if (target instanceof Iterable<?> iterable) {
|
||||
return proxyIterable(proxyFactory, iterable);
|
||||
}
|
||||
if (target instanceof Map<?, ?> map) {
|
||||
return proxyMap(proxyFactory, map);
|
||||
}
|
||||
if (target instanceof Stream<?> stream) {
|
||||
return proxyStream(proxyFactory, stream);
|
||||
}
|
||||
if (target instanceof Optional<?> optional) {
|
||||
return proxyOptional(proxyFactory, optional);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T> T proxyCast(AuthorizationProxyFactory proxyFactory, T target) {
|
||||
return (T) proxyFactory.proxy(target);
|
||||
}
|
||||
|
||||
private <T> Iterable<T> proxyIterable(AuthorizationProxyFactory proxyFactory, Iterable<T> iterable) {
|
||||
return () -> proxyIterator(proxyFactory, iterable.iterator());
|
||||
}
|
||||
|
||||
private <T> Iterator<T> proxyIterator(AuthorizationProxyFactory proxyFactory, Iterator<T> iterator) {
|
||||
return new Iterator<>() {
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
|
@ -208,15 +372,15 @@ public final class AuthorizationAdvisorProxyFactory implements AuthorizationProx
|
|||
|
||||
@Override
|
||||
public T next() {
|
||||
return proxyCast(iterator.next());
|
||||
return proxyCast(proxyFactory, iterator.next());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private <T> SortedSet<T> proxySortedSet(SortedSet<T> set) {
|
||||
private <T> SortedSet<T> proxySortedSet(AuthorizationProxyFactory proxyFactory, SortedSet<T> set) {
|
||||
SortedSet<T> proxies = new TreeSet<>(set.comparator());
|
||||
for (T toProxy : set) {
|
||||
proxies.add(proxyCast(toProxy));
|
||||
proxies.add(proxyCast(proxyFactory, toProxy));
|
||||
}
|
||||
try {
|
||||
set.clear();
|
||||
|
@ -228,10 +392,10 @@ public final class AuthorizationAdvisorProxyFactory implements AuthorizationProx
|
|||
}
|
||||
}
|
||||
|
||||
private <T> Set<T> proxySet(Set<T> set) {
|
||||
private <T> Set<T> proxySet(AuthorizationProxyFactory proxyFactory, Set<T> set) {
|
||||
Set<T> proxies = new LinkedHashSet<>(set.size());
|
||||
for (T toProxy : set) {
|
||||
proxies.add(proxyCast(toProxy));
|
||||
proxies.add(proxyCast(proxyFactory, toProxy));
|
||||
}
|
||||
try {
|
||||
set.clear();
|
||||
|
@ -243,20 +407,20 @@ public final class AuthorizationAdvisorProxyFactory implements AuthorizationProx
|
|||
}
|
||||
}
|
||||
|
||||
private <T> Queue<T> proxyQueue(Queue<T> queue) {
|
||||
private <T> Queue<T> proxyQueue(AuthorizationProxyFactory proxyFactory, Queue<T> queue) {
|
||||
Queue<T> proxies = new LinkedList<>();
|
||||
for (T toProxy : queue) {
|
||||
proxies.add(proxyCast(toProxy));
|
||||
proxies.add(proxyCast(proxyFactory, toProxy));
|
||||
}
|
||||
queue.clear();
|
||||
queue.addAll(proxies);
|
||||
return proxies;
|
||||
}
|
||||
|
||||
private <T> List<T> proxyList(List<T> list) {
|
||||
private <T> List<T> proxyList(AuthorizationProxyFactory proxyFactory, List<T> list) {
|
||||
List<T> proxies = new ArrayList<>(list.size());
|
||||
for (T toProxy : list) {
|
||||
proxies.add(proxyCast(toProxy));
|
||||
proxies.add(proxyCast(proxyFactory, toProxy));
|
||||
}
|
||||
try {
|
||||
list.clear();
|
||||
|
@ -268,10 +432,10 @@ public final class AuthorizationAdvisorProxyFactory implements AuthorizationProx
|
|||
}
|
||||
}
|
||||
|
||||
private Object[] proxyArray(Object[] objects) {
|
||||
private Object[] proxyArray(AuthorizationProxyFactory proxyFactory, Object[] objects) {
|
||||
List<Object> retain = new ArrayList<>(objects.length);
|
||||
for (Object object : objects) {
|
||||
retain.add(proxy(object));
|
||||
retain.add(proxyFactory.proxy(object));
|
||||
}
|
||||
Object[] proxies = (Object[]) Array.newInstance(objects.getClass().getComponentType(), retain.size());
|
||||
for (int i = 0; i < retain.size(); i++) {
|
||||
|
@ -280,10 +444,10 @@ public final class AuthorizationAdvisorProxyFactory implements AuthorizationProx
|
|||
return proxies;
|
||||
}
|
||||
|
||||
private <K, V> SortedMap<K, V> proxySortedMap(SortedMap<K, V> entries) {
|
||||
private <K, V> SortedMap<K, V> proxySortedMap(AuthorizationProxyFactory proxyFactory, SortedMap<K, V> entries) {
|
||||
SortedMap<K, V> proxies = new TreeMap<>(entries.comparator());
|
||||
for (Map.Entry<K, V> entry : entries.entrySet()) {
|
||||
proxies.put(entry.getKey(), proxyCast(entry.getValue()));
|
||||
proxies.put(entry.getKey(), proxyCast(proxyFactory, entry.getValue()));
|
||||
}
|
||||
try {
|
||||
entries.clear();
|
||||
|
@ -295,10 +459,10 @@ public final class AuthorizationAdvisorProxyFactory implements AuthorizationProx
|
|||
}
|
||||
}
|
||||
|
||||
private <K, V> Map<K, V> proxyMap(Map<K, V> entries) {
|
||||
private <K, V> Map<K, V> proxyMap(AuthorizationProxyFactory proxyFactory, Map<K, V> entries) {
|
||||
Map<K, V> proxies = new LinkedHashMap<>(entries.size());
|
||||
for (Map.Entry<K, V> entry : entries.entrySet()) {
|
||||
proxies.put(entry.getKey(), proxyCast(entry.getValue()));
|
||||
proxies.put(entry.getKey(), proxyCast(proxyFactory, entry.getValue()));
|
||||
}
|
||||
try {
|
||||
entries.clear();
|
||||
|
@ -310,13 +474,39 @@ public final class AuthorizationAdvisorProxyFactory implements AuthorizationProx
|
|||
}
|
||||
}
|
||||
|
||||
private Stream<?> proxyStream(Stream<?> stream) {
|
||||
return stream.map(this::proxy).onClose(stream::close);
|
||||
private Stream<?> proxyStream(AuthorizationProxyFactory proxyFactory, Stream<?> stream) {
|
||||
return stream.map(proxyFactory::proxy).onClose(stream::close);
|
||||
}
|
||||
|
||||
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
|
||||
private Optional<?> proxyOptional(Optional<?> optional) {
|
||||
return optional.map(this::proxy);
|
||||
private Optional<?> proxyOptional(AuthorizationProxyFactory proxyFactory, Optional<?> optional) {
|
||||
return optional.map(proxyFactory::proxy);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ReactiveTypeVisitor implements TargetVisitor {
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("ReactiveStreamsUnusedPublisher")
|
||||
public Object visit(AuthorizationAdvisorProxyFactory proxyFactory, Object target) {
|
||||
if (target instanceof Mono<?> mono) {
|
||||
return proxyMono(proxyFactory, mono);
|
||||
}
|
||||
if (target instanceof Flux<?> flux) {
|
||||
return proxyFlux(proxyFactory, flux);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private Mono<?> proxyMono(AuthorizationProxyFactory proxyFactory, Mono<?> mono) {
|
||||
return mono.map(proxyFactory::proxy);
|
||||
}
|
||||
|
||||
private Flux<?> proxyFlux(AuthorizationProxyFactory proxyFactory, Flux<?> flux) {
|
||||
return flux.map(proxyFactory::proxy);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
/*
|
||||
* Copyright 2002-2024 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;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisor;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerAfterReactiveMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeReactiveMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PostFilterAuthorizationReactiveMethodInterceptor;
|
||||
import org.springframework.security.authorization.method.PreFilterAuthorizationReactiveMethodInterceptor;
|
||||
|
||||
/**
|
||||
* A proxy factory for applying authorization advice to an arbitrary object.
|
||||
*
|
||||
* <p>
|
||||
* For example, consider a non-Spring-managed object {@code Foo}: <pre>
|
||||
* class Foo {
|
||||
* @PreAuthorize("hasAuthority('bar:read')")
|
||||
* String bar() { ... }
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Use {@link ReactiveAuthorizationAdvisorProxyFactory} to wrap the instance in Spring
|
||||
* Security's {@link org.springframework.security.access.prepost.PreAuthorize} method
|
||||
* interceptor like so:
|
||||
*
|
||||
* <pre>
|
||||
* AuthorizationManagerBeforeMethodInterceptor preAuthorize = AuthorizationManagerBeforeMethodInterceptor.preAuthorize();
|
||||
* AuthorizationProxyFactory proxyFactory = new AuthorizationProxyFactory(preAuthorize);
|
||||
* Foo foo = new Foo();
|
||||
* foo.bar(); // passes
|
||||
* Foo securedFoo = proxyFactory.proxy(foo);
|
||||
* securedFoo.bar(); // access denied!
|
||||
* </pre>
|
||||
*
|
||||
* @author Josh Cummings
|
||||
* @since 6.3
|
||||
*/
|
||||
public final class ReactiveAuthorizationAdvisorProxyFactory implements AuthorizationProxyFactory {
|
||||
|
||||
private final AuthorizationAdvisorProxyFactory defaults = new AuthorizationAdvisorProxyFactory();
|
||||
|
||||
public ReactiveAuthorizationAdvisorProxyFactory() {
|
||||
List<AuthorizationAdvisor> advisors = new ArrayList<>();
|
||||
advisors.add(AuthorizationManagerBeforeReactiveMethodInterceptor.preAuthorize());
|
||||
advisors.add(AuthorizationManagerAfterReactiveMethodInterceptor.postAuthorize());
|
||||
advisors.add(new PreFilterAuthorizationReactiveMethodInterceptor());
|
||||
advisors.add(new PostFilterAuthorizationReactiveMethodInterceptor());
|
||||
advisors.add(new AuthorizeReturnObjectMethodInterceptor(this));
|
||||
this.defaults.setAdvisors(advisors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Proxy an object to enforce authorization advice.
|
||||
*
|
||||
* <p>
|
||||
* Proxies any instance of a non-final class or a class that implements more than one
|
||||
* interface.
|
||||
*
|
||||
* <p>
|
||||
* If {@code target} is an {@link Iterator}, {@link Collection}, {@link Array},
|
||||
* {@link Map}, {@link Stream}, or {@link Optional}, then the element or value type is
|
||||
* proxied.
|
||||
*
|
||||
* <p>
|
||||
* If {@code target} is a {@link Class}, then {@link ProxyFactory#getProxyClass} is
|
||||
* invoked instead.
|
||||
* @param target the instance to proxy
|
||||
* @return the proxied instance
|
||||
*/
|
||||
@Override
|
||||
public Object proxy(Object target) {
|
||||
if (target instanceof Mono<?> mono) {
|
||||
return proxyMono(mono);
|
||||
}
|
||||
if (target instanceof Flux<?> flux) {
|
||||
return proxyFlux(flux);
|
||||
}
|
||||
return this.defaults.proxy(target);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add advisors that should be included to each proxy created.
|
||||
*
|
||||
* <p>
|
||||
* All advisors are re-sorted by their advisor order.
|
||||
* @param advisors the advisors to add
|
||||
*/
|
||||
public void setAdvisors(AuthorizationAdvisor... advisors) {
|
||||
this.defaults.setAdvisors(advisors);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add advisors that should be included to each proxy created.
|
||||
*
|
||||
* <p>
|
||||
* All advisors are re-sorted by their advisor order.
|
||||
* @param advisors the advisors to add
|
||||
*/
|
||||
public void setAdvisors(Collection<AuthorizationAdvisor> advisors) {
|
||||
this.defaults.setAdvisors(advisors);
|
||||
}
|
||||
|
||||
private Mono<?> proxyMono(Mono<?> mono) {
|
||||
return mono.map(this::proxy);
|
||||
}
|
||||
|
||||
private Flux<?> proxyFlux(Flux<?> flux) {
|
||||
return flux.map(this::proxy);
|
||||
}
|
||||
|
||||
}
|
|
@ -40,12 +40,14 @@ import org.springframework.aop.Pointcut;
|
|||
import org.springframework.security.access.AccessDeniedException;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.authentication.TestAuthentication;
|
||||
import org.springframework.security.authorization.AuthorizationAdvisorProxyFactory.TargetVisitor;
|
||||
import org.springframework.security.authorization.method.AuthorizationAdvisor;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.security.core.context.SecurityContextHolder;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.BDDMockito.given;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
@ -64,7 +66,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenPreAuthorizeThenHonors() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
Flight flight = new Flight();
|
||||
assertThat(flight.getAltitude()).isEqualTo(35000d);
|
||||
Flight secured = proxy(factory, flight);
|
||||
|
@ -75,7 +77,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenPreAuthorizeOnInterfaceThenHonors() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
assertThat(this.alan.getFirstName()).isEqualTo("alan");
|
||||
User secured = proxy(factory, this.alan);
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(secured::getFirstName);
|
||||
|
@ -89,7 +91,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenPreAuthorizeOnRecordThenHonors() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
HasSecret repo = new Repository("secret");
|
||||
assertThat(repo.secret()).isEqualTo("secret");
|
||||
HasSecret secured = proxy(factory, repo);
|
||||
|
@ -102,7 +104,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenImmutableListThenReturnsSecuredImmutableList() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
List<Flight> flights = List.of(this.flight);
|
||||
List<Flight> secured = proxy(factory, flights);
|
||||
secured.forEach(
|
||||
|
@ -114,7 +116,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenImmutableSetThenReturnsSecuredImmutableSet() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
Set<Flight> flights = Set.of(this.flight);
|
||||
Set<Flight> secured = proxy(factory, flights);
|
||||
secured.forEach(
|
||||
|
@ -126,7 +128,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenQueueThenReturnsSecuredQueue() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
Queue<Flight> flights = new LinkedList<>(List.of(this.flight));
|
||||
Queue<Flight> secured = proxy(factory, flights);
|
||||
assertThat(flights.size()).isEqualTo(secured.size());
|
||||
|
@ -138,7 +140,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenImmutableSortedSetThenReturnsSecuredImmutableSortedSet() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
SortedSet<User> users = Collections.unmodifiableSortedSet(new TreeSet<>(Set.of(this.alan)));
|
||||
SortedSet<User> secured = proxy(factory, users);
|
||||
secured
|
||||
|
@ -150,7 +152,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenImmutableSortedMapThenReturnsSecuredImmutableSortedMap() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
SortedMap<String, User> users = Collections
|
||||
.unmodifiableSortedMap(new TreeMap<>(Map.of(this.alan.getId(), this.alan)));
|
||||
SortedMap<String, User> secured = proxy(factory, users);
|
||||
|
@ -163,7 +165,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenImmutableMapThenReturnsSecuredImmutableMap() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
Map<String, User> users = Map.of(this.alan.getId(), this.alan);
|
||||
Map<String, User> secured = proxy(factory, users);
|
||||
secured.forEach(
|
||||
|
@ -175,7 +177,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenMutableListThenReturnsSecuredMutableList() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
List<Flight> flights = new ArrayList<>(List.of(this.flight));
|
||||
List<Flight> secured = proxy(factory, flights);
|
||||
secured.forEach(
|
||||
|
@ -187,7 +189,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenMutableSetThenReturnsSecuredMutableSet() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
Set<Flight> flights = new HashSet<>(Set.of(this.flight));
|
||||
Set<Flight> secured = proxy(factory, flights);
|
||||
secured.forEach(
|
||||
|
@ -199,7 +201,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenMutableSortedSetThenReturnsSecuredMutableSortedSet() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
SortedSet<User> users = new TreeSet<>(Set.of(this.alan));
|
||||
SortedSet<User> secured = proxy(factory, users);
|
||||
secured.forEach((u) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(u::getFirstName));
|
||||
|
@ -210,7 +212,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenMutableSortedMapThenReturnsSecuredMutableSortedMap() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
SortedMap<String, User> users = new TreeMap<>(Map.of(this.alan.getId(), this.alan));
|
||||
SortedMap<String, User> secured = proxy(factory, users);
|
||||
secured.forEach((id, u) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(u::getFirstName));
|
||||
|
@ -221,7 +223,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenMutableMapThenReturnsSecuredMutableMap() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
Map<String, User> users = new HashMap<>(Map.of(this.alan.getId(), this.alan));
|
||||
Map<String, User> secured = proxy(factory, users);
|
||||
secured.forEach((id, u) -> assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(u::getFirstName));
|
||||
|
@ -232,7 +234,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenPreAuthorizeForOptionalThenHonors() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
Optional<Flight> flights = Optional.of(this.flight);
|
||||
assertThat(flights.get().getAltitude()).isEqualTo(35000d);
|
||||
Optional<Flight> secured = proxy(factory, flights);
|
||||
|
@ -243,7 +245,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenPreAuthorizeForStreamThenHonors() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
Stream<Flight> flights = Stream.of(this.flight);
|
||||
Stream<Flight> secured = proxy(factory, flights);
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> secured.forEach(Flight::getAltitude));
|
||||
|
@ -253,7 +255,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenPreAuthorizeForArrayThenHonors() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
Flight[] flights = { this.flight };
|
||||
Flight[] secured = proxy(factory, flights);
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(secured[0]::getAltitude);
|
||||
|
@ -263,7 +265,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenPreAuthorizeForIteratorThenHonors() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
Iterator<Flight> flights = List.of(this.flight).iterator();
|
||||
Iterator<Flight> secured = proxy(factory, flights);
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> secured.next().getAltitude());
|
||||
|
@ -273,7 +275,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenPreAuthorizeForIterableThenHonors() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
Iterable<User> users = new UserRepository();
|
||||
Iterable<User> secured = proxy(factory, users);
|
||||
assertThatExceptionOfType(AccessDeniedException.class).isThrownBy(() -> secured.forEach(User::getFirstName));
|
||||
|
@ -282,7 +284,7 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
|
||||
@Test
|
||||
public void proxyWhenPreAuthorizeForClassThenHonors() {
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
Class<Flight> clazz = proxy(factory, Flight.class);
|
||||
assertThat(clazz.getSimpleName()).contains("SpringCGLIB$$");
|
||||
Flight secured = proxy(factory, this.flight);
|
||||
|
@ -297,13 +299,30 @@ public class AuthorizationAdvisorProxyFactoryTests {
|
|||
AuthorizationAdvisor advisor = mock(AuthorizationAdvisor.class);
|
||||
given(advisor.getAdvice()).willReturn(advisor);
|
||||
given(advisor.getPointcut()).willReturn(Pointcut.TRUE);
|
||||
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
factory.setAdvisors(advisor);
|
||||
Flight flight = proxy(factory, this.flight);
|
||||
flight.getAltitude();
|
||||
verify(advisor, atLeastOnce()).getPointcut();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setTargetVisitorThenUses() {
|
||||
TargetVisitor visitor = mock(TargetVisitor.class);
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
factory.setTargetVisitor(visitor);
|
||||
factory.proxy(new Flight());
|
||||
verify(visitor).visit(any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setTargetVisitorIgnoreValueTypesThenIgnores() {
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
assertThatExceptionOfType(ClassCastException.class).isThrownBy(() -> ((Integer) factory.proxy(35)).intValue());
|
||||
factory.setTargetVisitor(TargetVisitor.defaultsSkipValueTypes());
|
||||
assertThat(factory.proxy(35)).isEqualTo(35);
|
||||
}
|
||||
|
||||
private Authentication authenticated(String user, String... authorities) {
|
||||
return TestAuthentication.authenticated(TestAuthentication.withUsername(user).authorities(authorities).build());
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ public class ReactiveAuthorizationAdvisorProxyFactoryTests {
|
|||
|
||||
@Test
|
||||
public void proxyWhenPreAuthorizeThenHonors() {
|
||||
ReactiveAuthorizationAdvisorProxyFactory factory = new ReactiveAuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();
|
||||
Flight flight = new Flight();
|
||||
StepVerifier
|
||||
.create(flight.getAltitude().contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.user)))
|
||||
|
@ -67,7 +67,7 @@ public class ReactiveAuthorizationAdvisorProxyFactoryTests {
|
|||
@Test
|
||||
public void proxyWhenPreAuthorizeOnInterfaceThenHonors() {
|
||||
SecurityContextHolder.getContext().setAuthentication(this.user);
|
||||
ReactiveAuthorizationAdvisorProxyFactory factory = new ReactiveAuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();
|
||||
StepVerifier
|
||||
.create(this.alan.getFirstName().contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.user)))
|
||||
.expectNext("alan")
|
||||
|
@ -89,7 +89,7 @@ public class ReactiveAuthorizationAdvisorProxyFactoryTests {
|
|||
|
||||
@Test
|
||||
public void proxyWhenPreAuthorizeOnRecordThenHonors() {
|
||||
ReactiveAuthorizationAdvisorProxyFactory factory = new ReactiveAuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();
|
||||
HasSecret repo = new Repository(Mono.just("secret"));
|
||||
StepVerifier.create(repo.secret().contextWrite(ReactiveSecurityContextHolder.withAuthentication(this.user)))
|
||||
.expectNext("secret")
|
||||
|
@ -104,7 +104,7 @@ public class ReactiveAuthorizationAdvisorProxyFactoryTests {
|
|||
|
||||
@Test
|
||||
public void proxyWhenPreAuthorizeOnFluxThenHonors() {
|
||||
ReactiveAuthorizationAdvisorProxyFactory factory = new ReactiveAuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();
|
||||
Flux<Flight> flights = Flux.just(this.flight);
|
||||
Flux<Flight> secured = proxy(factory, flights);
|
||||
StepVerifier
|
||||
|
@ -115,7 +115,7 @@ public class ReactiveAuthorizationAdvisorProxyFactoryTests {
|
|||
|
||||
@Test
|
||||
public void proxyWhenPreAuthorizeForClassThenHonors() {
|
||||
ReactiveAuthorizationAdvisorProxyFactory factory = new ReactiveAuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();
|
||||
Class<Flight> clazz = proxy(factory, Flight.class);
|
||||
assertThat(clazz.getSimpleName()).contains("SpringCGLIB$$");
|
||||
Flight secured = proxy(factory, this.flight);
|
||||
|
@ -129,7 +129,7 @@ public class ReactiveAuthorizationAdvisorProxyFactoryTests {
|
|||
AuthorizationAdvisor advisor = mock(AuthorizationAdvisor.class);
|
||||
given(advisor.getAdvice()).willReturn(advisor);
|
||||
given(advisor.getPointcut()).willReturn(Pointcut.TRUE);
|
||||
ReactiveAuthorizationAdvisorProxyFactory factory = new ReactiveAuthorizationAdvisorProxyFactory();
|
||||
AuthorizationAdvisorProxyFactory factory = AuthorizationAdvisorProxyFactory.withReactiveDefaults();
|
||||
factory.setAdvisors(advisor);
|
||||
Flight flight = proxy(factory, this.flight);
|
||||
flight.getAltitude();
|
||||
|
|
|
@ -1812,12 +1812,40 @@ fun getEmailWhenProxiedThenAuthorizes() {
|
|||
----
|
||||
======
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
`@AuthorizeReturnObject` can be placed at the class level. Note, though, that this means Spring Security will proxy any return object, including ``String``, ``Integer`` and other types.
|
||||
=== Using `@AuthorizeReturnObject` at the class level
|
||||
|
||||
`@AuthorizeReturnObject` can be placed at the class level. Note, though, that this means Spring Security will attempt to proxy any return object, including ``String``, ``Integer`` and other types.
|
||||
This is often not what you want to do.
|
||||
|
||||
In most cases, you will want to annotate the individual methods.
|
||||
If you want to use `@AuthorizeReturnObject` on a class or interface whose methods return value types, like `int`, `String`, `Double` or collections of those types, then you should also publish the appropriate `AuthorizationAdvisorProxyFactory.TargetVisitor` as follows:
|
||||
|
||||
|
||||
[tabs]
|
||||
======
|
||||
Java::
|
||||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
@Bean
|
||||
static Customizer<AuthorizationAdvisorProxyFactory> skipValueTypes() {
|
||||
return (factory) -> factory.setTargetVisitor(TargetVisitor.defaultsSkipValueTypes());
|
||||
}
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
@Bean
|
||||
open fun skipValueTypes() = Customizer<AuthorizationAdvisorProxyFactory> {
|
||||
it.setTargetVisitor(TargetVisitor.defaultsSkipValueTypes())
|
||||
}
|
||||
----
|
||||
======
|
||||
|
||||
[TIP]
|
||||
====
|
||||
You can set your own `AuthorizationAdvisorProxyFactory.TargetVisitor` to customize the proxying for any set of types
|
||||
====
|
||||
|
||||
=== Programmatically Proxying
|
||||
|
@ -1877,22 +1905,27 @@ Java::
|
|||
+
|
||||
[source,java,role="primary"]
|
||||
----
|
||||
import org.springframework.security.authorization.AuthorizationAdvisorProxyFactory.TargetVisitor;
|
||||
import static org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.preAuthorize;
|
||||
|
||||
// ...
|
||||
|
||||
AuthorizationProxyFactory proxyFactory = new AuthorizationProxyFactory(preAuthorize());
|
||||
AuthorizationProxyFactory proxyFactory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
// and if needing to skip value types
|
||||
proxyFactory.setTargetVisitor(TargetVisitor.defaultsSkipValueTypes());
|
||||
----
|
||||
|
||||
Kotlin::
|
||||
+
|
||||
[source,kotlin,role="secondary"]
|
||||
----
|
||||
import org.springframework.security.authorization.AuthorizationAdvisorProxyFactory.TargetVisitor;
|
||||
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor.preAuthorize
|
||||
|
||||
// ...
|
||||
|
||||
val proxyFactory: AuthorizationProxyFactory = AuthorizationProxyFactory(preAuthorize())
|
||||
// and if needing to skip value types
|
||||
proxyFactory.setTargetVisitor(TargetVisitor.defaultsSkipValueTypes())
|
||||
----
|
||||
======
|
||||
|
||||
|
@ -1906,7 +1939,7 @@ Java::
|
|||
----
|
||||
@Test
|
||||
void getEmailWhenProxiedThenAuthorizes() {
|
||||
AuthorizationProxyFactory proxyFactory = new AuthorizationProxyFactory(preAuthorize());
|
||||
AuthorizationProxyFactory proxyFactory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
User user = new User("name", "email");
|
||||
assertThat(user.getEmail()).isNotNull();
|
||||
User securedUser = proxyFactory.proxy(user);
|
||||
|
@ -1920,7 +1953,7 @@ Kotlin::
|
|||
----
|
||||
@Test
|
||||
fun getEmailWhenProxiedThenAuthorizes() {
|
||||
val proxyFactory: AuthorizationProxyFactory = AuthorizationProxyFactory(preAuthorize())
|
||||
val proxyFactory: AuthorizationProxyFactory = AuthorizationAdvisorProxyFactory.withDefaults()
|
||||
val user: User = User("name", "email")
|
||||
assertThat(user.getEmail()).isNotNull()
|
||||
val securedUser: User = proxyFactory.proxy(user)
|
||||
|
@ -1948,7 +1981,7 @@ Java::
|
|||
----
|
||||
@Test
|
||||
void getEmailWhenProxiedThenAuthorizes() {
|
||||
AuthorizationProxyFactory proxyFactory = new AuthorizationProxyFactory(preAuthorize());
|
||||
AuthorizationProxyFactory proxyFactory = AuthorizationAdvisorProxyFactory.withDefaults();
|
||||
List<User> users = List.of(ada, albert, marie);
|
||||
List<User> securedUsers = proxyFactory.proxy(users);
|
||||
securedUsers.forEach((securedUser) ->
|
||||
|
|
Loading…
Reference in New Issue