diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/channel/config/SecurityPropagatingChannelsParser.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/channel/config/SecurityPropagatingChannelsParser.java index 6ee2ad08da..f18e8b4de2 100644 --- a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/channel/config/SecurityPropagatingChannelsParser.java +++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/channel/config/SecurityPropagatingChannelsParser.java @@ -16,23 +16,16 @@ package org.springframework.integration.security.channel.config; -import java.util.ArrayList; -import java.util.List; - -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; - import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; 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.integration.security.config.IncludeExcludePattern; import org.springframework.integration.security.config.IncludeExcludePatternParser; import org.springframework.integration.security.config.OrderedIncludeExcludeList; import org.springframework.security.context.SecurityContext; -import org.springframework.util.StringUtils; +import org.w3c.dom.Element; /** * Interprets the <secure-channels> element which controls default 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 index 5f3ea53315..9b0a464eb9 100644 --- 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 @@ -19,6 +19,7 @@ package org.springframework.integration.security.config; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; import org.springframework.integration.security.channel.config.SecuredChannelsParser; import org.springframework.integration.security.channel.config.SecurityPropagatingChannelsParser; +import org.springframework.integration.security.endpoint.config.SecurityEndpointInterceptorParser; /** * Namespace handler for the security namespace. @@ -30,6 +31,7 @@ public class IntegrationSecurityNamespaceHandler extends NamespaceHandlerSupport public void init() { registerBeanDefinitionParser("secured-channels", new SecuredChannelsParser()); registerBeanDefinitionParser("security-propagating-channels", new SecurityPropagatingChannelsParser()); + registerBeanDefinitionParser("endpoint-security-policy", new SecurityEndpointInterceptorParser()); } } diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/spring-integration-security-1.0.xsd b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/spring-integration-security-1.0.xsd index 26bd4f7ebf..dc827b584c 100644 --- a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/spring-integration-security-1.0.xsd +++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/config/spring-integration-security-1.0.xsd @@ -28,7 +28,7 @@ - + @@ -36,10 +36,7 @@ Targets - - - - + @@ -48,7 +45,6 @@ - Defines a bean post processor which propagates the diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/endpoint/SecurityEndpointInterceptor.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/endpoint/SecurityEndpointInterceptor.java index 74c524454c..599fb51eeb 100644 --- a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/endpoint/SecurityEndpointInterceptor.java +++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/endpoint/SecurityEndpointInterceptor.java @@ -17,7 +17,6 @@ package org.springframework.integration.security.endpoint; import org.aopalliance.intercept.MethodInvocation; - import org.springframework.integration.endpoint.interceptor.EndpointInterceptorAdapter; import org.springframework.integration.message.Message; import org.springframework.integration.security.SecurityContextUtils; @@ -35,7 +34,6 @@ public class SecurityEndpointInterceptor extends EndpointInterceptorAdapter { private final AccessDecisionManager accessDecisionManager; - public SecurityEndpointInterceptor(ConfigAttributeDefinition endpointSecurityAttributes, AccessDecisionManager accessDecisionManager) { super(); @@ -43,7 +41,6 @@ public class SecurityEndpointInterceptor extends EndpointInterceptorAdapter { this.accessDecisionManager = accessDecisionManager; } - @Override public boolean aroundInvoke(MethodInvocation invocation) throws Throwable { Message message = (Message) invocation.getArguments()[0]; @@ -54,8 +51,8 @@ public class SecurityEndpointInterceptor extends EndpointInterceptorAdapter { if (securityContext != null) { try { SecurityContextHolder.setContext(securityContext); - this.accessDecisionManager.decide(SecurityContextHolder.getContext().getAuthentication(), - invocation.getThis(), this.targetSecurityAttributes); + this.accessDecisionManager.decide(SecurityContextHolder.getContext().getAuthentication(), invocation + .getThis(), this.targetSecurityAttributes); return (Boolean) invocation.proceed(); } finally { @@ -63,8 +60,8 @@ public class SecurityEndpointInterceptor extends EndpointInterceptorAdapter { } } else { - this.accessDecisionManager.decide(SecurityContextHolder.getContext().getAuthentication(), - invocation.getThis(), this.targetSecurityAttributes); + this.accessDecisionManager.decide(SecurityContextHolder.getContext().getAuthentication(), invocation + .getThis(), this.targetSecurityAttributes); return (Boolean) invocation.proceed(); } } diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/endpoint/config/SecurityEndpointInterceptorParser.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/endpoint/config/SecurityEndpointInterceptorParser.java new file mode 100644 index 0000000000..e42e50ebbf --- /dev/null +++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/endpoint/config/SecurityEndpointInterceptorParser.java @@ -0,0 +1,68 @@ +/* + * 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.endpoint.config; + +import org.springframework.beans.BeanMetadataAttribute; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; +import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.integration.security.endpoint.SecurityEndpointInterceptor; +import org.springframework.security.ConfigAttributeDefinition; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +public class SecurityEndpointInterceptorParser extends AbstractSingleBeanDefinitionParser { + + public SecurityEndpointInterceptorParser() { + super(); + } + + @Override + protected boolean shouldGenerateId() { + return true; + } + + @Override + protected boolean shouldGenerateIdAsFallback() { + return true; + } + + protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) { + String access = element.getAttribute("access"); + Assert.hasText(access, "Access attribute is required for element endpoint-security-policy"); + + String accessDecisionManager = element.getAttribute("access-decision-manager"); + Assert.hasText(accessDecisionManager, "A non null value for the access-decision-manager is required"); + + ConfigAttributeDefinition accessDefintion = new ConfigAttributeDefinition(StringUtils.tokenizeToStringArray( + access, ",")); + builder.getBeanDefinition().getConstructorArgumentValues().addGenericArgumentValue( + new ValueHolder(accessDefintion)); + builder.getBeanDefinition().getConstructorArgumentValues().addGenericArgumentValue( + new RuntimeBeanReference(accessDecisionManager)); + + builder.getBeanDefinition().setBeanClass(SecurityEndpointInterceptor.class); + String beanName = BeanDefinitionReaderUtils.registerWithGeneratedName(builder.getBeanDefinition(), + parserContext.getRegistry()); + builder.getBeanDefinition().addMetadataAttribute(new BeanMetadataAttribute("interceptorName", beanName)); + } + +} diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecurityTestUtil.java b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/SecurityTestUtil.java similarity index 96% rename from org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecurityTestUtil.java rename to org.springframework.integration.security/src/test/java/org/springframework/integration/security/SecurityTestUtil.java index a355d0e724..226d9f4c42 100644 --- a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/config/SecurityTestUtil.java +++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/SecurityTestUtil.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.integration.security.config; +package org.springframework.integration.security; import org.springframework.security.GrantedAuthority; import org.springframework.security.GrantedAuthorityImpl; diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/channel/config/SecurityPropagatingChannelsParserTests-noPropagationByDefaultContext.xml b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/channel/config/SecurityPropagatingChannelsParserTests-noPropagationByDefaultContext.xml index 4f49dfec3e..05b56af744 100644 --- a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/channel/config/SecurityPropagatingChannelsParserTests-noPropagationByDefaultContext.xml +++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/channel/config/SecurityPropagatingChannelsParserTests-noPropagationByDefaultContext.xml @@ -1,6 +1,6 @@ - + - adminSpecial - admin.* + + adminSpecial + + + admin.* + diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/endpoint/EnpointSecurityIntegrationTest-context.xml b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/endpoint/EnpointSecurityIntegrationTest-context.xml new file mode 100644 index 0000000000..aad15f3c3e --- /dev/null +++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/endpoint/EnpointSecurityIntegrationTest-context.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/endpoint/EnpointSecurityIntegrationTest.java b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/endpoint/EnpointSecurityIntegrationTest.java new file mode 100644 index 0000000000..68c48060f0 --- /dev/null +++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/endpoint/EnpointSecurityIntegrationTest.java @@ -0,0 +1,71 @@ +/* + * 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.endpoint; + +import static org.junit.Assert.assertEquals; + +import org.junit.After; +import org.junit.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.integration.channel.MessageChannel; +import org.springframework.integration.message.StringMessage; +import org.springframework.integration.security.SecurityTestUtil; +import org.springframework.security.AccessDeniedException; +import org.springframework.security.context.SecurityContext; +import org.springframework.security.context.SecurityContextHolder; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.AbstractJUnit4SpringContextTests; + +@ContextConfiguration +public class EnpointSecurityIntegrationTest extends AbstractJUnit4SpringContextTests { + + @Autowired + MessageChannel channel; + + @Autowired + TestTarget testTarget; + + @After + public void tearDown() { + SecurityContextHolder.clearContext(); + } + + @Test + @DirtiesContext + public void testWithPermision() { + login("bob", "bobspassword", "ROLE_ADMIN"); + channel.send(new StringMessage("test")); + assertEquals("Wrong size of message list in target ", 1, testTarget.sentMessages.size()); + } + + /** + * + */ + @Test(expected = AccessDeniedException.class) + @DirtiesContext + public void testWithoutPermision() { + login("bob", "bobspassword", "ROLE_USER"); + channel.send(new StringMessage("test")); + assertEquals("Wrong size of message list in target ", 1, testTarget.sentMessages.size()); + } + + public void login(String username, String password, String... roles) { + SecurityContext context = SecurityTestUtil.createContext(username, password, roles); + SecurityContextHolder.setContext(context); + } + +} diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/endpoint/SecurityEndpointInterceptorTests.java b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/endpoint/SecurityEndpointInterceptorTests.java index ec495602ca..d2cb62d455 100644 --- a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/endpoint/SecurityEndpointInterceptorTests.java +++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/endpoint/SecurityEndpointInterceptorTests.java @@ -25,7 +25,7 @@ import org.junit.Test; import org.springframework.integration.message.Message; import org.springframework.integration.message.StringMessage; import org.springframework.integration.security.SecurityContextUtils; -import org.springframework.integration.security.config.SecurityTestUtil; +import org.springframework.integration.security.SecurityTestUtil; import org.springframework.security.AccessDecisionManager; import org.springframework.security.AccessDeniedException; import org.springframework.security.ConfigAttributeDefinition; diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/endpoint/TestTarget.java b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/endpoint/TestTarget.java new file mode 100644 index 0000000000..f5fc27b246 --- /dev/null +++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/endpoint/TestTarget.java @@ -0,0 +1,34 @@ +/* + * 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.endpoint; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.integration.message.Message; +import org.springframework.integration.message.MessageTarget; + +public class TestTarget implements MessageTarget { + + public List> sentMessages = new ArrayList>(); + + public boolean send(Message message) { + sentMessages.add(message); + return true; + } + +} diff --git a/org.springframework.integration/src/main/java/org/springframework/integration/config/EndpointInterceptorParser.java b/org.springframework.integration/src/main/java/org/springframework/integration/config/EndpointInterceptorParser.java index 572e5c91cb..e58967af89 100644 --- a/org.springframework.integration/src/main/java/org/springframework/integration/config/EndpointInterceptorParser.java +++ b/org.springframework.integration/src/main/java/org/springframework/integration/config/EndpointInterceptorParser.java @@ -19,35 +19,34 @@ package org.springframework.integration.config; import java.util.HashMap; import java.util.Map; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.config.RuntimeBeanReference; +import org.springframework.beans.factory.parsing.BeanComponentDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; +import org.springframework.beans.factory.xml.NamespaceHandler; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.Assert; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import org.springframework.beans.factory.config.BeanDefinitionHolder; -import org.springframework.beans.factory.config.RuntimeBeanReference; -import org.springframework.beans.factory.parsing.BeanComponentDefinition; -import org.springframework.beans.factory.support.ManagedList; -import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; -import org.springframework.beans.factory.xml.ParserContext; -import org.springframework.integration.ConfigurationException; - /** * A helper class for parsing the sub-elements of an endpoint's * interceptors element. - * + * * @author Mark Fisher */ public class EndpointInterceptorParser { private final Map parsers = new HashMap(); - public EndpointInterceptorParser() { this.parsers.put("transaction-interceptor", new TransactionInterceptorParser()); this.parsers.put("concurrency-interceptor", new ConcurrencyInterceptorParser()); } - @SuppressWarnings("unchecked") public ManagedList parseEndpointInterceptors(Element element, ParserContext parserContext) { ManagedList interceptors = new ManagedList(); @@ -58,7 +57,8 @@ public class EndpointInterceptorParser { Element childElement = (Element) child; String localName = child.getLocalName(); if ("bean".equals(localName)) { - BeanDefinitionParserDelegate beanParser = new BeanDefinitionParserDelegate(parserContext.getReaderContext()); + BeanDefinitionParserDelegate beanParser = new BeanDefinitionParserDelegate(parserContext + .getReaderContext()); beanParser.initDefaults(childElement.getOwnerDocument().getDocumentElement()); BeanDefinitionHolder beanDefinitionHolder = beanParser.parseBeanDefinitionElement(childElement); parserContext.registerBeanComponent(new BeanComponentDefinition(beanDefinitionHolder)); @@ -70,11 +70,13 @@ public class EndpointInterceptorParser { } else { BeanDefinitionRegisteringParser parser = this.parsers.get(localName); + String interceptorBeanName; if (parser == null) { - throw new ConfigurationException("No parser available for interceptor element '" - + localName + "'."); + interceptorBeanName = handleNonstandardInterceptor(childElement, parserContext); + } + else { + interceptorBeanName = parser.parse(childElement, parserContext); } - String interceptorBeanName = parser.parse(childElement, parserContext); interceptors.add(new RuntimeBeanReference(interceptorBeanName)); } } @@ -82,4 +84,15 @@ public class EndpointInterceptorParser { return interceptors; } + protected String handleNonstandardInterceptor(Element childElement, ParserContext parserContext) { + NamespaceHandler handlerFromOtherNamespace = parserContext.getReaderContext().getNamespaceHandlerResolver() + .resolve(childElement.getNamespaceURI()); + AbstractBeanDefinition interceptorDefintiion = ((AbstractBeanDefinition) handlerFromOtherNamespace.parse( + childElement, parserContext)); + String beanName = (String) interceptorDefintiion.getMetadataAttribute("interceptorName").getValue(); + Assert.hasText("No value for interceptorName provided by namespace handler for element " + + childElement.getNodeName()); + return beanName; + } + }