diff --git a/core/pom.xml b/core/pom.xml index ccbd57a87e..6ab0b8e763 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,7 +13,7 @@ org.springframework spring-expression - + org.springframework spring-core @@ -81,12 +81,6 @@ hsqldb test - - jaxen - jaxen - 1.1.1 - true - org.apache.tomcat annotations-api @@ -98,7 +92,7 @@ true - + diff --git a/core/src/test/java/org/springframework/security/core/authority/mapping/XmlMappableRolesRetrieverTests.java b/core/src/test/java/org/springframework/security/core/authority/mapping/XmlMappableRolesRetrieverTests.java deleted file mode 100755 index 75d21e7ecf..0000000000 --- a/core/src/test/java/org/springframework/security/core/authority/mapping/XmlMappableRolesRetrieverTests.java +++ /dev/null @@ -1,103 +0,0 @@ -package org.springframework.security.core.authority.mapping; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.Arrays; -import java.util.Collection; -import java.util.Set; - -import org.springframework.security.core.authority.mapping.XmlMappableAttributesRetriever; - -import junit.framework.TestCase; - -/** - * - * @author TSARDD - * @since 18-okt-2007 - */ -@SuppressWarnings("unchecked") -public class XmlMappableRolesRetrieverTests extends TestCase { - private static final String DEFAULT_XML = "Role1Role2"; - - private static final String DEFAULT_XPATH = "/roles/role/text()"; - - private static final String[] DEFAULT_EXPECTED_ROLES = new String[] { "Role1", "Role2" }; - - public final void testAfterPropertiesSetException() { - TestXmlMappableAttributesRetriever t = new TestXmlMappableAttributesRetriever(); - try { - t.afterPropertiesSet(); - fail("AfterPropertiesSet didn't throw expected exception"); - } catch (IllegalArgumentException expected) { - } catch (Exception unexpected) { - fail("AfterPropertiesSet throws unexpected exception"); - } - } - - public void testGetMappableRoles() { - XmlMappableAttributesRetriever r = getXmlMappableRolesRetriever(true, getDefaultInputStream(), DEFAULT_XPATH); - Set resultRoles = r.getMappableAttributes(); - assertNotNull("Result roles should not be null", resultRoles); - assertEquals("Number of result roles doesn't match expected number of roles", DEFAULT_EXPECTED_ROLES.length, resultRoles.size()); - Collection expectedRolesColl = Arrays.asList(DEFAULT_EXPECTED_ROLES); - assertTrue("Role collections do not match", expectedRolesColl.containsAll(resultRoles) - && resultRoles.containsAll(expectedRolesColl)); - } - - public void testCloseInputStream() { - testCloseInputStream(true); - } - - public void testDontCloseInputStream() { - testCloseInputStream(false); - } - - private void testCloseInputStream(boolean closeAfterRead) { - CloseableByteArrayInputStream is = getDefaultInputStream(); - XmlMappableAttributesRetriever r = getXmlMappableRolesRetriever(closeAfterRead, is, DEFAULT_XPATH); - r.getMappableAttributes(); - assertEquals(is.isClosed(), closeAfterRead); - } - - private XmlMappableAttributesRetriever getXmlMappableRolesRetriever(boolean closeInputStream, InputStream is, String xpath) { - XmlMappableAttributesRetriever result = new TestXmlMappableAttributesRetriever(); - result.setCloseInputStream(closeInputStream); - result.setXmlInputStream(is); - result.setXpathExpression(xpath); - try { - result.afterPropertiesSet(); - } catch (Exception e) { - fail("Unexpected exception" + e.toString()); - } - return result; - } - - private CloseableByteArrayInputStream getDefaultInputStream() { - return getInputStream(DEFAULT_XML); - } - - private CloseableByteArrayInputStream getInputStream(String data) { - return new CloseableByteArrayInputStream(data.getBytes()); - } - - private static final class TestXmlMappableAttributesRetriever extends XmlMappableAttributesRetriever { - } - - private static final class CloseableByteArrayInputStream extends ByteArrayInputStream { - private boolean closed = false; - - public CloseableByteArrayInputStream(byte[] buf) { - super(buf); - } - - public void close() throws IOException { - super.close(); - closed = true; - } - - public boolean isClosed() { - return closed; - } - } -} diff --git a/core/template.mf b/core/template.mf index 38c17de843..c9ea65886d 100644 --- a/core/template.mf +++ b/core/template.mf @@ -23,10 +23,7 @@ Import-Template: org.springframework.transaction.*;version="[3.0.0, 3.1.0)";resolution:=optional, org.springframework.util;version="[3.0.0, 3.1.0)", net.sf.ehcache.*;version="[1.4.1, 2.0.0)";resolution:=optional, - org.w3c.dom;version="0";resolution:=optional, - org.xml.sax;version="0";resolution:=optional, javax.annotation.security.*;version="0";resolution:=optional, javax.crypto.*;version="0";resolution:=optional, - javax.xml.*;version="0";resolution:=optional, javax.security.auth.*;version="0";resolution:=optional \ No newline at end of file diff --git a/docs/manual/src/docbook/preauth.xml b/docs/manual/src/docbook/preauth.xml index 995a65997d..ead72dbb5c 100644 --- a/docs/manual/src/docbook/preauth.xml +++ b/docs/manual/src/docbook/preauth.xml @@ -187,6 +187,11 @@ class="org.springframework.security.web.authentication.preauth.PreAuthenticatedA userPrincipal property of the HttpServletRequest. use of this filter would usually be combined with the use of J2EE roles as described above in . + + There is a sample application in the codebase which uses this approach, so get hold of the code from subversion and + have a look at the application context file if you are interested. The code is in the samples/preauth + directory. + diff --git a/samples/preauth/src/main/webapp/WEB-INF/applicationContext-security.xml b/samples/preauth/src/main/webapp/WEB-INF/applicationContext-security.xml index 083ed0b572..4e72628f05 100644 --- a/samples/preauth/src/main/webapp/WEB-INF/applicationContext-security.xml +++ b/samples/preauth/src/main/webapp/WEB-INF/applicationContext-security.xml @@ -9,8 +9,8 @@ + xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> @@ -32,7 +32,18 @@ - + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - diff --git a/web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/J2eePreAuthenticatedProcessingFilter.java b/web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/J2eePreAuthenticatedProcessingFilter.java index 7a2d231ef1..9afa7276a7 100755 --- a/web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/J2eePreAuthenticatedProcessingFilter.java +++ b/web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/J2eePreAuthenticatedProcessingFilter.java @@ -13,6 +13,7 @@ import org.springframework.security.web.authentication.preauth.AbstractPreAuthen * @since 2.0 */ public class J2eePreAuthenticatedProcessingFilter extends AbstractPreAuthenticatedProcessingFilter { + /** * Return the J2EE user name. */ diff --git a/web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/WebXmlMappableAttributesRetriever.java b/web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/WebXmlMappableAttributesRetriever.java index 6bd62cf032..b065246316 100755 --- a/web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/WebXmlMappableAttributesRetriever.java +++ b/web/src/main/java/org/springframework/security/web/authentication/preauth/j2ee/WebXmlMappableAttributesRetriever.java @@ -1,50 +1,123 @@ package org.springframework.security.web.authentication.preauth.j2ee; +import java.io.IOException; import java.io.InputStream; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; -import org.springframework.security.core.authority.mapping.XmlMappableAttributesRetriever; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.FactoryConfigurationError; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ResourceLoaderAware; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; +import org.springframework.security.core.authority.mapping.MappableAttributesRetriever; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; /** - *

