diff --git a/org.springframework.integration.security/.classpath b/org.springframework.integration.security/.classpath
new file mode 100644
index 0000000000..b6477a8014
--- /dev/null
+++ b/org.springframework.integration.security/.classpath
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.springframework.integration.security/.project b/org.springframework.integration.security/.project
new file mode 100644
index 0000000000..699b304075
--- /dev/null
+++ b/org.springframework.integration.security/.project
@@ -0,0 +1,17 @@
+
+
+ org.springframework.integration.security
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/org.springframework.integration.security/build.xml b/org.springframework.integration.security/build.xml
new file mode 100644
index 0000000000..f67f70c9f5
--- /dev/null
+++ b/org.springframework.integration.security/build.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/org.springframework.integration.security/ivy.xml b/org.springframework.integration.security/ivy.xml
new file mode 100644
index 0000000000..10b8e7fb45
--- /dev/null
+++ b/org.springframework.integration.security/ivy.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/SecurityContextAssociatingHandlerInterceptor.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/SecurityContextAssociatingHandlerInterceptor.java
new file mode 100644
index 0000000000..c4e17d4f62
--- /dev/null
+++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/SecurityContextAssociatingHandlerInterceptor.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2002-2008 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;
+
+import org.springframework.integration.handler.InterceptingMessageHandler;
+import org.springframework.integration.handler.MessageHandler;
+import org.springframework.integration.message.Message;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
+
+/**
+ * Associates the {@link SecurityContext} propagated in the message header
+ * with the thread executing the handle call to a {@link MessageHandler}.
+ *
+ * @author Jonas Partner
+ */
+public class SecurityContextAssociatingHandlerInterceptor extends InterceptingMessageHandler {
+
+ public SecurityContextAssociatingHandlerInterceptor(MessageHandler target) {
+ super(target);
+ }
+
+
+ @Override
+ public Message> handle(Message> message, MessageHandler target) {
+ if (message.getHeader().getAttributeNames().contains(
+ SecurityContextPropagatingChannelInterceptor.SECURITY_CONTEXT_HEADER_ATTRIBUTE)) {
+ return handleInSecurityContext(message, target);
+ }
+ return target.handle(message);
+ }
+
+ private Message> handleInSecurityContext(Message> message, MessageHandler target) {
+ SecurityContext context = (SecurityContext) message.getHeader().getAttribute(
+ SecurityContextPropagatingChannelInterceptor.SECURITY_CONTEXT_HEADER_ATTRIBUTE);
+ SecurityContextHolder.setContext(context);
+ try{
+ return target.handle(message);
+ }
+ finally {
+ SecurityContextHolder.clearContext();
+ }
+ }
+
+}
diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/SecurityContextPropagatingChannelInterceptor.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/SecurityContextPropagatingChannelInterceptor.java
new file mode 100644
index 0000000000..8f537afd78
--- /dev/null
+++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/SecurityContextPropagatingChannelInterceptor.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2002-2008 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;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.integration.channel.MessageChannel;
+import org.springframework.integration.channel.interceptor.ChannelInterceptorAdapter;
+import org.springframework.integration.message.Message;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
+
+/**
+ * Propagates the {@ link SecurityContext} associated with the current
+ * thread (if any) by adding it to the header of sent messages.
+ *
+ * @author Jonas Partner
+ */
+public class SecurityContextPropagatingChannelInterceptor extends ChannelInterceptorAdapter {
+
+ public static final String SECURITY_CONTEXT_HEADER_ATTRIBUTE = "SPRING_SECURITY_CONTEXT";
+
+
+ private final Log logger = LogFactory.getLog(this.getClass());
+
+
+ @Override
+ public boolean preSend(Message> message, MessageChannel channel) {
+ this.setSecurityContextAttribute(message);
+ return true;
+ }
+
+ protected void setSecurityContextAttribute(Message> message){
+ SecurityContext securityContext = SecurityContextHolder.getContext();
+ if (securityContext.getAuthentication() != null) {
+ message.getHeader().setAttribute(SECURITY_CONTEXT_HEADER_ATTRIBUTE, securityContext);
+ }
+ else if (logger.isInfoEnabled()) {
+ logger.info("No security context found");
+ }
+ }
+
+}
diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/SecurityEnforcingChannelInterceptor.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/SecurityEnforcingChannelInterceptor.java
new file mode 100644
index 0000000000..1df1ff8c78
--- /dev/null
+++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/SecurityEnforcingChannelInterceptor.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2002-2008 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;
+
+import org.springframework.integration.channel.AbstractMessageChannel;
+import org.springframework.integration.channel.MessageChannel;
+import org.springframework.integration.channel.interceptor.ChannelInterceptorAdapter;
+import org.springframework.integration.message.Message;
+import org.springframework.security.AccessDecisionManager;
+import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.util.Assert;
+
+/**
+ * Delegates to the provided instance of {@link AccessDecisionManager} to
+ * enforce the security on the send and receive calls on the {@ MessageChannel}.
+ *
+ * @author Jonas Partner
+ */
+public class SecurityEnforcingChannelInterceptor extends ChannelInterceptorAdapter{
+
+ private final AccessDecisionManager accessDecisionManger;
+
+ private final String channelName;
+
+ private volatile ConfigAttributeDefinition sendSecurityAttributes;
+
+ private volatile ConfigAttributeDefinition receiveSecurityAttributes;
+
+
+ public SecurityEnforcingChannelInterceptor(AccessDecisionManager accessDecisionManager, AbstractMessageChannel channelToSecure) {
+ Assert.notNull(accessDecisionManager, "AccessDecisionManager must not be null");
+ Assert.notNull(channelToSecure, "channel to secure must not be null");
+ this.accessDecisionManger = accessDecisionManager;
+ this.channelName = channelToSecure.getName();
+ channelToSecure.addInterceptor(this);
+ }
+
+
+ public ConfigAttributeDefinition getSendSecurityAttributes() {
+ return this.sendSecurityAttributes;
+ }
+
+ public void setSendSecurityAttributes(ConfigAttributeDefinition sendSecurityAttributes) {
+ this.sendSecurityAttributes = sendSecurityAttributes;
+ }
+
+ public ConfigAttributeDefinition getReceiveSecurityAttributes() {
+ return this.receiveSecurityAttributes;
+ }
+
+ public void setReceiveSecurityAttributes(ConfigAttributeDefinition receiveSecurityAttributes) {
+ this.receiveSecurityAttributes = receiveSecurityAttributes;
+ }
+
+ @Override
+ public boolean preSend(Message> message, MessageChannel channel) {
+ this.checkSend(channel);
+ return true;
+ }
+
+ @Override
+ public boolean preReceive(MessageChannel channel) {
+ this.checkReceive(channel);
+ return super.preReceive(channel);
+ }
+
+ private void checkSend(MessageChannel channel){
+ this.checkPermission(channel, this.sendSecurityAttributes);
+ }
+
+ private void checkReceive(MessageChannel channel){
+ this.checkPermission(channel, this.receiveSecurityAttributes);
+ }
+
+ private void checkPermission(MessageChannel messageChannel, ConfigAttributeDefinition securityAttributes){
+ if (securityAttributes != null) {
+ this.accessDecisionManger.decide(SecurityContextHolder.getContext().getAuthentication(),
+ messageChannel, securityAttributes);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getName() + " for channel '" + this.channelName + "'";
+ }
+
+}
diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/IntegrationSecurityNamespaceHandler.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/IntegrationSecurityNamespaceHandler.java
new file mode 100644
index 0000000000..0aa9efeb44
--- /dev/null
+++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/IntegrationSecurityNamespaceHandler.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2002-2008 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 org.springframework.beans.factory.xml.NamespaceHandlerSupport;
+
+/**
+ * Namespace handler for the security namespace.
+ *
+ * @author Jonas Partner
+ */
+public class IntegrationSecurityNamespaceHandler extends NamespaceHandlerSupport {
+
+ public void init() {
+ registerBeanDefinitionParser("secured", new SecuredParser());
+ registerBeanDefinitionParser("secure-channels", new SecureChannelsParser());
+ }
+
+}
diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/SecureChannelsParser.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/SecureChannelsParser.java
new file mode 100644
index 0000000000..5ae74315b9
--- /dev/null
+++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/SecureChannelsParser.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2002-2008 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 org.w3c.dom.Element;
+
+import org.springframework.beans.factory.BeanDefinitionStoreException;
+import org.springframework.beans.factory.support.AbstractBeanDefinition;
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.util.StringUtils;
+
+/**
+ * Interprets the <secure-channels> element which controls default
+ * {@link SecurityContext} propagation behaviour.
+ *
+ * @author Jonas Partner
+ */
+public class SecureChannelsParser extends AbstractSingleBeanDefinitionParser {
+
+ @Override
+ protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
+ builder.getBeanDefinition().setAbstract(true);
+ String propagation = element.getAttribute("propagate");
+ boolean propagateByDefault = true;
+ if (StringUtils.hasText(propagation)) {
+ propagateByDefault = Boolean.parseBoolean(propagation);
+ }
+ if (propagateByDefault) {
+ SecurityPropagatingBeanPostProcessorDefinitionHelper.setPropagationDefault(true, parserContext);
+ }
+ }
+
+ @Override
+ protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
+ throws BeanDefinitionStoreException {
+ return "internal.integration.SecureChannels";
+ }
+
+}
diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/SecuredParser.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/SecuredParser.java
new file mode 100644
index 0000000000..9f67858142
--- /dev/null
+++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/SecuredParser.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2002-2008 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 org.w3c.dom.Element;
+
+import org.springframework.beans.factory.support.BeanDefinitionBuilder;
+import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.integration.security.SecurityEnforcingChannelInterceptor;
+import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.util.StringUtils;
+
+/**
+ * Determines {@link SecurityContext} propagation behaviour for the parent element
+ * channel, and creates a {@link SecurityEnforcingChannelInterceptor} to control
+ * send and receive access if send-access and/or receive-access is specified.
+ *
+ * @author Jonas Partner
+ */
+public class SecuredParser extends AbstractSingleBeanDefinitionParser {
+
+ @Override
+ protected boolean shouldGenerateId() {
+ return false;
+ }
+
+ @Override
+ protected boolean shouldGenerateIdAsFallback() {
+ return true;
+ }
+
+ @Override
+ protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
+ String receiveAccess = element.getAttribute("receive-access");
+ String sendAccess = element.getAttribute("send-access");
+ String accessDecisionManager = element.getAttribute("access-decision-manager");
+ String propagation = element.getAttribute("propagate");
+ String channelName = ((Element)element.getParentNode()).getAttribute("id");
+ if (channelName == null) {
+ parserContext.getReaderContext().error("The secured element requires a channel parent id.", element);
+ }
+ builder.getBeanDefinition().setBeanClass(SecurityEnforcingChannelInterceptor.class);
+ if (!StringUtils.hasText(accessDecisionManager)) {
+ accessDecisionManager = "accessDecisionManager";
+ }
+ builder.addConstructorArgReference(accessDecisionManager);
+ builder.addConstructorArgReference(channelName);
+ if (StringUtils.hasText(sendAccess)) {
+ ConfigAttributeDefinition sendDefinition = new ConfigAttributeDefinition(sendAccess);
+ builder.addPropertyValue("sendSecurityAttributes", sendDefinition);
+ }
+ if (StringUtils.hasText(receiveAccess)) {
+ ConfigAttributeDefinition receiveDefinition = new ConfigAttributeDefinition(receiveAccess);
+ builder.addPropertyValue("receiveSecurityAttributes", receiveDefinition);
+ }
+ boolean propagationValue = true;
+ if (StringUtils.hasText(propagation)) {
+ propagationValue = Boolean.parseBoolean(propagation);
+ }
+ if (propagationValue) {
+ SecurityPropagatingBeanPostProcessorDefinitionHelper.addToIncludeChannelList(channelName, parserContext);
+ }
+ else {
+ SecurityPropagatingBeanPostProcessorDefinitionHelper.addToExcludeChannelList(channelName, parserContext);
+ }
+ }
+
+}
diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/SecurityPropagatingBeanPostProcessor.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/SecurityPropagatingBeanPostProcessor.java
new file mode 100644
index 0000000000..035d0512b0
--- /dev/null
+++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/SecurityPropagatingBeanPostProcessor.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2002-2008 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.ArrayList;
+import java.util.List;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.core.Ordered;
+import org.springframework.integration.channel.AbstractMessageChannel;
+import org.springframework.integration.security.SecurityContextPropagatingChannelInterceptor;
+
+/**
+ * Post processes channels applying appropriate propagation behaviour. If
+ * default propagation is specified with a secure-channels tag, that will
+ * be applied in the absence of a secured tag for the channel. If the
+ * secured tag is specified, it will always determine propagation behaviour.
+ *
+ * @author Jonas Partner
+ */
+public class SecurityPropagatingBeanPostProcessor implements BeanPostProcessor, Ordered {
+
+ protected static final String SECURITY_PROPAGATING_BEAN_POST_PROCESSOR_NAME = SecurityPropagatingBeanPostProcessor.class.getName();
+
+
+ private final SecurityContextPropagatingChannelInterceptor interceptor =
+ new SecurityContextPropagatingChannelInterceptor();
+
+ private boolean propagateByDefault;
+
+ private final Log logger = LogFactory.getLog(this.getClass());
+
+ private List channelsToInclude = new ArrayList();
+
+ private List channelsToExclude = new ArrayList();
+
+
+ public boolean isPropagateByDefault() {
+ return this.propagateByDefault;
+ }
+
+ public void setPropagateByDefault(boolean propagateByDefault) {
+ this.propagateByDefault = propagateByDefault;
+ }
+
+ public List getChannelsToInclude() {
+ return this.channelsToInclude;
+ }
+
+ public void setChannelsToInclude(List channelsToInclude) {
+ this.channelsToInclude = channelsToInclude;
+ }
+
+ public List getChannelsToExclude() {
+ return this.channelsToExclude;
+ }
+
+ public void setChannelsToExclude(List channelsToExclude) {
+ this.channelsToExclude = channelsToExclude;
+ }
+
+ public int getOrder() {
+ return 0;
+ }
+
+ public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
+ return bean;
+ }
+
+ public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
+ if (AbstractMessageChannel.class.isAssignableFrom(bean.getClass())) {
+ AbstractMessageChannel channel = (AbstractMessageChannel) bean;
+ if(this.channelsToInclude.contains(beanName) ||
+ (this.propagateByDefault && !this.channelsToExclude.contains(beanName))) {
+ channel.addInterceptor(this.interceptor);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Channel '" + beanName + "' will propagate a SecurityContext.");
+ }
+ }
+ else if (logger.isDebugEnabled()) {
+ logger.debug("Channel '" + beanName + "' is not configured to propagate a SecurityContext.");
+ }
+ }
+ return bean;
+ }
+
+}
diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/SecurityPropagatingBeanPostProcessorDefinitionHelper.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/SecurityPropagatingBeanPostProcessorDefinitionHelper.java
new file mode 100644
index 0000000000..9a5a50d3f3
--- /dev/null
+++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/SecurityPropagatingBeanPostProcessorDefinitionHelper.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2002-2008 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.ArrayList;
+import java.util.List;
+
+import org.springframework.beans.factory.config.BeanDefinition;
+import org.springframework.beans.factory.config.RuntimeBeanNameReference;
+import org.springframework.beans.factory.parsing.BeanComponentDefinition;
+import org.springframework.beans.factory.support.RootBeanDefinition;
+import org.springframework.beans.factory.xml.ParserContext;
+import org.springframework.context.ApplicationContext;
+
+/**
+ * Helper to configure the per {@link ApplicationContext}
+ * {@link SecurityPropagatingBeanPostProcessor} which determines
+ * {@link SecurityContext} propagation.
+ *
+ * @author Jonas Partner
+ */
+public class SecurityPropagatingBeanPostProcessorDefinitionHelper {
+
+ private static final String CHANNELS_TO_INCLUDE = "channelsToInclude";
+
+ private static final String CHANNELS_TO_EXCLUDE = "channelsToExclude";
+
+ private static final String PROPAGATE_BY_DEFAULT = "propagateByDefault";
+
+
+ public static void setPropagationDefault(boolean valueForPropagationDefault, ParserContext context) {
+ BeanDefinition beanDefintion = getOrCreateSecurityPropagatingBeanPostProcessor(context);
+ beanDefintion.getPropertyValues().addPropertyValue(PROPAGATE_BY_DEFAULT, Boolean.valueOf(valueForPropagationDefault));
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void addToExcludeChannelList(String channelName, ParserContext context) {
+ BeanDefinition beanDefintion = getOrCreateSecurityPropagatingBeanPostProcessor(context);
+ List channelsToExclude;
+ if (beanDefintion.getPropertyValues().contains(CHANNELS_TO_EXCLUDE)) {
+ channelsToExclude = (List) beanDefintion.getPropertyValues().getPropertyValue(CHANNELS_TO_EXCLUDE).getValue();
+ }
+ else {
+ channelsToExclude = new ArrayList();
+ beanDefintion.getPropertyValues().addPropertyValue(CHANNELS_TO_EXCLUDE, channelsToExclude);
+ }
+ channelsToExclude.add(channelName);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static void addToIncludeChannelList(String channelName, ParserContext context){
+ BeanDefinition beanDefintion = getOrCreateSecurityPropagatingBeanPostProcessor(context);
+ List channelsToExclude;
+ if (beanDefintion.getPropertyValues().contains(CHANNELS_TO_INCLUDE)) {
+ channelsToExclude = (List) beanDefintion.getPropertyValues().getPropertyValue(CHANNELS_TO_INCLUDE).getValue();
+ }
+ else {
+ channelsToExclude = new ArrayList();
+ beanDefintion.getPropertyValues().addPropertyValue(CHANNELS_TO_INCLUDE,channelsToExclude);
+ }
+ channelsToExclude.add(channelName);
+ }
+
+ private static BeanDefinition getOrCreateSecurityPropagatingBeanPostProcessor(ParserContext context) {
+ BeanDefinition beanDefinition = null;
+ String postProcessorBeanName = SecurityPropagatingBeanPostProcessor.SECURITY_PROPAGATING_BEAN_POST_PROCESSOR_NAME;
+ if (context.getRegistry().containsBeanDefinition(postProcessorBeanName)) {
+ beanDefinition = context.getRegistry().getBeanDefinition(postProcessorBeanName);
+ }
+ if (beanDefinition == null) {
+ beanDefinition = new RootBeanDefinition(SecurityPropagatingBeanPostProcessor.class);
+ context.registerBeanComponent(new BeanComponentDefinition(beanDefinition, postProcessorBeanName));
+ }
+ return beanDefinition;
+ }
+
+}
diff --git a/org.springframework.integration.security/src/main/resources/META-INF/spring-integration.parsers b/org.springframework.integration.security/src/main/resources/META-INF/spring-integration.parsers
new file mode 100644
index 0000000000..2269b399d6
--- /dev/null
+++ b/org.springframework.integration.security/src/main/resources/META-INF/spring-integration.parsers
@@ -0,0 +1,2 @@
+secured=org.springframework.integration.security.config.SecuredParser
+secure-channels=org.springframework.integration.security.config.SecureChannelsParser
\ No newline at end of file
diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/SecurityContextAssociatingHandlerInterceptorTests.java b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/SecurityContextAssociatingHandlerInterceptorTests.java
new file mode 100644
index 0000000000..1a84ee9a87
--- /dev/null
+++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/SecurityContextAssociatingHandlerInterceptorTests.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2002-2008 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotSame;
+import static org.junit.Assert.assertNull;
+
+import org.junit.Test;
+
+import org.springframework.integration.handler.MessageHandler;
+import org.springframework.integration.message.Message;
+import org.springframework.integration.message.StringMessage;
+import org.springframework.security.AccessDeniedException;
+import org.springframework.security.Authentication;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
+
+/**
+ * @author Jonas Partner
+ */
+public class SecurityContextAssociatingHandlerInterceptorTests {
+
+ @Test
+ public void testMessageWithSecurityContext() {
+ final StubSecurityContext securityContext = new StubSecurityContext();
+ StringMessage message = new StringMessage("test");
+ message.getHeader().setAttribute(
+ SecurityContextPropagatingChannelInterceptor.SECURITY_CONTEXT_HEADER_ATTRIBUTE, securityContext);
+ MessageHandler handler = new MessageHandler() {
+ public Message> handle(Message> message) {
+ SecurityContext associatedContext = SecurityContextHolder.getContext();
+ assertEquals("Wrong security context", securityContext, associatedContext);
+ return null;
+ }
+ };
+ SecurityContextAssociatingHandlerInterceptor associatingInterceptor =
+ new SecurityContextAssociatingHandlerInterceptor(handler);
+ associatingInterceptor.handle(message);
+ assertNull("Security context still present after handler returned",
+ SecurityContextHolder.getContext().getAuthentication());
+ }
+
+ @Test(expected = AccessDeniedException.class)
+ public void testForSecurityLeakageIfHandlerThrowsException() {
+ final StubSecurityContext securityContext = new StubSecurityContext();
+ StringMessage message = new StringMessage("test");
+ message.getHeader().setAttribute(
+ SecurityContextPropagatingChannelInterceptor.SECURITY_CONTEXT_HEADER_ATTRIBUTE, securityContext);
+ MessageHandler handler = new MessageHandler() {
+ public Message> handle(Message> message) {
+ SecurityContext associatedContext = SecurityContextHolder.getContext();
+ assertEquals("Wrong security context", securityContext, associatedContext);
+ throw new AccessDeniedException("Not allowed");
+ }
+ };
+ SecurityContextAssociatingHandlerInterceptor associatingInterceptor =
+ new SecurityContextAssociatingHandlerInterceptor(handler);
+ try {
+ associatingInterceptor.handle(message);
+ }
+ finally {
+ assertNull("Security context still present after handler threw exception",
+ SecurityContextHolder.getContext().getAuthentication());
+ }
+ }
+
+ @Test
+ public void testMessageWithoutSecurityContext() {
+ final StubSecurityContext securityContext = new StubSecurityContext();
+ StringMessage message = new StringMessage("test");
+ MessageHandler handler = new MessageHandler() {
+ public Message> handle(Message> message) {
+ SecurityContext associatedContext = SecurityContextHolder.getContext();
+ assertNotSame("Wrong security context", securityContext, associatedContext);
+ return null;
+ }
+ };
+ SecurityContextAssociatingHandlerInterceptor associatingInterceptor =
+ new SecurityContextAssociatingHandlerInterceptor(handler);
+ associatingInterceptor.handle(message);
+ assertNull("Security context still present after handler returned",
+ SecurityContextHolder.getContext().getAuthentication());
+ }
+
+
+ @SuppressWarnings("serial")
+ private static class StubSecurityContext implements SecurityContext {
+
+ StubAuthentication stubAuthentication = new StubAuthentication();
+
+ public Authentication getAuthentication() {
+ return stubAuthentication;
+ }
+
+ public void setAuthentication(Authentication authentication) {
+ }
+ }
+
+
+ @SuppressWarnings("serial")
+ private static class StubAuthentication implements Authentication {
+
+ public GrantedAuthority[] getAuthorities() {
+ return null;
+ }
+
+ public Object getCredentials() {
+ return null;
+ }
+
+ public Object getDetails() {
+ return null;
+ }
+
+ public Object getPrincipal() {
+ return null;
+ }
+
+ public boolean isAuthenticated() {
+ return false;
+ }
+
+ public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
+ }
+
+ public String getName() {
+ return null;
+ }
+ }
+
+}
diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/SecurityContextPropagatingChannelInterceptorTests.java b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/SecurityContextPropagatingChannelInterceptorTests.java
new file mode 100644
index 0000000000..4f3c9a8713
--- /dev/null
+++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/SecurityContextPropagatingChannelInterceptorTests.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright 2002-2008 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;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.integration.channel.QueueChannel;
+import org.springframework.integration.message.MessageHeader;
+import org.springframework.integration.message.StringMessage;
+import org.springframework.security.Authentication;
+import org.springframework.security.GrantedAuthority;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
+
+/**
+ * @author Jonas Partner
+ */
+public class SecurityContextPropagatingChannelInterceptorTests {
+
+ private QueueChannel channel;
+
+ private SecurityContextPropagatingChannelInterceptor securityPropogatingChannelInterceptor;
+
+ private StubSecurityContext securityContext;
+
+
+ @Before
+ public void setUp() {
+ this.channel = new QueueChannel();
+ this.securityPropogatingChannelInterceptor = new SecurityContextPropagatingChannelInterceptor();
+ this.channel.addInterceptor(securityPropogatingChannelInterceptor);
+ this.securityContext = new StubSecurityContext();
+ }
+
+ @After
+ public void tearDown(){
+ SecurityContextHolder.clearContext();
+ }
+
+
+ @Test
+ public void testPropogationWhenSecurityContextExists() {
+ this.associateContextWithThread();
+ StringMessage message = new StringMessage("test");
+ this.channel.send(message);
+ message = (StringMessage) channel.receive(0);
+ MessageHeader header = message.getHeader();
+ assertTrue("No security context attribute found in header.",
+ header.getAttributeNames().contains(SecurityContextPropagatingChannelInterceptor.SECURITY_CONTEXT_HEADER_ATTRIBUTE));
+ SecurityContext contextFromHeader = (SecurityContext) header.getAttribute(
+ SecurityContextPropagatingChannelInterceptor.SECURITY_CONTEXT_HEADER_ATTRIBUTE);
+ assertEquals("Incorrect security context in message header.", securityContext, contextFromHeader);
+ }
+
+ @Test
+ public void testHeaderNotSetWhenNoSecurityContextExists() {
+ StringMessage message = new StringMessage("test");
+ channel.send(message);
+ message = (StringMessage) channel.receive(0);
+ MessageHeader header = message.getHeader();
+ assertFalse("Security context header found when no security context existed.",
+ header.getAttributeNames().contains(SecurityContextPropagatingChannelInterceptor.SECURITY_CONTEXT_HEADER_ATTRIBUTE));
+ }
+
+
+ private void associateContextWithThread(){
+ SecurityContextHolder.setContext(securityContext);
+ }
+
+
+ @SuppressWarnings("serial")
+ private static class StubSecurityContext implements SecurityContext{
+
+ private Authentication authentication = new Authentication() {
+
+ public GrantedAuthority[] getAuthorities() {
+ return null;
+ }
+
+ public Object getCredentials() {
+ return null;
+ }
+
+ public Object getDetails() {
+ return null;
+ }
+
+ public Object getPrincipal() {
+ return null;
+ }
+
+ public boolean isAuthenticated() {
+ return false;
+ }
+
+ public void setAuthenticated(boolean isAuthenticated)
+ throws IllegalArgumentException {
+ }
+
+ public String getName() {
+ return null;
+ }
+ };
+
+ public Authentication getAuthentication() {
+ return authentication;
+ }
+
+ public void setAuthentication(Authentication authentication) {
+ }
+ }
+
+}
diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/SecurityEnforcingChannelInterceptorTests.java b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/SecurityEnforcingChannelInterceptorTests.java
new file mode 100644
index 0000000000..b26829e313
--- /dev/null
+++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/SecurityEnforcingChannelInterceptorTests.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright 2002-2008 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;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import org.springframework.integration.channel.QueueChannel;
+import org.springframework.integration.message.StringMessage;
+import org.springframework.security.AccessDecisionManager;
+import org.springframework.security.AccessDeniedException;
+import org.springframework.security.Authentication;
+import org.springframework.security.ConfigAttribute;
+import org.springframework.security.ConfigAttributeDefinition;
+import org.springframework.security.InsufficientAuthenticationException;
+
+/**
+ * @author Jonas Partner
+ */
+public class SecurityEnforcingChannelInterceptorTests {
+
+ private QueueChannel channel;
+
+ private SecurityEnforcingChannelInterceptor securityChannelInterceptor;
+
+
+ @Before
+ public void setUp() {
+ channel = new QueueChannel();
+ }
+
+
+ @Test(expected = AccessDeniedException.class)
+ public void testSendSecuredAndAccessDenied() {
+ try {
+ Runnable decision = new Runnable() {
+ public void run() {
+ throw new AccessDeniedException("nope");
+ }
+ };
+ this.registerInterceptor(new ConfigurableAccessDecisionManager(decision));
+ this.securityChannelInterceptor.setSendSecurityAttributes(new ConfigAttributeDefinition("ROLE_ADMIN"));
+ this.channel.send(new StringMessage("test"));
+ }
+ finally {
+ assertEquals("Wrong message count after refused send.", 0, channel.clear().size());
+ }
+ }
+
+ @Test
+ public void testUnsecuredSend() {
+ this.registerInterceptor(new ConfigurableAccessDecisionManager(null));
+ this.channel.send(new StringMessage("test"));
+ assertEquals("Wrong message count after send.", 1,channel.clear().size());
+ }
+
+ @Test
+ public void testSendSecuredAndAllowed() {
+ Runnable decision = new Runnable() {
+ public void run() {
+ }
+ };
+ this.registerInterceptor(new ConfigurableAccessDecisionManager(decision));
+ this.securityChannelInterceptor.setSendSecurityAttributes(new ConfigAttributeDefinition("ROLE_ADMIN"));
+ this.channel.send(new StringMessage("test"));
+ assertEquals("Wrong message count after send", 1, channel.clear().size());
+ }
+
+ @Test
+ public void testReceiveSecuredAndAllowed() {
+ Runnable decision = new Runnable() {
+ public void run() {
+ }
+ };
+ this.registerInterceptor(new ConfigurableAccessDecisionManager(decision));
+ this.securityChannelInterceptor.setReceiveSecurityAttributes(new ConfigAttributeDefinition("ROLE_ADMIN"));
+ this.channel.receive(0);
+ }
+
+ @Test(expected = AccessDeniedException.class)
+ public void testReceiveSecuredAndAccessDenied() {
+ Runnable decision = new Runnable() {
+ public void run() {
+ throw new AccessDeniedException("nope");
+ }
+ };
+ this.registerInterceptor(new ConfigurableAccessDecisionManager(decision));
+ this.securityChannelInterceptor.setReceiveSecurityAttributes(new ConfigAttributeDefinition("ROLE_ADMIN"));
+ this.channel.receive(0);
+ }
+
+ @Test
+ public void testReceiveUnsecured() {
+ Runnable decision = new Runnable() {
+ public void run() {
+ throw new AccessDeniedException("nope");
+ }
+ };
+ this.registerInterceptor(new ConfigurableAccessDecisionManager(decision));
+ this.channel.receive(0);
+ }
+
+
+ private void registerInterceptor(AccessDecisionManager accessDecisionManager) {
+ securityChannelInterceptor = new SecurityEnforcingChannelInterceptor(
+ accessDecisionManager, channel);
+
+ }
+
+
+ private static class ConfigurableAccessDecisionManager implements AccessDecisionManager {
+
+ private Runnable decisionRunner;
+
+ public ConfigurableAccessDecisionManager(Runnable decision) {
+ this.decisionRunner = decision;
+ }
+
+ public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
+ throws AccessDeniedException, InsufficientAuthenticationException {
+ this.decisionRunner.run();
+ }
+
+ public boolean supports(ConfigAttribute attribute) {
+ return true;
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean supports(Class clazz) {
+ return true;
+ }
+ }
+
+}
diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecureChannelsParserTests-noPropagationByDefaultContext.xml b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecureChannelsParserTests-noPropagationByDefaultContext.xml
new file mode 100644
index 0000000000..5ec08169cb
--- /dev/null
+++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecureChannelsParserTests-noPropagationByDefaultContext.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecureChannelsParserTests-propagateByDefaultContext.xml b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecureChannelsParserTests-propagateByDefaultContext.xml
new file mode 100644
index 0000000000..7cafb33911
--- /dev/null
+++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecureChannelsParserTests-propagateByDefaultContext.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecureChannelsParserTests.java b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecureChannelsParserTests.java
new file mode 100644
index 0000000000..0e0d03d2ca
--- /dev/null
+++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecureChannelsParserTests.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2002-2008 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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.After;
+import org.junit.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
+import org.springframework.context.support.ClassPathXmlApplicationContext;
+import org.springframework.integration.channel.MessageChannel;
+import org.springframework.integration.message.StringMessage;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.context.SecurityContextImpl;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+
+/**
+ * @author Jonas Partner
+ */
+public class SecureChannelsParserTests {
+
+ private ClassPathXmlApplicationContext applicationContext;
+
+ @Autowired
+ @Qualifier("propagationDefault")
+ MessageChannel propagationDefault;
+
+ @Autowired
+ @Qualifier("excludedFromPropagation")
+ MessageChannel excludedFromPropagation;
+
+
+ @After
+ public void tearDown() {
+ if (applicationContext != null) {
+ applicationContext.close();
+ }
+ }
+
+
+ @Test
+ public void testPropagationByDefault() {
+ loadApplicationContext(this.getClass().getSimpleName() + "-propagateByDefaultContext.xml");
+ assertTrue("security context did not propagate by setting message bus level default",
+ channelPropagatesSecurityContext(propagationDefault));
+ }
+
+ @Test
+ public void testNoPropagationOnExcludedChannel() {
+ loadApplicationContext(this.getClass().getSimpleName() + "-propagateByDefaultContext.xml");
+ assertFalse("security context propagated when channel was explicitly excluded",
+ channelPropagatesSecurityContext(excludedFromPropagation));
+ }
+
+ @Test
+ public void testNoPropagationWithNoDefaultPropagation() {
+ loadApplicationContext(this.getClass().getSimpleName() + "-noPropagationByDefaultContext.xml");
+ assertFalse("security context propagated when channel default was false and no secured tag present",
+ channelPropagatesSecurityContext(propagationDefault));
+ }
+
+
+ private boolean channelPropagatesSecurityContext(MessageChannel channel) {
+ login("bob", "bobspassword");
+ channel.send(new StringMessage("testMessage"));
+ SecurityContext context = (SecurityContext)
+ channel.receive(-1).getHeader().getAttribute("SPRING_SECURITY_CONTEXT");
+ return context != null;
+ }
+
+ private void login(String username, String password) {
+ UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(username, password);
+ SecurityContext context = new SecurityContextImpl();
+ context.setAuthentication(authToken);
+ SecurityContextHolder.setContext(context);
+ }
+
+ private void loadApplicationContext(String resource) {
+ this.applicationContext = new ClassPathXmlApplicationContext(resource, this.getClass());
+ AutowireCapableBeanFactory beanFactory = this.applicationContext.getAutowireCapableBeanFactory();
+ beanFactory.autowireBean(this);
+ }
+
+}
diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecuredParserTests-context.xml b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecuredParserTests-context.xml
new file mode 100644
index 0000000000..63c8a9a5d4
--- /dev/null
+++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecuredParserTests-context.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecuredParserTests.java b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecuredParserTests.java
new file mode 100644
index 0000000000..a9f0d54b56
--- /dev/null
+++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecuredParserTests.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2002-2008 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 static org.junit.Assert.assertNotNull;
+
+import org.junit.After;
+import org.junit.Test;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.integration.channel.MessageChannel;
+import org.springframework.integration.message.StringMessage;
+import org.springframework.security.AccessDeniedException;
+import org.springframework.security.context.SecurityContext;
+import org.springframework.security.context.SecurityContextHolder;
+import org.springframework.security.context.SecurityContextImpl;
+import org.springframework.security.providers.AuthenticationProvider;
+import org.springframework.security.providers.UsernamePasswordAuthenticationToken;
+import org.springframework.test.context.ContextConfiguration;
+import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests;
+
+/**
+ * @author Jonas Partner
+ */
+@ContextConfiguration
+public class SecuredParserTests extends AbstractJUnit4SpringContextTests{
+
+ @Autowired
+ private AuthenticationProvider provider;
+
+ @Autowired
+ @Qualifier("unsecured")
+ public MessageChannel unsecuredChannel;
+
+ @Autowired
+ @Qualifier("adminRequiredForSend")
+ public MessageChannel adminRequiredForSend;
+
+ @Autowired
+ @Qualifier("adminRequiredForReceive")
+ public MessageChannel adminRequiredForReceive;
+
+ @Autowired
+ @Qualifier("adminRequiredForSendAndReceive")
+ public MessageChannel adminRequiredForSendAndReceive;
+
+
+ @After
+ public void tearDown() {
+ SecurityContextHolder.clearContext();
+ }
+
+
+ @Test
+ public void testNoSecurityRestrictionsOnChannel() {
+ unsecuredChannel.send(new StringMessage("testUnsecured"));
+ assertNotNull("Message not received ", unsecuredChannel.receive(0));
+ }
+
+ @Test
+ public void testAdminRequiredForSendWithAccessGranted() {
+ login("jimi", "jimispassword");
+ adminRequiredForSend.send(new StringMessage("testmessage"));
+ SecurityContextHolder.clearContext();
+ assertNotNull("Message not received", adminRequiredForSend.receive(0));
+ }
+
+ @Test(expected=AccessDeniedException.class)
+ public void testAdminRequiredForSendWithAccessDenied() {
+ login("bob", "bobspassword");
+ adminRequiredForSend.send(new StringMessage("testmessage"));
+ }
+
+ @Test
+ public void testAdminRequiredForReceiveWithAccessGranted(){
+ adminRequiredForReceive.send(new StringMessage("testmessage"));
+ login("jimi", "jimispassword");
+ assertNotNull("Message not received", adminRequiredForReceive.receive(0));
+ }
+
+ @Test(expected=AccessDeniedException.class)
+ public void testAdminRequiredForReceiveWithAccessDenied() {
+ adminRequiredForReceive.send(new StringMessage("testmessage"));
+ login("bob", "bobspassword");
+ adminRequiredForReceive.receive(0);
+ }
+
+ @Test
+ public void testAdminRequiredForSendAndReceiveWithSendAccessGranted() {
+ login("jimi", "jimispassword");
+ adminRequiredForSendAndReceive.send(new StringMessage("test"));
+ }
+
+ @Test(expected=AccessDeniedException.class)
+ public void testAdminRequiredForSendAndReceiveWithSendAccessDenied() {
+ login("bob", "bobspassword");
+ adminRequiredForSendAndReceive.send(new StringMessage("test"));
+ }
+
+ @Test
+ public void testAdminRequiredForSendAndReceiveWithReceiveAccessGranted() {
+ login("jimi", "jimispassword");
+ adminRequiredForSendAndReceive.send(new StringMessage("test"));
+ assertNotNull("Message not received", adminRequiredForSendAndReceive.receive(0));
+ }
+
+ @Test(expected=AccessDeniedException.class)
+ public void testAdminRequiredForSendAndReceiveWithReceiveAccessDenied() {
+ login("bob","bobspassword");
+ adminRequiredForSendAndReceive.receive(0);
+ }
+
+
+ private void login(String username, String password) {
+ UsernamePasswordAuthenticationToken authToken = (UsernamePasswordAuthenticationToken)
+ provider.authenticate(new UsernamePasswordAuthenticationToken(username, password));
+ SecurityContext context = new SecurityContextImpl();
+ context.setAuthentication(authToken);
+ SecurityContextHolder.setContext(context);
+ }
+
+}
diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/commonSecurityConfiguration.xml b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/commonSecurityConfiguration.xml
new file mode 100644
index 0000000000..38dcb8617a
--- /dev/null
+++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/commonSecurityConfiguration.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/org.springframework.integration.security/src/test/resources/log4j.properties b/org.springframework.integration.security/src/test/resources/log4j.properties
new file mode 100644
index 0000000000..c9354151a2
--- /dev/null
+++ b/org.springframework.integration.security/src/test/resources/log4j.properties
@@ -0,0 +1,8 @@
+log4j.rootCategory=WARN, stdout
+
+log4j.appender.stdout=org.apache.log4j.ConsoleAppender
+log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
+log4j.appender.stdout.layout.ConversionPattern=%c{1}: %m%n
+
+log4j.category.org.springframework.integration.security=DEBUG
+log4j.category.org.springframework.integration=INFO
diff --git a/org.springframework.integration.security/template.mf b/org.springframework.integration.security/template.mf
new file mode 100644
index 0000000000..764bb18c03
--- /dev/null
+++ b/org.springframework.integration.security/template.mf
@@ -0,0 +1,11 @@
+Bundle-SymbolicName: org.springframework.integration.security
+Bundle-Name: Spring Integration Security Support
+Bundle-Vendor: SpringSource
+Bundle-ManifestVersion: 2
+Import-Template:
+ org.springframework.*;version="[2.5.4.A, 3.0.0)",
+ org.springframework.integration.*;version="[1.0.0, 1.0.0]",
+ org.apache.commons.logging;version="[1.1.1, 2.0.0)"
+Unversioned-Imports:
+ org.w3c.dom
+