SEC-1094: Simplified WebXml attribute mapping. Removed generic jaxen-based implementation on which it was based in favour of simple DOM model traversal. Updated sample.
This commit is contained in:
parent
aa511bb1f4
commit
5808da12ff
10
core/pom.xml
10
core/pom.xml
|
@ -13,7 +13,7 @@
|
|||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-expression</artifactId>
|
||||
</dependency>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-core</artifactId>
|
||||
|
@ -81,12 +81,6 @@
|
|||
<artifactId>hsqldb</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jaxen</groupId>
|
||||
<artifactId>jaxen</artifactId>
|
||||
<version>1.1.1</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat</groupId>
|
||||
<artifactId>annotations-api</artifactId>
|
||||
|
@ -98,7 +92,7 @@
|
|||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
|
|
@ -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 = "<roles><role>Role1</role><role>Role2</role></roles>";
|
||||
|
||||
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<String> 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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
@ -187,6 +187,11 @@ class="org.springframework.security.web.authentication.preauth.PreAuthenticatedA
|
|||
<literal>userPrincipal</literal> property of the <interfacename>HttpServletRequest</interfacename>. use of this
|
||||
filter would usually be combined with the use of J2EE roles as described above in <xref linkend="j2ee-preauth-details"/>.
|
||||
</para>
|
||||
<para>
|
||||
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 <filename>samples/preauth</filename>
|
||||
directory.
|
||||
</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
|
|
@ -9,8 +9,8 @@
|
|||
<beans xmlns="http://www.springframework.org/schema/beans"
|
||||
xmlns:sec="http://www.springframework.org/schema/security"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
|
||||
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.1.xsd">
|
||||
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">
|
||||
|
||||
<bean id="filterChainProxy" class="org.springframework.security.web.FilterChainProxy">
|
||||
<sec:filter-chain-map path-type="ant">
|
||||
|
@ -32,7 +32,18 @@
|
|||
|
||||
<bean id="j2eePreAuthFilter" class="org.springframework.security.web.authentication.preauth.j2ee.J2eePreAuthenticatedProcessingFilter">
|
||||
<property name="authenticationManager" ref="authenticationManager"/>
|
||||
<property name="authenticationDetailsSource" ref="authenticationDetailsSource"/>
|
||||
<property name="authenticationDetailsSource">
|
||||
<bean class="org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource">
|
||||
<property name="mappableRolesRetriever">
|
||||
<bean class="org.springframework.security.web.authentication.preauth.j2ee.WebXmlMappableAttributesRetriever" />
|
||||
</property>
|
||||
<property name="userRoles2GrantedAuthoritiesMapper">
|
||||
<bean class="org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper">
|
||||
<property name="convertAttributeToUpperCase" value="true"/>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="preAuthenticatedProcessingFilterEntryPoint"
|
||||
|
@ -47,26 +58,6 @@
|
|||
</constructor-arg>
|
||||
</bean>
|
||||
|
||||
<bean id="authenticationDetailsSource" class="org.springframework.security.web.authentication.preauth.j2ee.J2eeBasedPreAuthenticatedWebAuthenticationDetailsSource">
|
||||
<property name="mappableRolesRetriever" ref="j2eeMappableRolesRetriever"/>
|
||||
<property name="userRoles2GrantedAuthoritiesMapper" ref="j2eeUserRoles2GrantedAuthoritiesMapper"/>
|
||||
</bean>
|
||||
|
||||
<bean id="j2eeUserRoles2GrantedAuthoritiesMapper" class="org.springframework.security.core.authority.mapping.SimpleAttributes2GrantedAuthoritiesMapper">
|
||||
<property name="convertAttributeToUpperCase" value="true"/>
|
||||
</bean>
|
||||
|
||||
<bean id="j2eeMappableRolesRetriever" class="org.springframework.security.web.authentication.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="servletContext"/>
|
||||
<constructor-arg value="/WEB-INF/web.xml"/>
|
||||
</bean>
|
||||
|
||||
<bean id="servletContext" class="org.springframework.web.context.support.ServletContextFactoryBean"/>
|
||||
|
||||
<bean id="etf" class="org.springframework.security.web.access.ExceptionTranslationFilter">
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* 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.
|
||||
* <p>
|
||||
* Example on how to configure this MappableAttributesRetriever in the Spring
|
||||
* configuration file:
|
||||
*
|
||||
* <pre>
|
||||
*
|
||||
*
|
||||
* <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"/>
|
||||
*
|
||||
* </pre>
|
||||
* This <tt>MappableAttributesRetriever</tt> implementation reads the list of defined J2EE
|
||||
* roles from a <tt>web.xml</tt> 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<String> mappableAttributes;
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourceLoader = resourceLoader;
|
||||
}
|
||||
|
||||
|
||||
public Set<String> getMappableAttributes() {
|
||||
return mappableAttributes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param anInputStream
|
||||
* The InputStream to read the XML data from
|
||||
* Loads the web.xml file using the configured <tt>ResourceLoader</tt> and
|
||||
* parses the role-name elements from it, using these as the set of <tt>mappableAttributes</tt>.
|
||||
*/
|
||||
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<String> roleNames = new ArrayList<String>();
|
||||
|
||||
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<String>(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(""));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<String> 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<String> 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<String> 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<String> j2eeRoles = rolesRetriever.getMappableAttributes();
|
||||
assertEquals("J2eeRoles expected size: 0, actual size: " + j2eeRoles.size(), 0, j2eeRoles.size());
|
||||
|
|
|
@ -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"
|
||||
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
|
Loading…
Reference in New Issue