- * This MappableAttributesRetriever implementation reads the list of defined J2EE - * roles from a web.xml file. It's functionality is based on the - * XmlMappableAttributesRetriever base class. - *

- * Example on how to configure this MappableAttributesRetriever in the Spring - * configuration file: - * - *

- *
- *
- * <bean id="j2eeMappableRolesRetriever" class="org.springframework.security.ui.preauth.j2ee.WebXmlMappableAttributesRetriever">
- *     <property name="webXmlInputStream"><bean factory-bean="webXmlResource" factory-method="getInputStream"/></property>
- * </bean>
- * <bean id="webXmlResource" class="org.springframework.web.context.support.ServletContextResource">
- *     <constructor-arg><ref local="servletContext"/></constructor-arg>
- *     <constructor-arg><value>/WEB-INF/web.xml</value></constructor-arg>
- * </bean>
- * <bean id="servletContext" class="org.springframework.web.context.support.ServletContextFactoryBean"/>
- *
- * 
+ * This MappableAttributesRetriever implementation reads the list of defined J2EE + * roles from a web.xml file and returns these from {{@link #getMappableAttributes()}. * * @author Ruud Senden + * @author Luke Taylor * @since 2.0 */ -public class WebXmlMappableAttributesRetriever extends XmlMappableAttributesRetriever { - private static final String XPATH_EXPR = "/web-app/security-role/role-name/text()"; +public class WebXmlMappableAttributesRetriever implements ResourceLoaderAware, MappableAttributesRetriever, InitializingBean { + protected final Log logger = LogFactory.getLog(getClass()); - /** - * Constructor setting the XPath expression to use - */ - public WebXmlMappableAttributesRetriever() { - super.setXpathExpression(XPATH_EXPR); + private ResourceLoader resourceLoader; + private Set mappableAttributes; + + public void setResourceLoader(ResourceLoader resourceLoader) { + this.resourceLoader = resourceLoader; + } + + + public Set getMappableAttributes() { + return mappableAttributes; } /** - * @param anInputStream - * The InputStream to read the XML data from + * Loads the web.xml file using the configured ResourceLoader and + * parses the role-name elements from it, using these as the set of mappableAttributes. */ - public void setWebXmlInputStream(InputStream anInputStream) { - super.setXmlInputStream(anInputStream); + + public void afterPropertiesSet() throws Exception { + Resource webXml = resourceLoader.getResource("/WEB-INF/web.xml"); + Document doc = getDocument(webXml.getInputStream()); + NodeList webApp = doc.getElementsByTagName("web-app"); + if (webApp.getLength() != 1) { + throw new IllegalArgumentException("Failed to find 'web-app' element in resource" + webXml); + } + NodeList securityRoles = ((Element)webApp.item(0)).getElementsByTagName("security-role"); + + ArrayList roleNames = new ArrayList(); + + for (int i=0; i < securityRoles.getLength(); i++) { + Element secRoleElt = (Element) securityRoles.item(i); + NodeList roles = secRoleElt.getElementsByTagName("role-name"); + + if (roles.getLength() > 0) { + String roleName = ((Element)roles.item(0)).getTextContent().trim(); + roleNames.add(roleName); + logger.info("Retrieved role-name '" + roleName + "' from web.xml"); + } else { + logger.info("No security-role elements found in " + webXml); + } + } + + mappableAttributes = Collections.unmodifiableSet(new HashSet(roleNames)); + } + + /** + * @return Document for the specified InputStream + */ + private Document getDocument(InputStream aStream) { + Document doc; + try { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + factory.setValidating(false); + DocumentBuilder db = factory.newDocumentBuilder(); + db.setEntityResolver(new MyEntityResolver()); + doc = db.parse(aStream); + return doc; + } catch (FactoryConfigurationError e) { + throw new RuntimeException("Unable to parse document object", e); + } catch (ParserConfigurationException e) { + throw new RuntimeException("Unable to parse document object", e); + } catch (SAXException e) { + throw new RuntimeException("Unable to parse document object", e); + } catch (IOException e) { + throw new RuntimeException("Unable to parse document object", e); + } finally { + try { + aStream.close(); + } catch (IOException e) { + logger.warn("Failed to close input stream for web.xml", e); + } + } + } + + /** + * We do not need to resolve external entities, so just return an empty + * String. + */ + private static final class MyEntityResolver implements EntityResolver { + public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { + return new InputSource(new StringReader("")); + } } } diff --git a/web/src/test/java/org/springframework/security/web/authentication/preauth/j2ee/WebXmlJ2eeDefinedRolesRetrieverTests.java b/web/src/test/java/org/springframework/security/web/authentication/preauth/j2ee/WebXmlJ2eeDefinedRolesRetrieverTests.java index 0f52464461..ee661dd4c5 100755 --- a/web/src/test/java/org/springframework/security/web/authentication/preauth/j2ee/WebXmlJ2eeDefinedRolesRetrieverTests.java +++ b/web/src/test/java/org/springframework/security/web/authentication/preauth/j2ee/WebXmlJ2eeDefinedRolesRetrieverTests.java @@ -1,21 +1,34 @@ package org.springframework.security.web.authentication.preauth.j2ee; -import java.io.InputStream; +import static org.junit.Assert.*; + import java.util.Arrays; import java.util.List; import java.util.Set; -import org.springframework.security.web.authentication.preauth.j2ee.WebXmlMappableAttributesRetriever; +import org.junit.Test; +import org.springframework.core.io.ClassPathResource; +import org.springframework.core.io.Resource; +import org.springframework.core.io.ResourceLoader; -import junit.framework.TestCase; +public class WebXmlJ2eeDefinedRolesRetrieverTests { -public class WebXmlJ2eeDefinedRolesRetrieverTests extends TestCase { - - public final void testRole1To4Roles() throws Exception { - final List ROLE1TO4_EXPECTED_ROLES = Arrays.asList(new String[] { "Role1", "Role2", "Role3", "Role4" }); - InputStream role1to4InputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("webxml/Role1-4.web.xml"); + @Test + public void testRole1To4Roles() throws Exception { + List ROLE1TO4_EXPECTED_ROLES = Arrays.asList(new String[] { "Role1", "Role2", "Role3", "Role4" }); + final Resource webXml = new ClassPathResource("webxml/Role1-4.web.xml"); WebXmlMappableAttributesRetriever rolesRetriever = new WebXmlMappableAttributesRetriever(); - rolesRetriever.setWebXmlInputStream(role1to4InputStream); + + rolesRetriever.setResourceLoader(new ResourceLoader() { + public ClassLoader getClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + + public Resource getResource(String location) { + return webXml; + } + }); + rolesRetriever.afterPropertiesSet(); Set j2eeRoles = rolesRetriever.getMappableAttributes(); assertNotNull(j2eeRoles); @@ -25,10 +38,19 @@ public class WebXmlJ2eeDefinedRolesRetrieverTests extends TestCase { j2eeRoles.containsAll(ROLE1TO4_EXPECTED_ROLES)); } - public final void testGetZeroJ2eeRoles() throws Exception { - InputStream noRolesInputStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("webxml/NoRoles.web.xml"); + @Test + public void testGetZeroJ2eeRoles() throws Exception { + final Resource webXml = new ClassPathResource("webxml/NoRoles.web.xml"); WebXmlMappableAttributesRetriever rolesRetriever = new WebXmlMappableAttributesRetriever(); - rolesRetriever.setWebXmlInputStream(noRolesInputStream); + rolesRetriever.setResourceLoader(new ResourceLoader() { + public ClassLoader getClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + + public Resource getResource(String location) { + return webXml; + } + }); rolesRetriever.afterPropertiesSet(); Set j2eeRoles = rolesRetriever.getMappableAttributes(); assertEquals("J2eeRoles expected size: 0, actual size: " + j2eeRoles.size(), 0, j2eeRoles.size()); diff --git a/web/template.mf b/web/template.mf index 8df9f48953..49e35008c3 100644 --- a/web/template.mf +++ b/web/template.mf @@ -24,6 +24,7 @@ Import-Template: org.springframework.beans.*;version="[3.0.0, 3.1.0)", org.springframework.context.*;version="[3.0.0, 3.1.0)", org.springframework.core;version="[3.0.0, 3.1.0)", + org.springframework.core.io;version="[3.0.0, 3.1.0)", org.springframework.dao;version="[3.0.0, 3.1.0)";resolution:=optional, org.springframework.expression;version="[3.0.0, 3.1.0)";resolution:=optional, org.springframework.expression.spel.*;version="[3.0.0, 3.1.0)";resolution:=optional, @@ -31,4 +32,7 @@ Import-Template: org.springframework.mock.web;version="[3.0.0, 3.1.0)";resolution:=optional, org.springframework.web.context.*;version="[3.0.0, 3.1.0)";resolution:=optional, org.springframework.util;version="[3.0.0, 3.1.0)";resolution:=optional, - javax.servlet.*;version="0" \ No newline at end of file + org.w3c.dom;version="0";resolution:=optional, + org.xml.sax;version="0";resolution:=optional, + javax.servlet.*;version="0", + javax.xml.parsers.*;version="0";resolution:=optional \ No newline at end of file