/* * @(#)SelectorModule.java * * Copyright 2003-2004 Sun Microsystems, Inc. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistribution of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistribution in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Sun Microsystems, Inc. or the names of contributors may * be used to endorse or promote products derived from this software without * specific prior written permission. * * This software is provided "AS IS," without a warranty of any kind. ALL * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE, * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. * * You acknowledge that this software is not designed or intended for use in * the design, construction, operation or maintenance of any nuclear facility. */ package com.sun.xacml.finder.impl; import java.net.URI; import java.util.ArrayList; import org.apache.xpath.XPathAPI; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.sun.xacml.EvaluationCtx; import com.sun.xacml.ParsingException; import com.sun.xacml.PolicyMetaData; import com.sun.xacml.UnknownIdentifierException; import com.sun.xacml.attr.AttributeFactory; import com.sun.xacml.attr.AttributeValue; import com.sun.xacml.attr.BagAttribute; import com.sun.xacml.cond.EvaluationResult; import com.sun.xacml.ctx.Status; import com.sun.xacml.finder.AttributeFinderModule; /** * This module implements the basic behavior of the AttributeSelectorType, looking for attribute * values in the physical request document using the given XPath expression. This is implemented as * a separate module (instead of being implemented directly in <code>AttributeSelector</code> so * that programmers can remove this functionality if they want (it's optional in the spec), so they * can replace this code with more efficient, specific code as needed, and so they can easily swap * in different XPath libraries. * <p> * Note that if no matches are found, this module will return an empty bag (unless some error * occurred). The <code>AttributeSelector</code> is still deciding what to return to the policy * based on the MustBePresent attribute. * <p> * This module uses the Xalan XPath implementation, and supports only version 1.0 of XPath. It is a * fully functional, correct implementation of XACML's AttributeSelector functionality, but is not * designed for environments that make significant use of XPath queries. Developers for any such * environment should consider implementing their own module. * * @since 1.0 * @author Seth Proctor * * Adding generic type support by Christian Mueller (geotools) */ public class SelectorModule extends AttributeFinderModule { /** * Returns true since this module supports retrieving attributes based on the data provided in * an AttributeSelectorType. * * @return true */ public boolean isSelectorSupported() { return true; } /** * Private helper to create a new processing error status result */ private EvaluationResult createProcessingError(String msg) { ArrayList<String> code = new ArrayList<String>(); code.add(Status.STATUS_PROCESSING_ERROR); return new EvaluationResult(new Status(code, msg)); } /** * Tries to find attribute values based on the given selector data. The result, if successful, * always contains a <code>BagAttribute</code>, even if only one value was found. If no values * were found, but no other error occurred, an empty bag is returned. * * @param path * the XPath expression to search against * @param namespaceNode * the DOM node defining namespace mappings to use, or null if mappings come from the * context root * @param type * the datatype of the attributes to find * @param context * the representation of the request data * @param xpathVersion * the XPath version to use * * @return the result of attribute retrieval, which will be a bag of attributes or an error */ public EvaluationResult findAttribute(String path, Node namespaceNode, URI type, EvaluationCtx context, String xpathVersion) { // we only support 1.0 if (!xpathVersion.equals(PolicyMetaData.XPATH_1_0_IDENTIFIER)) return new EvaluationResult(BagAttribute.createEmptyBag(type)); // get the DOM root of the request document Node root = context.getRequestRoot(); // if we were provided with a non-null namespace node, then use it // to resolve namespaces, otherwise use the context root node Node nsNode = (namespaceNode != null) ? namespaceNode : root; // setup the root path (pre-pended to the context path), which... String rootPath = ""; // ...only has content if the context path is relative if (path.charAt(0) != '/') { String rootName = root.getLocalName(); // see if the request root is in a namespace String namespace = root.getNamespaceURI(); if (namespace == null) { // no namespacing, so we're done rootPath = "/" + rootName + "/"; } else { // namespaces are used, so we need to lookup the correct // prefix to use in the search string NamedNodeMap nmap = namespaceNode.getAttributes(); rootPath = null; for (int i = 0; i < nmap.getLength(); i++) { Node n = nmap.item(i); if (n.getNodeValue().equals(namespace)) { // we found the matching namespace, so get the prefix // and then break out String name = n.getNodeName(); int pos = name.indexOf(':'); if (pos == -1) { // the namespace was the default namespace rootPath = "/"; } else { // we found a prefixed namespace rootPath = "/" + name.substring(pos + 1); } // finish off the string rootPath += ":" + rootName + "/"; break; } } // if the rootPath is still null, then we don't have any // definitions for the namespace if (rootPath == null) return createProcessingError("Failed to map a namespace" + " in an XPath expression"); } } // now do the query, pre-pending the root path to the context path NodeList matches = null; try { // NOTE: see comments in XALAN docs about why this is slow matches = XPathAPI.selectNodeList(root, rootPath + path, nsNode); } catch (Exception e) { // in the case of any exception, we need to return an error return createProcessingError("error in XPath: " + e.getMessage()); } if (matches.getLength() == 0) { // we didn't find anything, so we return an empty bag return new EvaluationResult(BagAttribute.createEmptyBag(type)); } // there was at least one match, so try to generate the values try { ArrayList<AttributeValue> list = new ArrayList<AttributeValue>(); AttributeFactory attrFactory = AttributeFactory.getInstance(); for (int i = 0; i < matches.getLength(); i++) { String text = null; Node node = matches.item(i); short nodeType = node.getNodeType(); // see if this is straight text, or a node with data under // it and then get the values accordingly if ((nodeType == Node.CDATA_SECTION_NODE) || (nodeType == Node.COMMENT_NODE) || (nodeType == Node.TEXT_NODE) || (nodeType == Node.ATTRIBUTE_NODE)) { // there is no child to this node text = node.getNodeValue(); } else { // the data is in a child node text = node.getFirstChild().getNodeValue(); } list.add(attrFactory.createValue(type, text)); } return new EvaluationResult(new BagAttribute(type, list)); } catch (ParsingException pe) { return createProcessingError(pe.getMessage()); } catch (UnknownIdentifierException uie) { return createProcessingError("unknown attribute type: " + type); } } }