Răsfoiți Sursa

SEC-271: Added BeanDefitnitionParser for principal-repository, extended security schema and added unit tests

Vishal Puri 18 ani în urmă
părinte
comite
1203e9858a

+ 69 - 0
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);
+		}
+	}
+
+	//
+
+}

+ 89 - 0
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;
+	}
+
+}

+ 66 - 0
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]);
+	}
+}

+ 30 - 0
core/src/test/resources/org/acegisecurity/config/logout-filter-with-handlers.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:security="http://www.springframework.org/schema/security"
+	xmlns:util="http://www.springframework.org/schema/util"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
+	http://www.springframework.org/schema/util http://www.springframework.org/schema/beans/spring-util-2.0.xsd
+http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
+
+	<!-- http://www.springframework.org/schema/security file:/Users/vpuri/interface21/acegisecurity/trunk/acegisecurity/core/src/main/resources/org/acegisecurity/config/spring-security-2.0.xsd -->
+	<!-- http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd" -->
+
+	<!-- If LogoutFilter does not have setHandlers populated, introspect app ctx for LogoutHandlers, using Ordered (if present, otherwise assume Integer.MAX_VALUE) -->
+	<!-- The logoutUrl and redirectAfterLogout are both optional and default to that shown -->
+	<security:logout-support id="logoutFilter"
+		redirectAfterLogoutUrl="/" logoutUrl="/logout" />
+
+	<security:authentication-remember-me-services
+		id="rememberMeServices" key="someValue" />
+
+	<security:principal-repository id="userDetailsService">
+		<security:user-definition username="vishal"
+			password="nottellingya" enabled="true">
+			<security:granted-authority authority="ROLE_YO" />
+			<security:granted-authority authority="ROLE_YOYO" />
+			<!-- TODO: <security:granted-authority-ref authorityBeanRef="fooBarAuthority"/>-->
+		</security:user-definition>
+	</security:principal-repository>
+</beans>

+ 6 - 4
core/src/test/resources/org/acegisecurity/config/principal-repository.xml → 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
 	<!-- userDetailsService, This is used if they want an out-of-the-bx UserDetailsService; if they write their own, this goes away and they wire a legacy bean definition and then the various
 		beans depending on a UserDetailsService will auto-detect it at runtime OR provide a way of setUserDetailsService(UserDetailsService) if to specified explicitly.
 		If they fail to provide a repository, the security-autodetect will set one up for them with a few basic in-memory users and pwds -->
+		
+	<!--<security:security-autoconfig/>	-->
+	
 	<security:principal-repository id="userDetailsService">
-		<security:jdbc dataSourceBeanRef="dataSource" jdbcTemplateBeanRef="jdbcTemplate" authoritiesByUsernameQuery=""  rolePrefix="" usernameBasedPrimaryKey="true"  usersByUsernameQuery=""/>
+		<security:jdbc dataSourceBeanRef="dataSource"/>
 	</security:principal-repository>
 
 	<bean id="dataSource"
@@ -35,8 +38,7 @@ http://www.springframework.org/schema/security file:/Users/vpuri/interface21/ace
 		</property>
 	</bean>
 	
-	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
+	<!--<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
 		<property name="dataSource" ref="dataSource"></property>
-	</bean>
-
+	</bean>-->
 </beans>

+ 22 - 0
core/src/test/resources/org/acegisecurity/config/principal-repository-properties.xml

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:security="http://www.springframework.org/schema/security"
+	xmlns:util="http://www.springframework.org/schema/util"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
+	http://www.springframework.org/schema/util http://www.springframework.org/schema/beans/spring-util-2.0.xsd
+http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
+
+	<!-- http://www.springframework.org/schema/security file:/Users/vpuri/interface21/acegisecurity/trunk/acegisecurity/core/src/main/resources/org/acegisecurity/config/spring-security-2.0.xsd -->
+	<!-- http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd" -->
+
+	<!-- userDetailsService, This is used if they want an out-of-the-bx UserDetailsService; if they write their own, this goes away and they wire a legacy bean definition and then the various
+		beans depending on a UserDetailsService will auto-detect it at runtime OR provide a way of setUserDetailsService(UserDetailsService) if to specified explicitly.
+		If they fail to provide a repository, the security-autodetect will set one up for them with a few basic in-memory users and pwds -->
+		
+	<security:principal-repository id="userDetailsService">
+		<security:properties resource="classpath:org/acegisecurity/config/user.properties"/>
+	</security:principal-repository>
+	
+</beans>

+ 28 - 0
core/src/test/resources/org/acegisecurity/config/principal-repository-user-map.xml

@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<beans xmlns="http://www.springframework.org/schema/beans"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xmlns:security="http://www.springframework.org/schema/security"
+	xmlns:util="http://www.springframework.org/schema/util"
+	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd 
+	http://www.springframework.org/schema/util http://www.springframework.org/schema/beans/spring-util-2.0.xsd
+http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd">
+
+	<!-- http://www.springframework.org/schema/security file:/Users/vpuri/interface21/acegisecurity/trunk/acegisecurity/core/src/main/resources/org/acegisecurity/config/spring-security-2.0.xsd -->
+	<!-- http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.xsd" -->
+
+	<!-- userDetailsService, This is used if they want an out-of-the-bx UserDetailsService; if they write their own, this goes away and they wire a legacy bean definition and then the various
+		beans depending on a UserDetailsService will auto-detect it at runtime OR provide a way of setUserDetailsService(UserDetailsService) if to specified explicitly.
+		If they fail to provide a repository, the security-autodetect will set one up for them with a few basic in-memory users and pwds -->
+		
+	<security:principal-repository id="userDetailsService">
+			<security:user-definition username="vishal" password="nottellingya" enabled="true">
+				<security:granted-authority authority="ROLE_YO"/>
+			    <security:granted-authority authority="ROLE_YOYO"/>
+			    <!-- TODO: <security:granted-authority-ref authorityBeanRef="fooBarAuthority"/>-->
+			</security:user-definition>
+	</security:principal-repository>
+	
+	<!-- TODO: <security:granted-authority id="fooBarAuthority" authority="ROLE_FOOBAR"/> -->
+	
+</beans>

+ 2 - 0
core/src/test/resources/org/acegisecurity/config/user.properties

@@ -0,0 +1,2 @@
+vishal=ity,ROLE_ADMIN
+ity=vishal,ROLE_TELLER