Skip to content

Commit

Permalink
Apply Visitor Pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
jzheaux committed Mar 20, 2024
1 parent a41e971 commit e6c33da
Show file tree
Hide file tree
Showing 10 changed files with 357 additions and 319 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
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.AuthorizationProxyFactoryDelegate;
import org.springframework.security.authorization.method.AuthorizationProxyTargetVisitor;
import org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor;

@Configuration(proxyBeanMethods = false)
Expand All @@ -38,12 +38,12 @@ final class AuthorizationProxyConfiguration implements AopInfrastructureBean {
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static AuthorizationAdvisorProxyFactory authorizationProxyFactory(ObjectProvider<AuthorizationAdvisor> provider,
ObjectProvider<AuthorizationProxyFactoryDelegate> delegate) {
ObjectProvider<AuthorizationProxyTargetVisitor> delegate) {
List<AuthorizationAdvisor> advisors = new ArrayList<>();
provider.forEach(advisors::add);
AuthorizationAdvisorProxyFactory factory = new AuthorizationAdvisorProxyFactory();
factory.setAdvisors(advisors);
delegate.ifAvailable(factory::setProxyFactoryDelegate);
delegate.ifAvailable(factory::setProxyTargetVisitor);
return factory;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
import org.springframework.context.annotation.Role;
import org.springframework.security.authorization.ReactiveAuthorizationAdvisorProxyFactory;
import org.springframework.security.authorization.method.AuthorizationAdvisor;
import org.springframework.security.authorization.method.AuthorizationProxyFactoryDelegate;
import org.springframework.security.authorization.method.AuthorizationProxyTargetVisitor;
import org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor;

@Configuration(proxyBeanMethods = false)
Expand All @@ -38,12 +38,12 @@ final class ReactiveAuthorizationProxyConfiguration implements AopInfrastructure
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static ReactiveAuthorizationAdvisorProxyFactory authorizationProxyFactory(
ObjectProvider<AuthorizationAdvisor> provider, ObjectProvider<AuthorizationProxyFactoryDelegate> delegate) {
ObjectProvider<AuthorizationAdvisor> provider, ObjectProvider<AuthorizationProxyTargetVisitor> delegate) {
List<AuthorizationAdvisor> advisors = new ArrayList<>();
provider.forEach(advisors::add);
ReactiveAuthorizationAdvisorProxyFactory factory = new ReactiveAuthorizationAdvisorProxyFactory();
factory.setAdvisors(advisors);
delegate.ifAvailable(factory::setProxyFactoryDelegate);
delegate.ifAvailable(factory::setProxyTargetVisitor);
return factory;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@
import org.springframework.security.authorization.AuthorizationManager;
import org.springframework.security.authorization.method.AuthorizationInterceptorsOrder;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.authorization.method.AuthorizationProxyFactoryDelegate;
import org.springframework.security.authorization.method.AuthorizationProxyFactoryDelegates;
import org.springframework.security.authorization.method.AuthorizationProxyTargetVisitor;
import org.springframework.security.authorization.method.AuthorizationProxyTargetVisitors;
import org.springframework.security.authorization.method.AuthorizeReturnObject;
import org.springframework.security.authorization.method.MethodInvocationResult;
import org.springframework.security.authorization.method.PrePostTemplateDefaults;
Expand Down Expand Up @@ -1147,8 +1147,8 @@ static class AuthorizeResultConfig {

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static AuthorizationProxyFactoryDelegate ignoreValueTypes() {
return AuthorizationProxyFactoryDelegates.ignoreValueTypes();
static AuthorizationProxyTargetVisitor ignoreValueTypes() {
return AuthorizationProxyTargetVisitors.defaultVisitorIgnoreValueTypes();
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@
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.method.AuthorizationProxyFactoryDelegate;
import org.springframework.security.authorization.method.AuthorizationProxyFactoryDelegates;
import org.springframework.security.authorization.method.AuthorizationProxyTargetVisitor;
import org.springframework.security.authorization.method.AuthorizationProxyTargetVisitors;
import org.springframework.security.authorization.method.AuthorizeReturnObject;
import org.springframework.security.config.core.GrantedAuthorityDefaults;
import org.springframework.security.config.test.SpringTestContext;
Expand Down Expand Up @@ -244,8 +244,8 @@ static class AuthorizeResultConfig {

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
static AuthorizationProxyFactoryDelegate skipValueTypes() {
return AuthorizationProxyFactoryDelegates.ignoreValueTypes();
static AuthorizationProxyTargetVisitor skipValueTypes() {
return AuthorizationProxyTargetVisitors.defaultVisitorIgnoreValueTypes();
}

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,10 @@
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.stream.Stream;

import org.springframework.aop.Advisor;
Expand All @@ -42,13 +32,12 @@
import org.springframework.security.authorization.method.AuthorizationAdvisor;
import org.springframework.security.authorization.method.AuthorizationManagerAfterMethodInterceptor;
import org.springframework.security.authorization.method.AuthorizationManagerBeforeMethodInterceptor;
import org.springframework.security.authorization.method.AuthorizationProxyFactoryDelegate;
import org.springframework.security.authorization.method.AuthorizationProxyFactoryDelegates;
import org.springframework.security.authorization.method.AuthorizationProxyTargetVisitor;
import org.springframework.security.authorization.method.AuthorizationProxyTargetVisitors;
import org.springframework.security.authorization.method.AuthorizeReturnObjectMethodInterceptor;
import org.springframework.security.authorization.method.PostFilterAuthorizationMethodInterceptor;
import org.springframework.security.authorization.method.PreFilterAuthorizationMethodInterceptor;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

/**
* A proxy factory for applying authorization advice to an arbitrary object.
Expand Down Expand Up @@ -77,11 +66,12 @@
* @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 AuthorizationProxyFactoryDelegate delegate = AuthorizationProxyFactoryDelegates.ignore((c) -> false);
private AuthorizationProxyTargetVisitor visitor = AuthorizationProxyTargetVisitors.defaultVisitor();

public AuthorizationAdvisorProxyFactory() {
List<AuthorizationAdvisor> advisors = new ArrayList<>();
Expand Down Expand Up @@ -116,44 +106,9 @@ public Object proxy(Object target) {
if (target == null) {
return null;
}
if (this.delegate.proxies(target)) {
return this.delegate.proxy(this, target);
}
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) {
Expand Down Expand Up @@ -188,7 +143,7 @@ public void setAdvisors(Collection<AuthorizationAdvisor> advisors) {
}

/**
* Consult this delegate first before exercising the default proxy behavior.
* 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
Expand All @@ -197,154 +152,19 @@ public void setAdvisors(Collection<AuthorizationAdvisor> advisors) {
*
* <pre>
* AuthorizationAdvisorProxyFactory proxyFactory = new AuthorizationAdvisorProxyFactory();
* AuthorizationProxyFactoryDelegate delegate = AuthorizationProxyFactoryDelegates.ignoreValueTypes();
* proxyFactory.setProxyFactoryDelegate(delegate);
* AuthorizationProxyTargetVisitor visitor = AuthorizationProxyTargetVisitors.defaultVisotorIgnoreValueTypes();
* proxyFactory.setProxyTargetVisitor(visitor);
* </pre>
* @param delegate the delegate to use to introduce specialized behavior for a type
* @param visitor the visitor to use to introduce specialized behavior for a type
*/
public void setProxyFactoryDelegate(AuthorizationProxyFactoryDelegate delegate) {
Assert.notNull(delegate, "delegate cannot be null");
this.delegate = delegate;
}

@SuppressWarnings("unchecked")
private <T> T proxyCast(T target) {
return (T) proxy(target);
}

private Class<?> proxyClass(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) {
factory.addAdvisors(advisor);
}
return factory.getProxyClass(getClass().getClassLoader());
public void setProxyTargetVisitor(AuthorizationProxyTargetVisitor visitor) {
Assert.notNull(visitor, "delegate cannot be null");
this.visitor = visitor;
}

private <T> Iterable<T> proxyIterable(Iterable<T> iterable) {
return () -> proxyIterator(iterable.iterator());
}

private <T> Iterator<T> proxyIterator(Iterator<T> iterator) {
return new Iterator<>() {
@Override
public boolean hasNext() {
return iterator.hasNext();
}

@Override
public T next() {
return proxyCast(iterator.next());
}
};
}

private <T> SortedSet<T> proxySortedSet(SortedSet<T> set) {
SortedSet<T> proxies = new TreeSet<>(set.comparator());
for (T toProxy : set) {
proxies.add(proxyCast(toProxy));
}
try {
set.clear();
set.addAll(proxies);
return proxies;
}
catch (UnsupportedOperationException ex) {
return Collections.unmodifiableSortedSet(proxies);
}
}

private <T> Set<T> proxySet(Set<T> set) {
Set<T> proxies = new LinkedHashSet<>(set.size());
for (T toProxy : set) {
proxies.add(proxyCast(toProxy));
}
try {
set.clear();
set.addAll(proxies);
return proxies;
}
catch (UnsupportedOperationException ex) {
return Collections.unmodifiableSet(proxies);
}
}

private <T> Queue<T> proxyQueue(Queue<T> queue) {
Queue<T> proxies = new LinkedList<>();
for (T toProxy : queue) {
proxies.add(proxyCast(toProxy));
}
queue.clear();
queue.addAll(proxies);
return proxies;
}

private <T> List<T> proxyList(List<T> list) {
List<T> proxies = new ArrayList<>(list.size());
for (T toProxy : list) {
proxies.add(proxyCast(toProxy));
}
try {
list.clear();
list.addAll(proxies);
return proxies;
}
catch (UnsupportedOperationException ex) {
return Collections.unmodifiableList(proxies);
}
}

private Object[] proxyArray(Object[] objects) {
List<Object> retain = new ArrayList<>(objects.length);
for (Object object : objects) {
retain.add(proxy(object));
}
Object[] proxies = (Object[]) Array.newInstance(objects.getClass().getComponentType(), retain.size());
for (int i = 0; i < retain.size(); i++) {
proxies[i] = retain.get(i);
}
return proxies;
}

private <K, V> SortedMap<K, V> proxySortedMap(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()));
}
try {
entries.clear();
entries.putAll(proxies);
return entries;
}
catch (UnsupportedOperationException ex) {
return Collections.unmodifiableSortedMap(proxies);
}
}

private <K, V> Map<K, V> proxyMap(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()));
}
try {
entries.clear();
entries.putAll(proxies);
return entries;
}
catch (UnsupportedOperationException ex) {
return Collections.unmodifiableMap(proxies);
}
}

private Stream<?> proxyStream(Stream<?> stream) {
return stream.map(this::proxy).onClose(stream::close);
}

@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private Optional<?> proxyOptional(Optional<?> optional) {
return optional.map(this::proxy);
@Override
public Iterator<AuthorizationAdvisor> iterator() {
return this.advisors.iterator();
}

}
Loading

0 comments on commit e6c33da

Please sign in to comment.