422 lines
15 KiB
Java
422 lines
15 KiB
Java
|
|
/*
|
|
* @(#)AttributeSelector.java
|
|
*
|
|
* Copyright 2003-2005 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.attr;
|
|
|
|
import com.sun.xacml.EvaluationCtx;
|
|
import com.sun.xacml.Indenter;
|
|
import com.sun.xacml.ParsingException;
|
|
import com.sun.xacml.PolicyMetaData;
|
|
|
|
import com.sun.xacml.cond.Evaluatable;
|
|
import com.sun.xacml.cond.EvaluationResult;
|
|
import com.sun.xacml.cond.Expression;
|
|
|
|
import com.sun.xacml.ctx.Status;
|
|
import com.sun.xacml.debug.RuntimeInfo;
|
|
import com.sun.xacml.debug.RuntimeInfo.ELEMENT_TYPE;
|
|
|
|
import java.io.OutputStream;
|
|
import java.io.PrintStream;
|
|
import java.io.UnsupportedEncodingException;
|
|
|
|
import java.net.URI;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
import org.apache.log4j.Logger;
|
|
|
|
import org.w3c.dom.NamedNodeMap;
|
|
import org.w3c.dom.Node;
|
|
|
|
|
|
/**
|
|
* Supports the standard selector functionality in XACML, which uses XPath
|
|
* expressions to resolve values from the Request or elsewhere. All selector
|
|
* queries are done by <code>AttributeFinderModule</code>s so that it's easy
|
|
* to plugin different XPath implementations.
|
|
*
|
|
* @since 1.0
|
|
* @author Seth Proctor
|
|
*/
|
|
public class AttributeSelector implements Evaluatable
|
|
{
|
|
|
|
// the data type returned by this selector
|
|
private URI type;
|
|
|
|
// the XPath to search
|
|
private String contextPath;
|
|
|
|
// must resolution find something
|
|
private boolean mustBePresent;
|
|
|
|
// the xpath version we've been told to use
|
|
private String xpathVersion;
|
|
|
|
// the policy root, where we get namespace mapping details
|
|
private Node policyRoot;
|
|
|
|
private RuntimeInfo src;
|
|
|
|
// the logger we'll use for all messages
|
|
private static final Logger logger =
|
|
Logger.getLogger(AttributeSelector.class.getName());
|
|
|
|
/**
|
|
* Creates a new <code>AttributeSelector</code> with no policy root.
|
|
*
|
|
* @param type the data type of the attribute values this selector
|
|
* looks for
|
|
* @param contextPath the XPath to query
|
|
* @param mustBePresent must resolution find a match
|
|
* @param xpathVersion the XPath version to use, which must be a valid
|
|
* XPath version string (the identifier for XPath 1.0
|
|
* is provided in <code>PolicyMetaData</code>)
|
|
*/
|
|
public AttributeSelector(URI type, String contextPath,
|
|
boolean mustBePresent, String xpathVersion) {
|
|
this(type, contextPath, null, mustBePresent, xpathVersion);
|
|
}
|
|
|
|
/**
|
|
* Creates a new <code>AttributeSelector</code>.
|
|
*
|
|
* @param type the data type of the attribute values this selector
|
|
* looks for
|
|
* @param contextPath the XPath to query
|
|
* @param policyRoot the root DOM Element for the policy containing this
|
|
* selector, which defines namespace mappings
|
|
* @param mustBePresent must resolution find a match
|
|
* @param xpathVersion the XPath version to use, which must be a valid
|
|
* XPath version string (the identifier for XPath 1.0
|
|
* is provided in <code>PolicyMetaData</code>)
|
|
*/
|
|
public AttributeSelector(URI type, String contextPath, Node policyRoot,
|
|
boolean mustBePresent, String xpathVersion) {
|
|
this.type = type;
|
|
this.contextPath = contextPath;
|
|
this.mustBePresent = mustBePresent;
|
|
this.xpathVersion = xpathVersion;
|
|
this.policyRoot = policyRoot;
|
|
}
|
|
|
|
/**
|
|
* Creates a new <code>AttributeSelector</code> based on the DOM root
|
|
* of the XML type. Note that as of XACML 1.1 the XPathVersion element
|
|
* is required in any policy that uses a selector, so if the
|
|
* <code>xpathVersion</code> string is null, then this will throw
|
|
* an exception.
|
|
*
|
|
* @param root the root of the DOM tree for the XML AttributeSelectorType
|
|
* XML type
|
|
* @param metaData the meta-data associated with the containing policy
|
|
*
|
|
* @return an <code>AttributeSelector</code>
|
|
*
|
|
* @throws ParsingException if the AttributeSelectorType was invalid
|
|
*/
|
|
public static AttributeSelector getInstance(Node root,
|
|
PolicyMetaData metaData)
|
|
throws ParsingException {
|
|
RuntimeInfo src = RuntimeInfo.getRuntimeInfo(root, ELEMENT_TYPE.ATTRIBUTE_SELECTOR);
|
|
if (root.getNodeType() != Node.ELEMENT_NODE ||
|
|
!root.getLocalName().equals("AttributeSelector")) {
|
|
throw new ParsingException("Can't create an AttributeSelector"
|
|
+ " from a " + root.getLocalName() + " element"
|
|
+ (src != null ? src.getLocationMsgForError() : ""));
|
|
}
|
|
URI type = null;
|
|
String contextPath = null;
|
|
boolean mustBePresent = false;
|
|
String xpathVersion = metaData.getXPathIdentifier();
|
|
|
|
// make sure we were given an xpath version
|
|
if (xpathVersion == null) {
|
|
throw new ParsingException("An XPathVersion is required for "+
|
|
"any policies that use selectors"
|
|
+ (src != null ? src.getLocationMsgForError() : ""));
|
|
}
|
|
NamedNodeMap attrs = root.getAttributes();
|
|
|
|
try {
|
|
// there's always a DataType attribute
|
|
type = new URI(attrs.getNamedItem("DataType").getNodeValue());
|
|
} catch (Exception e) {
|
|
throw new ParsingException("Error parsing required DataType " +
|
|
"attribute in AttributeSelector"
|
|
+ (src != null ? src.getLocationMsgForError() : ""), e);
|
|
}
|
|
|
|
try {
|
|
// there's always a RequestPath
|
|
contextPath =
|
|
attrs.getNamedItem("RequestContextPath").getNodeValue();
|
|
} catch (Exception e) {
|
|
throw new ParsingException("Error parsing required " +
|
|
"RequestContextPath attribute in AttributeSelector"
|
|
+ (src != null ? src.getLocationMsgForError() : ""), e);
|
|
}
|
|
|
|
try {
|
|
// there may optionally be a MustBePresent
|
|
Node node = attrs.getNamedItem("MustBePresent");
|
|
if (node != null)
|
|
if (node.getNodeValue().equals("true")) {
|
|
mustBePresent = true;
|
|
}
|
|
} catch (Exception e) {
|
|
// this shouldn't happen, since we check the cases, but still...
|
|
throw new ParsingException("Error parsing optional attributes "
|
|
+ "in AttributeSelector"
|
|
+ (src != null ? src.getLocationMsgForError() : ""), e);
|
|
}
|
|
|
|
// as of 1.2 we need the root element of the policy so we can get
|
|
// the namespace mapping, but in order to leave the APIs unchanged,
|
|
// we'll walk up the tree to find the root rather than pass this
|
|
// element around through all the code
|
|
Node policyRoot = null;
|
|
Node node = root.getParentNode();
|
|
|
|
while ((node != null) && (node.getNodeType() == Node.ELEMENT_NODE)) {
|
|
policyRoot = node;
|
|
node = node.getParentNode();
|
|
}
|
|
|
|
// create the new selector
|
|
AttributeSelector attrSelector = new AttributeSelector(type,
|
|
contextPath, policyRoot, mustBePresent, xpathVersion);
|
|
if ( src != null ) {
|
|
attrSelector.src = src;
|
|
src.setXACMLObject(attrSelector);
|
|
}
|
|
return attrSelector;
|
|
}
|
|
|
|
/**
|
|
* Returns the data type of the attribute values that this selector
|
|
* will resolve
|
|
*
|
|
* @return the data type of the values found by this selector
|
|
*/
|
|
public URI getType() {
|
|
return this.type;
|
|
}
|
|
|
|
/**
|
|
* Returns the XPath query used to resolve attribute values.
|
|
*
|
|
* @return the XPath query
|
|
*/
|
|
public String getContextPath() {
|
|
return this.contextPath;
|
|
}
|
|
|
|
/**
|
|
* Returns whether or not a value is required to be resolved by this
|
|
* selector.
|
|
*
|
|
* @return true if a value is required, false otherwise
|
|
*/
|
|
public boolean mustBePresent() {
|
|
return this.mustBePresent;
|
|
}
|
|
|
|
/**
|
|
* Always returns true, since a selector always returns a bag of
|
|
* attribute values.
|
|
*
|
|
* @return true
|
|
*/
|
|
public boolean returnsBag() {
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Always returns an empty list since selectors never have children.
|
|
*
|
|
* @return an empty <code>List</code>
|
|
*/
|
|
public List<Expression> getChildren() {
|
|
return Expression.EMPTY_LIST;
|
|
}
|
|
|
|
/**
|
|
* Returns the XPath version this selector is supposed to use. This is
|
|
* typically provided by the defaults section of the policy containing
|
|
* this selector.
|
|
*
|
|
* @return the XPath version
|
|
*/
|
|
public String getXPathVersion() {
|
|
return this.xpathVersion;
|
|
}
|
|
|
|
/**
|
|
* Returns the policy root node, from where we get namespace
|
|
* mapping details.
|
|
*
|
|
* @return a <code>Node</code> object
|
|
*/
|
|
public Node getPolicyRoot() {
|
|
return this.policyRoot;
|
|
}
|
|
|
|
public RuntimeInfo getRuntimeInfo() {
|
|
return src;
|
|
}
|
|
|
|
|
|
/**
|
|
* Invokes the <code>AttributeFinder</code> used by the given
|
|
* <code>EvaluationCtx</code> to try to resolve an attribute value. If
|
|
* the selector is defined with MustBePresent as true, then failure
|
|
* to find a matching value will result in Indeterminate, otherwise it
|
|
* will result in an empty bag. To support the basic selector
|
|
* functionality defined in the XACML specification, use a finder that
|
|
* has only the <code>SelectorModule</code> as a module that supports
|
|
* selector finding.
|
|
*
|
|
* @param context representation of the request to search
|
|
*
|
|
* @return a result containing a bag either empty because no values were
|
|
* found or containing at least one value, or status associated with an
|
|
* Indeterminate result
|
|
*/
|
|
public EvaluationResult evaluate(EvaluationCtx context) {
|
|
context.newEvent(this);
|
|
// query the context
|
|
EvaluationResult result = context.getAttribute(this.contextPath,
|
|
this.policyRoot,
|
|
this.type,
|
|
this.xpathVersion);
|
|
|
|
// see if we got anything
|
|
if (! result.indeterminate()) {
|
|
BagAttribute bag = (BagAttribute)(result.getAttributeValue());
|
|
|
|
// see if it's an empty bag
|
|
if (bag.isEmpty()) {
|
|
// see if this is an error or not
|
|
if (this.mustBePresent) {
|
|
// this is an error
|
|
// if (logger.isLoggable(Level.INFO)) {
|
|
logger.info("AttributeSelector failed to resolve a " +
|
|
"value for a required attribute: " +
|
|
this.contextPath);
|
|
// }
|
|
ArrayList<String> code = new ArrayList<String>();
|
|
code.add(Status.STATUS_MISSING_ATTRIBUTE);
|
|
String message = "couldn't resolve XPath expression " +
|
|
this.contextPath + " for type " + this.type.toString();
|
|
EvaluationResult evaluationResult
|
|
= new EvaluationResult(new Status(code, message));
|
|
context.closeCurrentEvent(evaluationResult);
|
|
return evaluationResult;
|
|
}
|
|
// return the empty bag
|
|
context.closeCurrentEvent();
|
|
return result;
|
|
}
|
|
// return the values
|
|
context.closeCurrentEvent(result);
|
|
return result;
|
|
}
|
|
// return the error
|
|
context.closeCurrentEvent(result);
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Encodes this selector into its XML representation and
|
|
* writes this encoding to the given <code>OutputStream</code> with no
|
|
* indentation.
|
|
*
|
|
* @param output a stream into which the XML-encoded data is written
|
|
* @param charsetName the character set to use in encoding of strings.
|
|
* This may be null in which case the platform
|
|
* default character set will be used.
|
|
*
|
|
* @throws UnsupportedEncodingException
|
|
*/
|
|
public void encode(OutputStream output, String charsetName)
|
|
throws UnsupportedEncodingException {
|
|
encode(output, charsetName, new Indenter(0));
|
|
}
|
|
|
|
/**
|
|
* Encodes this selector into its XML representation and
|
|
* writes this encoding to the given <code>OutputStream</code> with
|
|
* indentation.
|
|
*
|
|
* @param output a stream into which the XML-encoded data is written
|
|
* @param charsetName the character set to use in encoding of strings.
|
|
* This may be null in which case the platform
|
|
* default character set will be used.
|
|
* @param indenter an object that creates indentation strings
|
|
*
|
|
* @throws UnsupportedEncodingException
|
|
*/
|
|
public void encode(OutputStream output, String charsetName,
|
|
Indenter indenter)
|
|
throws UnsupportedEncodingException {
|
|
PrintStream out;
|
|
if(charsetName == null) {
|
|
out = new PrintStream(output);
|
|
} else {
|
|
out = new PrintStream(output, false, charsetName);
|
|
}
|
|
String indent = indenter.makeString();
|
|
|
|
String tag = "<AttributeSelector RequestContextPath=\""
|
|
+ this.contextPath + "\" DataType=\""
|
|
+ this.type.toString() + "\"";
|
|
|
|
if (this.mustBePresent) {
|
|
tag += " MustBePresent=\"true\"";
|
|
}
|
|
|
|
tag += "/>";
|
|
|
|
out.println(indent + tag);
|
|
}
|
|
|
|
|
|
|
|
}
|