org.springframework.security.web.authentication.preauth.j2ee.WebXmlMappableAttributesRetriever Maven / Gradle / Ivy
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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.List;
import java.util.Set;
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.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;
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.springframework.util.Assert;
/**
* 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
implements ResourceLoaderAware, MappableAttributesRetriever, InitializingBean {
protected final Log logger = LogFactory.getLog(getClass());
private ResourceLoader resourceLoader;
private Set mappableAttributes;
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
@Override
public Set getMappableAttributes() {
return this.mappableAttributes;
}
/**
* Loads the web.xml file using the configured ResourceLoader and parses the
* role-name elements from it, using these as the set of mappableAttributes.
*/
@Override
public void afterPropertiesSet() throws Exception {
Resource webXml = this.resourceLoader.getResource("/WEB-INF/web.xml");
Document doc = getDocument(webXml.getInputStream());
NodeList webApp = doc.getElementsByTagName("web-app");
Assert.isTrue(webApp.getLength() == 1, () -> "Failed to find 'web-app' element in resource" + webXml);
NodeList securityRoles = ((Element) webApp.item(0)).getElementsByTagName("security-role");
List roleNames = getRoleNames(webXml, securityRoles);
this.mappableAttributes = Collections.unmodifiableSet(new HashSet<>(roleNames));
}
private List getRoleNames(Resource webXml, NodeList securityRoles) {
ArrayList roleNames = new ArrayList<>();
for (int i = 0; i < securityRoles.getLength(); i++) {
Element securityRoleElement = (Element) securityRoles.item(i);
NodeList roles = securityRoleElement.getElementsByTagName("role-name");
if (roles.getLength() > 0) {
String roleName = roles.item(0).getTextContent().trim();
roleNames.add(roleName);
this.logger.info("Retrieved role-name '" + roleName + "' from web.xml");
}
else {
this.logger.info("No security-role elements found in " + webXml);
}
}
return roleNames;
}
/**
* @return Document for the specified InputStream
*/
private Document getDocument(InputStream aStream) {
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(new MyEntityResolver());
return builder.parse(aStream);
}
catch (FactoryConfigurationError | IOException | SAXException | ParserConfigurationException ex) {
throw new RuntimeException("Unable to parse document object", ex);
}
finally {
try {
aStream.close();
}
catch (IOException ex) {
this.logger.warn("Failed to close input stream for web.xml", ex);
}
}
}
/**
* We do not need to resolve external entities, so just return an empty String.
*/
private static final class MyEntityResolver implements EntityResolver {
@Override
public InputSource resolveEntity(String publicId, String systemId) {
return new InputSource(new StringReader(""));
}
}
}