From 53ceb3f5c71017342f71de129cd9f4d6d1b9c7bf Mon Sep 17 00:00:00 2001 From: Jonas Partner Date: Wed, 25 Jun 2008 17:41:46 +0000 Subject: [PATCH] target securing advice --- .../security/TargetSecuringAdvisor.java | 67 +++++++++ .../security/TargetSecuringInterceptor.java | 65 +++++++++ .../security/TargetSendMethodArgMatcher.java | 57 ++++++++ .../security/TargetSecuringAdvisorTests.java | 134 ++++++++++++++++++ .../TargetSecuringInterceptorTests.java | 110 ++++++++++++++ 5 files changed, 433 insertions(+) create mode 100644 org.springframework.integration.security/src/main/java/org/springframework/integration/security/TargetSecuringAdvisor.java create mode 100644 org.springframework.integration.security/src/main/java/org/springframework/integration/security/TargetSecuringInterceptor.java create mode 100644 org.springframework.integration.security/src/main/java/org/springframework/integration/security/TargetSendMethodArgMatcher.java create mode 100644 org.springframework.integration.security/src/test/java/org/springframework/integration/security/TargetSecuringAdvisorTests.java create mode 100644 org.springframework.integration.security/src/test/java/org/springframework/integration/security/TargetSecuringInterceptorTests.java diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/TargetSecuringAdvisor.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/TargetSecuringAdvisor.java new file mode 100644 index 0000000000..6d5bba1713 --- /dev/null +++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/TargetSecuringAdvisor.java @@ -0,0 +1,67 @@ +/* + * 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.aopalliance.aop.Advice; +import org.springframework.aop.ClassFilter; +import org.springframework.aop.MethodMatcher; +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.AbstractPointcutAdvisor; +import org.springframework.aop.support.ClassFilters; +import org.springframework.aop.support.MethodMatchers; +import org.springframework.aop.support.NameMatchMethodPointcut; +import org.springframework.aop.support.RootClassFilter; +import org.springframework.integration.message.BlockingTarget; +import org.springframework.integration.message.Target; +import org.springframework.security.AccessDecisionManager; +import org.springframework.security.ConfigAttributeDefinition; +import org.springframework.util.StringUtils; + +public class TargetSecuringAdvisor extends AbstractPointcutAdvisor implements Pointcut { + + private ClassFilter classFilter; + + private MethodMatcher matcher; + + private Advice targetSecuringInterceptor; + + public TargetSecuringAdvisor(AccessDecisionManager accessDecisionManager, String securityConfig) { + targetSecuringInterceptor = new TargetSecuringInterceptor(new ConfigAttributeDefinition(StringUtils + .tokenizeToStringArray(securityConfig, ",")), accessDecisionManager); + classFilter = ClassFilters.union(new RootClassFilter(Target.class), new RootClassFilter(BlockingTarget.class)); + NameMatchMethodPointcut nameMatcher = new NameMatchMethodPointcut(); + nameMatcher.addMethodName("send"); + matcher = MethodMatchers.intersection(nameMatcher, new TargetSendMethodArgMatcher()); + } + + public Pointcut getPointcut() { + return this; + } + + public Advice getAdvice() { + return targetSecuringInterceptor; + } + + public ClassFilter getClassFilter() { + return classFilter; + } + + public MethodMatcher getMethodMatcher() { + return matcher; + } + +} diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/TargetSecuringInterceptor.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/TargetSecuringInterceptor.java new file mode 100644 index 0000000000..15fc4c8f45 --- /dev/null +++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/TargetSecuringInterceptor.java @@ -0,0 +1,65 @@ +/* + * 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.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.integration.message.Message; +import org.springframework.security.AccessDecisionManager; +import org.springframework.security.ConfigAttributeDefinition; +import org.springframework.security.context.SecurityContext; +import org.springframework.security.context.SecurityContextHolder; + +/** + * + * @author Jonas Partner + * + */ +public class TargetSecuringInterceptor implements MethodInterceptor { + + private final ConfigAttributeDefinition targetSecurityAttributes; + + private final AccessDecisionManager accessDecisionManager; + + public TargetSecuringInterceptor(ConfigAttributeDefinition targetSecurityAttributes, + AccessDecisionManager accessDecisionManager) { + this.targetSecurityAttributes = targetSecurityAttributes; + this.accessDecisionManager = accessDecisionManager; + } + + public Object invoke(MethodInvocation invocation) throws Throwable { + Message message = (Message) invocation.getArguments()[0]; + SecurityContext ctx = SecurityContextUtils.getSecurityContextFromHeader(message); + if (ctx != null) { + try { + SecurityContextHolder.setContext(ctx); + accessDecisionManager.decide(SecurityContextHolder.getContext().getAuthentication(), invocation + .getThis(), targetSecurityAttributes); + return invocation.proceed(); + } + finally { + SecurityContextHolder.clearContext(); + } + } + else { + accessDecisionManager.decide(SecurityContextHolder.getContext().getAuthentication(), invocation.getThis(), + targetSecurityAttributes); + return invocation.proceed(); + } + } + +} diff --git a/org.springframework.integration.security/src/main/java/org/springframework/integration/security/TargetSendMethodArgMatcher.java b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/TargetSendMethodArgMatcher.java new file mode 100644 index 0000000000..57b743ffc0 --- /dev/null +++ b/org.springframework.integration.security/src/main/java/org/springframework/integration/security/TargetSendMethodArgMatcher.java @@ -0,0 +1,57 @@ +/* + * 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 java.lang.reflect.Method; + +import org.springframework.aop.support.StaticMethodMatcher; +import org.springframework.integration.message.Message; + +/** + * + * @author Jonas Partner + * + */ +public class TargetSendMethodArgMatcher extends StaticMethodMatcher{ + + + public boolean matches(Method method, Class targetClass) { + return argsTypesMatch(method.getParameterTypes()); + } + + + + protected boolean argsTypesMatch(Class[] args){ + if(args.length > 2){ + return false; + } + + if(args.length > 0){ + if(!Message.class.isAssignableFrom(args[0] )){ + return false; + } + } + + if (args.length > 1 ){ + if(!long.class.isAssignableFrom(args[1])){ + return false; + } + } + return true; + + + } +} \ No newline at end of file diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/TargetSecuringAdvisorTests.java b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/TargetSecuringAdvisorTests.java new file mode 100644 index 0000000000..b61fe37b9a --- /dev/null +++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/TargetSecuringAdvisorTests.java @@ -0,0 +1,134 @@ +/* + * 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.junit.Test; +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.integration.message.BlockingTarget; +import org.springframework.integration.message.Message; +import org.springframework.integration.message.StringMessage; +import org.springframework.integration.message.Target; +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 TargetSecuringAdvisorTests { + + public Object proxy(Object target, TargetSecuringAdvisor advisor) { + ProxyFactory proxyFactory = new ProxyFactory(target); + proxyFactory.addAdvisor(advisor); + + return proxyFactory.getProxy(); + + } + + @Test + public void testTargetSendIntercpeted() { + TestTarget target = new TestTarget(); + } + + @Test(expected = AccessDeniedException.class) + public void testTargetSendAdvised() { + TargetSecuringAdvisor advisor = new TargetSecuringAdvisor(new AlwaysDenyAccessDecisionManager(), "ROLE_ADMIN"); + Target target = (Target) proxy(new TestTarget(), advisor); + target.send(new StringMessage("test")); + } + + @Test(expected = AccessDeniedException.class) + public void testBlockingTargetSendAdvised() { + TargetSecuringAdvisor advisor = new TargetSecuringAdvisor(new AlwaysDenyAccessDecisionManager(), "ROLE_ADMIN"); + Target target = (Target) proxy(new BlockingTestTarget(), advisor); + target.send(new StringMessage("test")); + } + + @Test(expected = AccessDeniedException.class) + public void testBlockingTargetSendWithTimeoutAdvised() { + TargetSecuringAdvisor advisor = new TargetSecuringAdvisor(new AlwaysDenyAccessDecisionManager(), "ROLE_ADMIN"); + BlockingTarget target = (BlockingTarget) proxy(new BlockingTestTarget(), advisor); + target.send(new StringMessage("test"), 10l); + } + + @Test + public void testTargetSendNotFromTargetInterface() { + TargetSecuringAdvisor advisor = new TargetSecuringAdvisor(new AlwaysDenyAccessDecisionManager(), "ROLE_ADMIN"); + OtherSend target = (OtherSend) proxy(new TestTarget(), advisor); + target.send(10l); + } + + static interface OtherSend { + + public void send(long l); + } + + static class AlwaysDenyAccessDecisionManager implements AccessDecisionManager { + + public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config) + throws AccessDeniedException, InsufficientAuthenticationException { + throw new AccessDeniedException("dave"); + } + + public boolean supports(ConfigAttribute attribute) { + return true; + } + + public boolean supports(Class clazz) { + return true; + } + + } + + static class TestTarget implements Target, OtherSend { + + boolean invoked; + + public boolean send(Message message) { + invoked = true; + return false; + } + + public void send(long a) { + + } + + } + + static class BlockingTestTarget implements BlockingTarget { + + boolean invoked; + + public boolean send(Message message) { + invoked = true; + return false; + } + + public void send() { + } + + public boolean send(Message message, long timeout) { + return false; + } + + } + +} diff --git a/org.springframework.integration.security/src/test/java/org/springframework/integration/security/TargetSecuringInterceptorTests.java b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/TargetSecuringInterceptorTests.java new file mode 100644 index 0000000000..5dd1af799f --- /dev/null +++ b/org.springframework.integration.security/src/test/java/org/springframework/integration/security/TargetSecuringInterceptorTests.java @@ -0,0 +1,110 @@ +/* + * 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 java.util.ArrayList; +import java.util.List; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.aop.framework.ProxyFactory; +import org.springframework.integration.message.Message; +import org.springframework.integration.message.StringMessage; +import org.springframework.integration.message.Target; +import org.springframework.integration.security.SecurityContextUtils; +import org.springframework.integration.security.TargetSecuringInterceptor; +import org.springframework.integration.security.config.SecurityTestUtil; +import org.springframework.security.AccessDecisionManager; +import org.springframework.security.AccessDeniedException; +import org.springframework.security.ConfigAttributeDefinition; +import org.springframework.security.context.SecurityContext; +import org.springframework.security.vote.AccessDecisionVoter; +import org.springframework.security.vote.AuthenticatedVoter; +import org.springframework.security.vote.RoleVoter; +import org.springframework.security.vote.UnanimousBased; +import org.springframework.util.StringUtils; + +/** + * + * @author Jonas Partner + * + */ +public class TargetSecuringInterceptorTests { + + + UnanimousBased accessDecisionManager; + + @Before + public void setup(){ + accessDecisionManager = new UnanimousBased(); + List voterList = new ArrayList(); + voterList.add(new AuthenticatedVoter()); + voterList.add(new RoleVoter()); + accessDecisionManager.setDecisionVoters(voterList); + } + + + + public Object createProxy(Object target,String securityAttributes, AccessDecisionManager accessDecisionManager){ + TargetSecuringInterceptor interceptor = new TargetSecuringInterceptor(new ConfigAttributeDefinition(StringUtils.tokenizeToStringArray(securityAttributes,",")), accessDecisionManager); + ProxyFactory factory = new ProxyFactory(target); + factory.addAdvice(interceptor); + return factory.getProxy(); + } + + @Test(expected=AccessDeniedException.class) + public void testAccessDenied(){ + Target proxiedTarget = (Target) createProxy(new TestTarget(), "IS_AUTHENTICATED_FULLY, ROLE_ADMIN", accessDecisionManager); + SecurityContext sctx = SecurityTestUtil.createContext("bob", "password", "IS_AUTHENTICATED_ANONYMOUSLY", "ROLE_USER"); + StringMessage message = new StringMessage("test"); + SecurityContextUtils.setSecurityContextHeader(sctx, message); + proxiedTarget.send(message); + } + + @Test + public void testAccessGranted(){ + Target proxiedTarget = (Target) createProxy(new TestTarget(), "IS_AUTHENTICATED_FULLY, ROLE_ADMIN", accessDecisionManager); + SecurityContext sctx = SecurityTestUtil.createContext("bob", "password", "IS_AUTHENTICATED_ANONYMOUSLY", "ROLE_USER", "ROLE_ADMIN"); + StringMessage message = new StringMessage("test"); + SecurityContextUtils.setSecurityContextHeader(sctx, message); + proxiedTarget.send(message); + } + + + @Test(expected=RuntimeException.class) + public void testNotAuthenticated(){ + Target proxiedTarget = (Target) createProxy(new TestTarget(), "IS_AUTHENTICATED_FULLY, ROLE_ADMIN", accessDecisionManager); + StringMessage message = new StringMessage("test"); + proxiedTarget.send(message); + } + + + static class TestTarget implements Target{ + + boolean invoked; + + public boolean send(Message message) { + invoked = true; + return false; + } + + public void send(long l) { + // TODO Auto-generated method stub + + } + } + +}