All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.att.research.xacmlatt.pdp.policy.expressions.AttributeSelector Maven / Gradle / Ivy

The newest version!
/*
 *
 *          Copyright (c) 2013,2019, 2023  AT&T Knowledge Ventures
 *                     SPDX-License-Identifier: MIT
 */
package com.att.research.xacmlatt.pdp.policy.expressions;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.att.research.xacml.api.Attribute;
import com.att.research.xacml.api.AttributeValue;
import com.att.research.xacml.api.DataType;
import com.att.research.xacml.api.DataTypeException;
import com.att.research.xacml.api.DataTypeFactory;
import com.att.research.xacml.api.Identifier;
import com.att.research.xacml.api.Request;
import com.att.research.xacml.api.RequestAttributes;
import com.att.research.xacml.api.StatusCode;
import com.att.research.xacml.std.StdStatus;
import com.att.research.xacml.std.StdStatusCode;
import com.att.research.xacml.std.datatypes.DataTypes;
import com.att.research.xacml.std.datatypes.NodeNamespaceContext;
import com.att.research.xacml.std.datatypes.XPathExpressionWrapper;
import com.att.research.xacml.std.dom.DOMStructureException;
import com.att.research.xacml.std.dom.DOMUtil;
import com.att.research.xacml.util.FactoryException;
import com.att.research.xacmlatt.pdp.eval.EvaluationContext;
import com.att.research.xacmlatt.pdp.eval.EvaluationException;
import com.att.research.xacmlatt.pdp.policy.Bag;
import com.att.research.xacmlatt.pdp.policy.ExpressionResult;
import com.att.research.xacmlatt.pdp.policy.PolicyDefaults;

/**
 * AttributeSelector extends {@link com.att.research.xacmlatt.pdp.policy.expressions.AttributeRetrievalBase} to implement
 * the XACML AttributeSelector element.
 * 
 * @author car
 * @version $Revision: 1.2 $
 */
public class AttributeSelector extends AttributeRetrievalBase {
	private Identifier 		contextSelectorId;
	private String			path;
	@SuppressWarnings("unused")
	private DataType 	dataType;
	
	protected DataType getDataType() {
		Identifier dataTypeIdThis	= this.getDataTypeId();
		if (dataTypeIdThis == null) {
			return null;
		} else {
			DataTypeFactory dataTypeFactory		= null;
			try {
				dataTypeFactory	= DataTypeFactory.newInstance();
				if (dataTypeFactory == null) {
					return null;
				}
			} catch (FactoryException ex) {
				return null;
			}
			return (this.dataType	= dataTypeFactory.getDataType(dataTypeIdThis));
		}
	}

	public AttributeSelector(StatusCode statusCodeIn, String statusMessageIn) {
		super(statusCodeIn, statusMessageIn);
	}

	public AttributeSelector(StatusCode statusCodeIn) {
		super(statusCodeIn);
	}

	public AttributeSelector() {
	}
	
	public Identifier getContextSelectorId() {
		return this.contextSelectorId;
	}
	
	public void setContextSelectorId(Identifier identifier) {
		this.contextSelectorId	= identifier;
	}
	
	public String getPath() {
		return this.path;
	}
	
	public void setPath(String pathIn) {
		this.path	= pathIn;
	}
	
	@Override
	protected boolean validateComponent() {
		if (!super.validateComponent()) {
			return false;
		} else if (this.getPath() == null) {
			this.setStatus(StdStatusCode.STATUS_CODE_SYNTAX_ERROR, "Missing Path");
			return false;
		} else {
			return true;
		}
	}
	
	/**
	 * If there is a context selector ID, get the attributes from the given RequestAttributes with that
	 * ID, ensure they are XPathExpressions and return them.
	 * 
	 * @param requestAttributes RequestAttributes
	 * @return List of XPathExpression objects
	 */
	protected List getContextSelectorValues(RequestAttributes requestAttributes) {
		Identifier thisContextSelectorId	= this.getContextSelectorId();
		if (thisContextSelectorId == null) {
			return null;
		}
		List listXPathExpressions	= null;
		Iterator iterAttributes	= requestAttributes.getAttributes(thisContextSelectorId);
		if (iterAttributes != null) {
			while (iterAttributes.hasNext()) {
				Attribute attribute	= iterAttributes.next();
				Iterator> iterXPathExpressions	= attribute.findValues(DataTypes.DT_XPATHEXPRESSION);
				if (iterXPathExpressions != null && iterXPathExpressions.hasNext()) {
					if (listXPathExpressions == null) {
						listXPathExpressions	= new ArrayList<>();
					}
					// The content selector xpath expressions from the request context don't have any namespace context.
					// To query the content we create new XPathExpressionWrappers with the content's namespace context.
					listXPathExpressions.add(new XPathExpressionWrapper(
							requestAttributes.getContentRoot().getOwnerDocument(),
							iterXPathExpressions.next().getValue().getPath()));
				}
			}
		}
		return listXPathExpressions;
	}

