1
0
mirror of synced 2026-08-05 17:57:15 +00:00

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:
Luke Taylor
2009-06-08 15:23:41 +00:00
parent aa511bb1f4
commit 5808da12ff
9 changed files with 167 additions and 183 deletions
@@ -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>
*
*
* &lt;bean id=&quot;j2eeMappableRolesRetriever&quot; class=&quot;org.springframework.security.ui.preauth.j2ee.WebXmlMappableAttributesRetriever&quot;&gt;
* &lt;property name=&quot;webXmlInputStream&quot;&gt;&lt;bean factory-bean=&quot;webXmlResource&quot; factory-method=&quot;getInputStream&quot;/&gt;&lt;/property&gt;
* &lt;/bean&gt;
* &lt;bean id=&quot;webXmlResource&quot; class=&quot;org.springframework.web.context.support.ServletContextResource&quot;&gt;
* &lt;constructor-arg&gt;&lt;ref local=&quot;servletContext&quot;/&gt;&lt;/constructor-arg&gt;
* &lt;constructor-arg&gt;&lt;value&gt;/WEB-INF/web.xml&lt;/value&gt;&lt;/constructor-arg&gt;
* &lt;/bean&gt;
* &lt;bean id=&quot;servletContext&quot; class=&quot;org.springframework.web.context.support.ServletContextFactoryBean&quot;/&gt;
*
* </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());