org.jasig.springframework.security.portlet.authentication.PortletXmlMappableAttributesRetriever.java Source code

Java tutorial

Introduction

Here is the source code for org.jasig.springframework.security.portlet.authentication.PortletXmlMappableAttributesRetriever.java

Source

/**
 * Licensed to Jasig under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Jasig licenses this file to you 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:
 *
 * http://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.jasig.springframework.security.portlet.authentication;

import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import javax.portlet.PortletConfig;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.FactoryConfigurationError;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathVariableResolver;

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.springframework.security.web.authentication.preauth.j2ee.WebXmlMappableAttributesRetriever;
import org.springframework.web.portlet.context.PortletConfigAware;
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 <tt>MappableAttributesRetriever</tt> implementation reads the list of defined Portlet
 * roles from a <tt>portlet.xml</tt> file and returns these from {{@link #getMappableAttributes()}.
 * <p>If configured in a portlet application level context then all security-role-refs from all
 * portlets are merged into a single list. If configured in a portlet level context then only
 * the security-role-refs from that portlet are used in the list.
 *
 * @author Ruud Senden
 * @author Luke Taylor
 * @author Eric Dalquist
 * @since 2.0
 * @see WebXmlMappableAttributesRetriever
 */
public class PortletXmlMappableAttributesRetriever
        implements ResourceLoaderAware, PortletConfigAware, MappableAttributesRetriever, InitializingBean {
    protected final Log logger = LogFactory.getLog(getClass());

    private ResourceLoader resourceLoader;
    private PortletConfig portletConfig;
    private Set<String> mappableAttributes;

    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    @Override
    public void setPortletConfig(PortletConfig portletConfig) {
        this.portletConfig = portletConfig;
    }

    public Set<String> getMappableAttributes() {
        return mappableAttributes;
    }

    /**
     * Loads the portlet.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 afterPropertiesSet() throws Exception {
        Resource portletXml = resourceLoader.getResource("/WEB-INF/portlet.xml");
        Document doc = getDocument(portletXml.getInputStream());

        final XPathExpression roleNamesExpression;
        if (portletConfig == null) {
            final XPathFactory xPathFactory = XPathFactory.newInstance();

            final XPath xPath = xPathFactory.newXPath();
            roleNamesExpression = xPath.compile("/portlet-app/portlet/security-role-ref/role-name");
        } else {
            final XPathFactory xPathFactory = XPathFactory.newInstance();
            xPathFactory.setXPathVariableResolver(new XPathVariableResolver() {
                @Override
                public Object resolveVariable(QName variableName) {
                    if ("portletName".equals(variableName.getLocalPart())) {
                        return portletConfig.getPortletName();
                    }

                    return null;
                }
            });
            final XPath xPath = xPathFactory.newXPath();
            roleNamesExpression = xPath
                    .compile("/portlet-app/portlet[portlet-name=$portletName]/security-role-ref/role-name");
        }

        final NodeList securityRoles = (NodeList) roleNamesExpression.evaluate(doc, XPathConstants.NODESET);
        final Set<String> roleNames = new HashSet<String>();

        for (int i = 0; i < securityRoles.getLength(); i++) {
            Element secRoleElt = (Element) securityRoles.item(i);
            String roleName = secRoleElt.getTextContent().trim();
            roleNames.add(roleName);
            logger.info("Retrieved role-name '" + roleName + "' from portlet.xml");
        }

        if (roleNames.isEmpty()) {
            logger.info("No security-role-ref elements found in " + portletXml
                    + (portletConfig == null ? "" : " for portlet " + portletConfig.getPortletName()));
        }

        mappableAttributes = Collections.unmodifiableSet(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 portlet.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(""));
        }
    }
}