	@Override
	public ExpressionResult evaluate(EvaluationContext evaluationContext, PolicyDefaults policyDefaults) throws EvaluationException {
		if (!this.validate()) {
			return ExpressionResult.newError(new StdStatus(this.getStatusCode(), this.getStatusMessage()));
		}
		
		/*
		 * Get the DataType for this AttributeSelector for converting the resulting nodes into AttributeValues
		 */
		DataType thisDataType	= this.getDataType();

		Iterator iterRequestAttributes;
		if (getCategory() != null) {
			/*
			 * Get the Request so we can find the XPathExpression to locate the root node and to find the Content element
			 * of the requested category.
			 */
			Request request = evaluationContext.getRequest();
			assert (request != null);

			/*
			 * Get the RequestAttributes objects for our Category.  If none are found, then we abort quickly with either
			 * an empty or indeterminate result.
			 */
			iterRequestAttributes = request.getRequestAttributes(this.getCategory());
			if (iterRequestAttributes == null || !iterRequestAttributes.hasNext()) {
				return this.getEmptyResult("No Attributes with Category " + this.getCategory().toString(), null);
			}
		} else {
			assert (getEntity() != null);
			iterRequestAttributes = Collections.singleton(getEntity()).iterator();
		}
		
		/*
		 * Section 5.30 of the XACML 3.0 specification is a little vague about how to use the
		 * ContextSelectorId in the face of having multiple Attributes elements with the same CategoryId.  My interpretation
		 * is that each is distinct, so we look for an attribute matching the ContextSelectorId in each matching Attributes element
		 * and use that to search the Content in that particular Attributes element.  If either an Attribute matching the context selector id
		 * is not found or there is no Content, then that particular Attributes element is skipped.
		 */
		Bag bagAttributeValues						= new Bag();
		StdStatus statusFirstError					= null;
		while (iterRequestAttributes.hasNext()) {
			RequestAttributes requestAttributes	= iterRequestAttributes.next();
			
			/*
			 * See if we have a Content element to query.
			 */
			Node nodeContentRoot	= requestAttributes.getContentRoot();
			if (nodeContentRoot != null) {
				List listNodesToQuery					= new ArrayList<>();
				List listXPathExpressions	= this.getContextSelectorValues(requestAttributes);
				if (listXPathExpressions == null) {
					listNodesToQuery.add(nodeContentRoot);
				} else {
					Iterator iterXPathExpressions	= listXPathExpressions.iterator();
					while (iterXPathExpressions.hasNext()) {
						XPathExpression xpathExpression	= iterXPathExpressions.next();
						Node nodeContent	= requestAttributes.getContentNodeByXpathExpression(xpathExpression);
						if (nodeContent != null) {
							listNodesToQuery.add(nodeContent);
						}
					}
					if (listNodesToQuery.isEmpty()) {
						return ExpressionResult.newError(new StdStatus(StdStatusCode.STATUS_CODE_SYNTAX_ERROR, "No context node selected"));
					}
				}
				
				/*
				 * If there are any nodes to query, do so now and add the results
				 */
				if (! listNodesToQuery.isEmpty()) {
					for (Node nodeToQuery : listNodesToQuery) {
						NodeList nodeList	= null;
						try {
							XPath xPath	= XPathFactory.newInstance().newXPath();
							xPath.setNamespaceContext(new NodeNamespaceContext(nodeToQuery.getOwnerDocument()));
							XPathExpression xPathExpression	= xPath.compile(this.getPath());
							Node nodeToQueryDocumentRoot	= null;
							try {
								nodeToQueryDocumentRoot	= DOMUtil.getDirectDocumentChild(nodeToQuery);
							} catch (DOMStructureException ex) {
								return ExpressionResult.newError(new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR, "Exception processing context node: " + ex.getMessage()));
							}
							nodeList	= (NodeList)xPathExpression.evaluate(nodeToQueryDocumentRoot, XPathConstants.NODESET);
						} catch (XPathExpressionException ex) {
							if (statusFirstError == null) {
								statusFirstError	= new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR, "XPathExpressionException: " + ex.getMessage());
							}
						}
						if (nodeList != null && nodeList.getLength() > 0) {
							for (int i = 0 ; i < nodeList.getLength() ; i++) {
								AttributeValue attributeValueNode	= null;
								try {
									attributeValueNode	= thisDataType.createAttributeValue(nodeList.item(i));
								} catch (DataTypeException ex) {
									if (statusFirstError == null) {
										statusFirstError	= new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR, ex.getMessage());
									}
								}
								if (attributeValueNode != null) {
									bagAttributeValues.add(attributeValueNode);
								} else if (statusFirstError == null) {
									statusFirstError	= new StdStatus(StdStatusCode.STATUS_CODE_PROCESSING_ERROR, "Unable to convert node to " + this.getDataTypeId().toString());
								}
							}
						}
					}
				}
			}
			
		}
		
		if (bagAttributeValues.size() == 0) {
			if (statusFirstError == null) {
				return this.getEmptyResult("No Content found", null);
			} else {
				return ExpressionResult.newError(statusFirstError);
			}
		} else {
			return ExpressionResult.newBag(bagAttributeValues);
		}
	}
	
	@Override
	public String toString() {
		StringBuilder stringBuilder	= new StringBuilder("{");
		
		stringBuilder.append("super=");
		stringBuilder.append(super.toString());
		
		Object objectToDump;
		if ((objectToDump = this.getContextSelectorId()) != null) {
			stringBuilder.append(",contextSelectorId=");
			stringBuilder.append(objectToDump.toString());
		}
		if ((objectToDump = this.getPath()) != null) {
			stringBuilder.append(",path=");
			stringBuilder.append((String)objectToDump);
		}
		stringBuilder.append('}');
		return stringBuilder.toString();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy