INT-3284: Add 'spring.factories' Infrastructure
JIRA: https://jira.springsource.org/browse/INT-3284 * Introduce `IntegrationConfigurationBeanFactoryPostProcessor`, `IntegrationConfigurationInitializer` * Apply `IntegrationConfigurationInitializer` and 'spring.factories' for HTTP and Security modules INT-3284: Address PR's comments JIRA: https://jira.springsource.org/browse/INT-3287 * Fix package tangle (INT-3287) * Apply `IntegrationConfigurationInitializer` for `MBeanExporterHelper` * Previously, there was a separate `ChannelSecurityInterceptorBeanPostProcessor` for each `ChannelSecurityInterceptor` whereas now, there is one `ChannelSecurityInterceptorBeanPostProcessor` that processes all interceptors INT-3284 Refactoring around config package tangle
This commit is contained in:
committed by
Gary Russell
parent
b59fe444cf
commit
ea080e8a9b
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -16,52 +16,61 @@
|
||||
|
||||
package org.springframework.integration.security.config;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.aop.support.DefaultPointcutAdvisor;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
import org.springframework.integration.security.channel.ChannelSecurityMetadataSource;
|
||||
import org.springframework.integration.security.channel.ChannelSecurityInterceptor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.integration.security.channel.ChannelSecurityMetadataSource;
|
||||
import org.springframework.messaging.MessageChannel;
|
||||
|
||||
/**
|
||||
* A {@link BeanPostProcessor} that proxies {@link MessageChannel}s to apply a {@link ChannelSecurityInterceptor}.
|
||||
*
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class ChannelSecurityInterceptorBeanPostProcessor implements BeanPostProcessor {
|
||||
|
||||
private final ChannelSecurityInterceptor interceptor;
|
||||
private final Collection<ChannelSecurityInterceptor> securityInterceptors;
|
||||
|
||||
|
||||
public ChannelSecurityInterceptorBeanPostProcessor(ChannelSecurityInterceptor interceptor) {
|
||||
Assert.notNull(interceptor, "interceptor must not be null");
|
||||
this.interceptor = interceptor;
|
||||
public ChannelSecurityInterceptorBeanPostProcessor(Collection<ChannelSecurityInterceptor> securityInterceptors) {
|
||||
this.securityInterceptors = securityInterceptors;
|
||||
}
|
||||
|
||||
|
||||
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
|
||||
ChannelSecurityMetadataSource channelSecurityMetadataSource =
|
||||
(ChannelSecurityMetadataSource) this.interceptor.obtainSecurityMetadataSource();
|
||||
if (bean instanceof MessageChannel &&
|
||||
shouldProxy(beanName, (MessageChannel) bean, channelSecurityMetadataSource)) {
|
||||
ProxyFactory proxyFactory = new ProxyFactory(bean);
|
||||
proxyFactory.addAdvisor(new DefaultPointcutAdvisor(this.interceptor));
|
||||
return proxyFactory.getProxy();
|
||||
if (bean instanceof MessageChannel) {
|
||||
for (ChannelSecurityInterceptor securityInterceptor : securityInterceptors) {
|
||||
ChannelSecurityMetadataSource channelSecurityMetadataSource =
|
||||
(ChannelSecurityMetadataSource) securityInterceptor.obtainSecurityMetadataSource();
|
||||
if (this.shouldProxy(beanName, channelSecurityMetadataSource)) {
|
||||
if (AopUtils.isAopProxy(bean) && bean instanceof Advised) {
|
||||
((Advised) bean).addAdvisor(new DefaultPointcutAdvisor(securityInterceptor));
|
||||
}
|
||||
else {
|
||||
ProxyFactory proxyFactory = new ProxyFactory(bean);
|
||||
proxyFactory.addAdvisor(new DefaultPointcutAdvisor(securityInterceptor));
|
||||
bean = proxyFactory.getProxy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
private boolean shouldProxy(String beanName, MessageChannel channel, ChannelSecurityMetadataSource channelSecurityMetadataSource) {
|
||||
private boolean shouldProxy(String beanName, ChannelSecurityMetadataSource channelSecurityMetadataSource) {
|
||||
Set<Pattern> patterns = channelSecurityMetadataSource.getPatterns();
|
||||
for (Pattern pattern : patterns) {
|
||||
if (pattern.matcher(beanName).matches()) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -23,11 +23,13 @@ import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionReaderUtils;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.integration.config.xml.IntegrationNamespaceUtils;
|
||||
import org.springframework.integration.security.channel.ChannelSecurityInterceptor;
|
||||
import org.springframework.integration.security.channel.ChannelSecurityMetadataSource;
|
||||
import org.springframework.integration.security.channel.DefaultChannelAccessPolicy;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
@@ -36,18 +38,16 @@ import org.springframework.util.xml.DomUtils;
|
||||
* to control send and receive access, and creates a bean post-processor to apply the
|
||||
* interceptor to {@link org.springframework.messaging.MessageChannel}s
|
||||
* whose names match the specified patterns.
|
||||
*
|
||||
*
|
||||
* @author Jonas Partner
|
||||
* @author Mark Fisher
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
public class SecuredChannelsParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private final static String BASE_PACKAGE_NAME = "org.springframework.integration.security";
|
||||
|
||||
|
||||
@Override
|
||||
protected String getBeanClassName(Element element) {
|
||||
return BASE_PACKAGE_NAME + ".config.ChannelSecurityInterceptorBeanPostProcessor";
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return ChannelSecurityInterceptor.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,23 +57,15 @@ public class SecuredChannelsParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
String securityMetadataSourceBeanName = this.parseSecurityMetadataSource(element, parserContext);
|
||||
BeanDefinitionBuilder interceptorBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
BASE_PACKAGE_NAME + ".channel.ChannelSecurityInterceptor");
|
||||
interceptorBuilder.addConstructorArgReference(securityMetadataSourceBeanName);
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(interceptorBuilder, element, "authentication-manager");
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(interceptorBuilder, element, "access-decision-manager");
|
||||
String interceptorBeanName = BeanDefinitionReaderUtils.registerWithGeneratedName(
|
||||
interceptorBuilder.getBeanDefinition(), parserContext.getRegistry());
|
||||
builder.addConstructorArgReference(interceptorBeanName);
|
||||
builder.addConstructorArgValue(this.parseSecurityMetadataSource(element, parserContext));
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "authentication-manager");
|
||||
IntegrationNamespaceUtils.setReferenceIfAttributeDefined(builder, element, "access-decision-manager");
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private String parseSecurityMetadataSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
BASE_PACKAGE_NAME + ".channel.ChannelSecurityMetadataSource");
|
||||
private BeanDefinition parseSecurityMetadataSource(Element element, ParserContext parserContext) {
|
||||
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(ChannelSecurityMetadataSource.class);
|
||||
List<Element> accessPolicyElements = DomUtils.getChildElementsByTagName(element, "access-policy");
|
||||
ManagedMap patternMappings = new ManagedMap();
|
||||
ManagedMap<Pattern, BeanDefinition> patternMappings = new ManagedMap<Pattern, BeanDefinition>();
|
||||
for (Element accessPolicyElement : accessPolicyElements) {
|
||||
Pattern pattern = Pattern.compile(accessPolicyElement.getAttribute("pattern"));
|
||||
String sendAccess = accessPolicyElement.getAttribute("send-access");
|
||||
@@ -82,17 +74,15 @@ public class SecuredChannelsParser extends AbstractSingleBeanDefinitionParser {
|
||||
parserContext.getReaderContext().error(
|
||||
"At least one of 'send-access' or 'receive-access' must be provided.", accessPolicyElement);
|
||||
}
|
||||
BeanDefinitionBuilder accessPolicyBuilder = BeanDefinitionBuilder.genericBeanDefinition(
|
||||
BASE_PACKAGE_NAME + ".channel.DefaultChannelAccessPolicy");
|
||||
BeanDefinitionBuilder accessPolicyBuilder = BeanDefinitionBuilder.genericBeanDefinition(DefaultChannelAccessPolicy.class);
|
||||
accessPolicyBuilder.addConstructorArgValue(sendAccess);
|
||||
accessPolicyBuilder.addConstructorArgValue(receiveAccess);
|
||||
accessPolicyBuilder.getBeanDefinition().setRole(BeanDefinition.ROLE_SUPPORT);
|
||||
patternMappings.put(pattern, accessPolicyBuilder.getBeanDefinition());
|
||||
}
|
||||
builder.addConstructorArgValue(patternMappings);
|
||||
builder.setRole(BeanDefinition.ROLE_SUPPORT);
|
||||
return BeanDefinitionReaderUtils.registerWithGeneratedName(
|
||||
builder.getBeanDefinition(), parserContext.getRegistry());
|
||||
|
||||
return builder.getBeanDefinition();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2014 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
|
||||
*
|
||||
* http://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.integration.security.config;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
|
||||
import org.springframework.integration.config.IntegrationConfigurationInitializer;
|
||||
import org.springframework.integration.security.channel.ChannelSecurityInterceptor;
|
||||
|
||||
/**
|
||||
* The Integration Security infrastructure {@code beanFactory} initializer.
|
||||
*
|
||||
* @author Artem Bilan
|
||||
* @since 4.0
|
||||
*/
|
||||
public class SecurityIntegrationConfigurationInitializer implements IntegrationConfigurationInitializer {
|
||||
|
||||
@Override
|
||||
public void initialize(ConfigurableListableBeanFactory beanFactory) throws BeansException {
|
||||
Collection<ChannelSecurityInterceptor> securityInterceptors = beanFactory.getBeansOfType(ChannelSecurityInterceptor.class).values();
|
||||
beanFactory.addBeanPostProcessor(new ChannelSecurityInterceptorBeanPostProcessor(securityInterceptors));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
org.springframework.integration.config.IntegrationConfigurationInitializer=\
|
||||
org.springframework.integration.security.config.SecurityIntegrationConfigurationInitializer
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
<outbound-channel-adapter id="securedChannelAdapter" ref="testHandler"/>
|
||||
|
||||
<service-activator input-channel="securedChannelAdapter2" ref="testHandler"/>
|
||||
|
||||
<outbound-channel-adapter id="unsecuredChannelAdapter" ref="testHandler"/>
|
||||
|
||||
</beans:beans>
|
||||
</beans:beans>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -37,6 +37,7 @@ import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
*/
|
||||
@ContextConfiguration
|
||||
public class ChannelAdapterSecurityIntegrationTests extends AbstractJUnit4SpringContextTests {
|
||||
@@ -45,6 +46,10 @@ public class ChannelAdapterSecurityIntegrationTests extends AbstractJUnit4Spring
|
||||
@Qualifier("securedChannelAdapter")
|
||||
MessageChannel securedChannelAdapter;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("securedChannelAdapter")
|
||||
MessageChannel securedChannelAdapter2;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("unsecuredChannelAdapter")
|
||||
MessageChannel unsecuredChannelAdapter;
|
||||
@@ -65,13 +70,14 @@ public class ChannelAdapterSecurityIntegrationTests extends AbstractJUnit4Spring
|
||||
login("bob", "bobspassword", "ROLE_ADMINA");
|
||||
securedChannelAdapter.send(new GenericMessage<String>("test"));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
@DirtiesContext
|
||||
public void testSecuredWithPermission() {
|
||||
login("bob", "bobspassword", "ROLE_ADMIN", "ROLE_PRESIDENT");
|
||||
securedChannelAdapter.send(new GenericMessage<String>("test"));
|
||||
assertEquals("Wrong size of message list in target", 1, testConsumer.sentMessages.size());
|
||||
securedChannelAdapter2.send(new GenericMessage<String>("test"));
|
||||
assertEquals("Wrong size of message list in target", 2, testConsumer.sentMessages.size());
|
||||
}
|
||||
|
||||
@Test(expected = AccessDeniedException.class)
|
||||
@@ -81,6 +87,13 @@ public class ChannelAdapterSecurityIntegrationTests extends AbstractJUnit4Spring
|
||||
securedChannelAdapter.send(new GenericMessage<String>("test"));
|
||||
}
|
||||
|
||||
@Test(expected = AccessDeniedException.class)
|
||||
@DirtiesContext
|
||||
public void testSecured2WithoutPermision() {
|
||||
login("bob", "bobspassword", "ROLE_USER");
|
||||
securedChannelAdapter2.send(new GenericMessage<String>("test"));
|
||||
}
|
||||
|
||||
@Test(expected = AuthenticationException.class)
|
||||
@DirtiesContext
|
||||
public void testSecuredWithoutAuthenticating() {
|
||||
|
||||
@@ -19,6 +19,7 @@ package org.springframework.integration.security.channel;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.junit.Test;
|
||||
@@ -38,7 +39,7 @@ public class ChannelSecurityInterceptorBeanPostProcessorTests {
|
||||
ChannelSecurityMetadataSource securityMetadataSource = new ChannelSecurityMetadataSource();
|
||||
securityMetadataSource.addPatternMapping(Pattern.compile("secured.*"), new DefaultChannelAccessPolicy("ROLE_ADMIN", null));
|
||||
ChannelSecurityInterceptor interceptor = new ChannelSecurityInterceptor(securityMetadataSource);
|
||||
ChannelSecurityInterceptorBeanPostProcessor postProcessor = new ChannelSecurityInterceptorBeanPostProcessor(interceptor);
|
||||
ChannelSecurityInterceptorBeanPostProcessor postProcessor = new ChannelSecurityInterceptorBeanPostProcessor(Collections.singletonList(interceptor));
|
||||
QueueChannel securedChannel = new QueueChannel();
|
||||
securedChannel.setBeanName("securedChannel");
|
||||
MessageChannel postProcessedChannel = (MessageChannel) postProcessor.postProcessAfterInitialization(securedChannel, "securedChannel");
|
||||
@@ -50,7 +51,7 @@ public class ChannelSecurityInterceptorBeanPostProcessorTests {
|
||||
ChannelSecurityMetadataSource securityMetadataSource = new ChannelSecurityMetadataSource();
|
||||
securityMetadataSource.addPatternMapping(Pattern.compile("secured.*"), new DefaultChannelAccessPolicy("ROLE_ADMIN", null));
|
||||
ChannelSecurityInterceptor interceptor = new ChannelSecurityInterceptor(securityMetadataSource);
|
||||
ChannelSecurityInterceptorBeanPostProcessor postProcessor = new ChannelSecurityInterceptorBeanPostProcessor(interceptor);
|
||||
ChannelSecurityInterceptorBeanPostProcessor postProcessor = new ChannelSecurityInterceptorBeanPostProcessor(Collections.singletonList(interceptor));
|
||||
QueueChannel channel = new QueueChannel();
|
||||
channel.setBeanName("testChannel");
|
||||
MessageChannel postProcessedChannel = (MessageChannel) postProcessor.postProcessAfterInitialization(channel, "testChannel");
|
||||
|
||||
Reference in New Issue
Block a user