From 1203e9858a5f12f3a412cd6659f2d67b52ca3a44 Mon Sep 17 00:00:00 2001 From: Vishal Puri Date: Tue, 15 May 2007 13:32:06 +0000 Subject: [PATCH] SEC-271: Added BeanDefitnitionParser for principal-repository, extended security schema and added unit tests --- .../LogoutFilterBeanDefinitionParser.java | 69 ++++++++++++++ .../config/LogoutHandlerOrderResolver.java | 89 +++++++++++++++++++ .../PrincipalRepositoryNamespaceTests.java | 66 ++++++++++++++ .../config/logout-filter-with-handlers.xml | 30 +++++++ ...tory.xml => principal-repository-jdbc.xml} | 10 ++- .../principal-repository-properties.xml | 22 +++++ .../config/principal-repository-user-map.xml | 28 ++++++ .../org/acegisecurity/config/user.properties | 2 + 8 files changed, 312 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/org/acegisecurity/config/LogoutFilterBeanDefinitionParser.java create mode 100644 core/src/main/java/org/acegisecurity/config/LogoutHandlerOrderResolver.java create mode 100644 core/src/test/java/org/acegisecurity/config/PrincipalRepositoryNamespaceTests.java create mode 100644 core/src/test/resources/org/acegisecurity/config/logout-filter-with-handlers.xml rename core/src/test/resources/org/acegisecurity/config/{principal-repository.xml => principal-repository-jdbc.xml} (88%) create mode 100644 core/src/test/resources/org/acegisecurity/config/principal-repository-properties.xml create mode 100644 core/src/test/resources/org/acegisecurity/config/principal-repository-user-map.xml create mode 100644 core/src/test/resources/org/acegisecurity/config/user.properties diff --git a/core/src/main/java/org/acegisecurity/config/LogoutFilterBeanDefinitionParser.java b/core/src/main/java/org/acegisecurity/config/LogoutFilterBeanDefinitionParser.java new file mode 100644 index 0000000000..246aa513b2 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/config/LogoutFilterBeanDefinitionParser.java @@ -0,0 +1,69 @@ +/** + * + */ +package org.acegisecurity.config; + +import org.acegisecurity.ui.logout.LogoutFilter; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.config.BeanDefinitionHolder; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.beans.factory.xml.AbstractBeanDefinitionParser; +import org.springframework.beans.factory.xml.ParserContext; +import org.springframework.util.StringUtils; +import org.w3c.dom.Element; + +/** + * @author vpuri + * @since + */ +public class LogoutFilterBeanDefinitionParser extends AbstractBeanDefinitionParser { + + // ~ Instance fields + // ================================================================================================ + private static final String REDIRECT_AFTER_LOGOUT_URL = "redirectAfterLogoutUrl"; + + private static final String LOGOUT_URL = "logoutUrl"; + + // ~ Methods + // ================================================================================================ + + protected AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) { + + // add the properties + RootBeanDefinition definition = new RootBeanDefinition(LogoutFilter.class); + setConstructorArgumentIfAvailable(0, element, REDIRECT_AFTER_LOGOUT_URL, "logoutSuccessUrl", definition); + // setPropertyIfAvailable(element, + // element.getAttribute(REDIRECT_AFTER_LOGOUT_URL), "logoutSuccessUrl", + // definition); + setPropertyIfAvailable(element, LOGOUT_URL, "filterProcessesUrl", definition); + + // register BFPP to check if LogoutFilter does not have setHandlers + // populated, introspect app ctx for LogoutHandlers, using Ordered (if + // present, otherwise assume Integer.MAX_VALUE) + RootBeanDefinition bfpp = new RootBeanDefinition(LogoutHandlerOrderResolver.class); + parserContext.getReaderContext().registerWithGeneratedName(bfpp); + + return definition; + } + + private void setConstructorArgumentIfAvailable(int index, Element element, String attribute, String property, + RootBeanDefinition definition) { + String propertyValue = element.getAttribute(attribute); + if (StringUtils.hasText(propertyValue)) { + definition.getConstructorArgumentValues().addIndexedArgumentValue(index, propertyValue); + } + } + + private void setPropertyIfAvailable(Element element, String attribute, String property, + RootBeanDefinition definition) { + String propertyValue = element.getAttribute(attribute); + if (StringUtils.hasText(propertyValue)) { + definition.getPropertyValues().addPropertyValue(property, propertyValue); + } + } + + // + +} diff --git a/core/src/main/java/org/acegisecurity/config/LogoutHandlerOrderResolver.java b/core/src/main/java/org/acegisecurity/config/LogoutHandlerOrderResolver.java new file mode 100644 index 0000000000..04037c21a3 --- /dev/null +++ b/core/src/main/java/org/acegisecurity/config/LogoutHandlerOrderResolver.java @@ -0,0 +1,89 @@ +/** + * + */ +package org.acegisecurity.config; + +import java.util.Collections; +import java.util.List; + +import org.acegisecurity.ui.logout.LogoutFilter; +import org.acegisecurity.ui.logout.LogoutHandler; +import org.acegisecurity.ui.logout.SecurityContextLogoutHandler; +import org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.config.ConstructorArgumentValues.ValueHolder; +import org.springframework.beans.factory.support.ManagedList; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.core.OrderComparator; +import org.springframework.core.Ordered; + +/** + * @author vpuri + * @since + */ +public class LogoutHandlerOrderResolver implements BeanFactoryPostProcessor { + + // ~ Methods + // ================================================================================================ + + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + // If LogoutFilter does not have setHandlers populated, introspect app + // ctx for LogoutHandlers, using Ordered (if present, otherwise assume + // Integer.MAX_VALUE) + String[] names = beanFactory.getBeanNamesForType(LogoutFilter.class); + RootBeanDefinition definition = (RootBeanDefinition) beanFactory.getBeanDefinition(names[0]); + ValueHolder holder = getHandlersIfConfigured(beanFactory, definition); + if (holder == null) { + // intropect the appcontext for registerd LogoutHandler + List logoutHandlers = retrieveAllLogoutHandlers(beanFactory); + definition.getConstructorArgumentValues().addIndexedArgumentValue(1, logoutHandlers); + } + } + + /** + * + * @param beanFactory + * @param definition + * @return + */ + private ValueHolder getHandlersIfConfigured(ConfigurableListableBeanFactory beanFactory, + RootBeanDefinition definition) { + // there should be only one LogoutFilter + return definition.getConstructorArgumentValues().getArgumentValue(1, null); + + } + + /** + * + * @param beanFactory + * @return + */ + private List retrieveAllLogoutHandlers(ConfigurableListableBeanFactory beanFactory) { + String[] names = beanFactory.getBeanNamesForType(LogoutHandler.class); + ManagedList list = new ManagedList(); + + for (int i = 0, n = names.length; i < n; i++) { + RootBeanDefinition definition = (RootBeanDefinition) beanFactory.getBeanDefinition(names[i]); + + if (Ordered.class.isAssignableFrom(definition.getBeanClass())) { + definition.getPropertyValues().addPropertyValue("order", getOrder(definition.getBeanClass())); + list.add(definition); + } + } + Collections.sort(list, new OrderComparator()); + return list; + } + + private int getOrder(Class clazz) { + if (clazz.getName().equals(TokenBasedRememberMeServices.class.getName())) { + return 0; + } + if (clazz.getName().equals(SecurityContextLogoutHandler.class.getName())) { + return 1; + } + return Integer.MAX_VALUE; + } + +} diff --git a/core/src/test/java/org/acegisecurity/config/PrincipalRepositoryNamespaceTests.java b/core/src/test/java/org/acegisecurity/config/PrincipalRepositoryNamespaceTests.java new file mode 100644 index 0000000000..3640d936e4 --- /dev/null +++ b/core/src/test/java/org/acegisecurity/config/PrincipalRepositoryNamespaceTests.java @@ -0,0 +1,66 @@ +/** + * + */ +package org.acegisecurity.config; + +import junit.framework.TestCase; + +import org.acegisecurity.GrantedAuthority; +import org.acegisecurity.GrantedAuthorityImpl; +import org.acegisecurity.userdetails.User; +import org.acegisecurity.userdetails.UserDetailsService; +import org.acegisecurity.userdetails.memory.InMemoryDaoImpl; +import org.acegisecurity.userdetails.memory.UserMap; +import org.springframework.beans.PropertyValue; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.ApplicationContext; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +/** + * @author vpuri + * + */ +public class PrincipalRepositoryNamespaceTests extends TestCase { + + public void testParserWithUserDefinition() { + ApplicationContext context = new ClassPathXmlApplicationContext( + "org/acegisecurity/config/principal-repository-user-map.xml"); + + ConfigurableListableBeanFactory clbf = (ConfigurableListableBeanFactory) context + .getAutowireCapableBeanFactory(); + + String[] names = clbf.getBeanNamesForType(UserDetailsService.class); + assertEquals(1, names.length); + + RootBeanDefinition definition = (RootBeanDefinition) clbf.getBeanDefinition(names[0]); + assertEquals(InMemoryDaoImpl.class, definition.getBeanClass()); + + UserMap map = new UserMap(); + + GrantedAuthority[] authotities = { new GrantedAuthorityImpl("ROLE_YO"), new GrantedAuthorityImpl("ROLE_YOYO") }; + + User user = new User("vishal", "nottellingya", true, true, true, true, authotities); + + map.addUser(user); + + assertPropertyValues(map, definition, "userMap"); + + } + + private void assertPropertyValues(UserMap assertionValue, RootBeanDefinition definition, String property) { + PropertyValue propertyValue = definition.getPropertyValues().getPropertyValue(property); + assertNotNull(propertyValue); + assertTrue(propertyValue.getValue() instanceof UserMap); + UserMap users = (UserMap) propertyValue.getValue(); + assertTrue(assertionValue.getUserCount() == users.getUserCount()); + assertEquals(assertionValue.getUser("vishal"), users.getUser("vishal")); + assertTrue(users.getUser("vishal").isEnabled()); + assertTrue(users.getUser("vishal").isAccountNonExpired()); + assertTrue(users.getUser("vishal").isAccountNonLocked()); + assertTrue(users.getUser("vishal").isCredentialsNonExpired()); + assertEquals(2, users.getUser("vishal").getAuthorities().length); + assertEquals(new GrantedAuthorityImpl("ROLE_YO"), users.getUser("vishal").getAuthorities()[0]); + assertEquals(new GrantedAuthorityImpl("ROLE_YOYO"), users.getUser("vishal").getAuthorities()[1]); + } +} diff --git a/core/src/test/resources/org/acegisecurity/config/logout-filter-with-handlers.xml b/core/src/test/resources/org/acegisecurity/config/logout-filter-with-handlers.xml new file mode 100644 index 0000000000..977261e59d --- /dev/null +++ b/core/src/test/resources/org/acegisecurity/config/logout-filter-with-handlers.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/test/resources/org/acegisecurity/config/principal-repository.xml b/core/src/test/resources/org/acegisecurity/config/principal-repository-jdbc.xml similarity index 88% rename from core/src/test/resources/org/acegisecurity/config/principal-repository.xml rename to core/src/test/resources/org/acegisecurity/config/principal-repository-jdbc.xml index d417826faf..543b986f32 100644 --- a/core/src/test/resources/org/acegisecurity/config/principal-repository.xml +++ b/core/src/test/resources/org/acegisecurity/config/principal-repository-jdbc.xml @@ -14,8 +14,11 @@ http://www.springframework.org/schema/security file:/Users/vpuri/interface21/ace + + + - + - + \ No newline at end of file diff --git a/core/src/test/resources/org/acegisecurity/config/principal-repository-properties.xml b/core/src/test/resources/org/acegisecurity/config/principal-repository-properties.xml new file mode 100644 index 0000000000..dfe21d29e4 --- /dev/null +++ b/core/src/test/resources/org/acegisecurity/config/principal-repository-properties.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/test/resources/org/acegisecurity/config/principal-repository-user-map.xml b/core/src/test/resources/org/acegisecurity/config/principal-repository-user-map.xml new file mode 100644 index 0000000000..4e035ff1d6 --- /dev/null +++ b/core/src/test/resources/org/acegisecurity/config/principal-repository-user-map.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/test/resources/org/acegisecurity/config/user.properties b/core/src/test/resources/org/acegisecurity/config/user.properties new file mode 100644 index 0000000000..cd5f8e8b31 --- /dev/null +++ b/core/src/test/resources/org/acegisecurity/config/user.properties @@ -0,0 +1,2 @@ +vishal=ity,ROLE_ADMIN +ity=vishal,ROLE_TELLER