/* * @(#)AttributeDesignator.java * * Copyright 2003-2006 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.Constants; 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.Attribute; import com.sun.xacml.ctx.Status; import com.sun.xacml.ctx.StatusDetail; import com.sun.xacml.debug.RuntimeInfo; import com.sun.xacml.debug.RuntimeInfo.ELEMENT_TYPE; import com.sun.xacml.finder.RequiredAttributesModule; import java.io.OutputStream; import java.io.PrintStream; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import java.util.Set; import org.apache.log4j.Logger; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; /** * Represents Attribute Designators in XACML. * * @since 1.0 * @author Seth Proctor * @author Ludwig Seitz */ public class AttributeDesignator implements Evaluatable { /** * The category of the Designator. */ private URI category; /** * The datatype resolved by this designator */ private URI type; /** * The attribute id resolved by this designator */ private URI id; /** * The optional issuer attribute */ private URI issuer; /** * Throw an error if the resolution doesn't find something */ private boolean mustBePresent; /** * The XACML version */ private int xacmlVersion; /** * Backwards compatibility switch that indicates if the * SubjectCategory was explicit or default. */ private boolean withSubjectCategory = false; /** * Information where the Attribute Designator has been defined in src (files) */ private RuntimeInfo src = null; /** * the logger we'll use for all messages */ private static final Logger logger = Logger.getLogger(AttributeDesignator.class.getName()); private static ArrayList<RequiredAttributesModule> requiredAttrModules; public static void setRequiredAttrModules(ArrayList<RequiredAttributesModule> requiredAttrModules) { AttributeDesignator.requiredAttrModules = requiredAttrModules; } /** * Creates a new <code>AttributeDesignator</code>. * * @param category the category of this designator * @param type the data type resolved by this designator * @param id the attribute id looked for by this designator * @param mustBePresent whether resolution must find a value * @param issuer the issuer of the values to search for or null if no * issuer is specified * */ public AttributeDesignator(URI category, URI type, URI id, boolean mustBePresent, URI issuer) { this(category, type, id, mustBePresent, issuer, Constants.XACML_DEFAULT_VERSION, false); } /** * Creates a new <code>AttributeDesignator</code>. * * @param category the category of this designator * @param type the data type resolved by this designator * @param id the attribute id looked for by this designator * @param mustBePresent whether resolution must find a value * @param issuer the issuer of the values to search for or null if no * issuer is specified * @param xacmlVersion the XACML version that is used. * @param withSubjectCategory Backwards compatibility switch that * indicates if the SubjectCategory is * explicit or default. * */ public AttributeDesignator(URI category, URI type, URI id, boolean mustBePresent, URI issuer, int xacmlVersion, boolean withSubjectCategory) { this.category = category; this.type = type; this.id = id; this.mustBePresent = mustBePresent; this.issuer = issuer; this.xacmlVersion = xacmlVersion; this.withSubjectCategory = withSubjectCategory; } /** * Creates a new <code>AttributeDesignator</code> based on the DOM * root of the XML data. * * @param root the DOM root of the AttributeDesignatorType XML type * @param metaData the meta-data associated with the containing policy * * @return the designator * * @throws ParsingException if the AttributeDesignatorType was invalid */ public static AttributeDesignator getInstance(Node root, PolicyMetaData metaData) throws ParsingException { URI category = null; URI type = null; URI id = null; URI issuer = null; boolean mustBePresent = false; int xacmlVersion = metaData.getXACMLVersion(); boolean withSubjectCategory = false; RuntimeInfo src = RuntimeInfo.getRuntimeInfo(root, ELEMENT_TYPE.ATTRIBUTE_DESIGNATOR); NamedNodeMap attrs = root.getAttributes(); if (root.getNodeType() != Node.ELEMENT_NODE) { throw new ParsingException("Cannot build a AttributeDesignator" + " with this node type: " + root.getClass().getName() + (src != null ? src.getLocationMsgForError() : "")); } if (root.getLocalName().equals("SubjectAttributeDesignator")) { if (xacmlVersion > Constants.XACML_VERSION_2_0) { throw new ParsingException("Can't create a > XACML 2.0" + "AttributeDesignator from " + root.getLocalName() + (src != null ? src.getLocationMsgForError() : "")); } //compatibility code for XACML 2.0 category = Constants.SUBJECT_CAT; if (attrs != null) { Node catNode = attrs.getNamedItem("SubjectCategory"); if (catNode != null) { try { category = new URI(catNode.getNodeValue()); } catch (URISyntaxException e) { throw new ParsingException("Error while parsing" + "category: " + catNode.getNodeValue() + (src != null ? src.getLocationMsgForError() : "")); } withSubjectCategory = true; } } } else if (root.getLocalName().equals("ResourceAttributeDesignator")) { if (xacmlVersion > Constants.XACML_VERSION_2_0) { throw new ParsingException("Can't create an > XACML 2.0 " + "AttributeDesignator from " + root.getLocalName() + src.getLocationMsgForError()); } category = Constants.RESOURCE_CAT; } else if (root.getLocalName().equals("ActionAttributeDesignator")) { if (xacmlVersion > Constants.XACML_VERSION_2_0) { throw new ParsingException("Can't create an > XACML 2.0 " + "AttributeDesignator from " + root.getLocalName() + (src != null ? src.getLocationMsgForError() : "")); } category = Constants.ACTION_CAT; } else if (root.getLocalName().equals( "EnvironmentAttributeDesignator")) { if (xacmlVersion != Constants.XACML_VERSION_2_0) { throw new ParsingException("Can't create an XACML 2.0 " + "AttributeDesignator from " + root.getLocalName() + src.getLocationMsgForError()); } category = Constants.ENVIRONMENT_CAT; } else { if (xacmlVersion < Constants.XACML_VERSION_3_0) { throw new ParsingException("Can't create an XACML " + xacmlVersion + " AttributeDesignator from " + root.getLocalName() + (src != null ? src.getLocationMsgForError() : "")); } // there's always a category try { category = new URI( attrs.getNamedItem("Category").getNodeValue()); } catch (Exception e) { throw new ParsingException("Required Category missing or" + " invalid in AttributeDesignator" + (src != null ? src.getLocationMsgForError() : ""), e); } } try { // there's always an Id id = new URI(attrs.getNamedItem("AttributeId").getNodeValue()); } catch (Exception e) { throw new ParsingException("Required AttributeId missing or" + " invalid in AttributeDesignator" + src.getLocationMsgForError(), e); } try { // there's always a data type type = new URI(attrs.getNamedItem("DataType").getNodeValue()); } catch (Exception e) { throw new ParsingException("Required DataType missing or" + " invalid in AttributeDesignator" + (src != null ? src.getLocationMsgForError() : ""), e); } try { // there might be an issuer Node node = attrs.getNamedItem("Issuer"); if (node != null) { issuer = new URI(node.getNodeValue()); } // there might be a mustBePresent flag node = attrs.getNamedItem("MustBePresent"); if (node != null) { if (node.getNodeValue().equals("true")) { mustBePresent = true; } } } catch (Exception e) { // this shouldn't ever happen, but in theory something could go // wrong in the code in this try block throw new ParsingException("Error parsing AttributeDesignator " + "optional attributes" + (src != null ? src.getLocationMsgForError() : ""), e); } AttributeDesignator attrDesgn = new AttributeDesignator(category, type, id, mustBePresent, issuer, xacmlVersion, withSubjectCategory); if ( src != null ) { attrDesgn.src = src; src.setXACMLObject(attrDesgn); } return attrDesgn; } /** * Returns the type of attribute that is resolved by this designator. * While an AD will always return a bag, this method will always return * the type that is stored in the bag. * * @return the attribute type */ public URI getType() { return this.type; } /** * Returns the AttributeId of the values resolved by this designator. * * @return identifier for the values to resolve */ public URI getId() { return this.id; } /** * Returns the category for this designator. * * @return the category */ public URI getCategory() { return this.category; } /** * Returns the issuer of the values resolved by this designator if * specified. * * @return the attribute issuer or null if unspecified */ public URI getIssuer() { return this.issuer; } /** * Returns whether or not a value is required to be resolved by this * designator. * * @return true if a value is required, false otherwise */ public boolean mustBePresent() { return this.mustBePresent; } /** * Always returns true, since a designator always returns a bag of * attribute values. * * @return true */ public boolean returnsBag() { return true; } /** * Always returns an empty list since designators never have children. * * @return an empty <code>List</code> */ public List<Expression> getChildren() { return Expression.EMPTY_LIST; //Collections.<Expression>emptyList(); } public RuntimeInfo getRuntimeInfo() { return this.src; } /** * Evaluates the pre-assigned meta-data against the given context, * trying to find some matching values. * * @param context the representation of the request * * @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); EvaluationResult result = null; // look in the right section for some attribute values result = context.getAttribute(this.category, this.type, this.id, this.issuer); // if the lookup was indeterminate, then we return immediately if (result.indeterminate()) { context.closeCurrentEvent(result); return result; } BagAttribute bag = (BagAttribute)(result.getAttributeValue()); if (bag.isEmpty()) { // if it's empty, this may be an error if (this.mustBePresent) { if (logger.isInfoEnabled()) { logger.info("AttributeDesignator failed to resolve a " + "value for a required attribute: " + this.id.toString() + " (MustBePresent: " + mustBePresent + ")"); } ArrayList<String> code = new ArrayList<String>(); code.add(Status.STATUS_MISSING_ATTRIBUTE); String message = "Couldn't find " + "AttributeDesignator attribute"; // Note that there is a bug in the XACML spec. You can't // specify an identifier without specifying acceptable // values. Until this is fixed, this code will only // return the status code, and not any hints about what // was missing Set<Attribute> attr = null; for ( RequiredAttributesModule requAttrMod : requiredAttrModules ) { attr = requAttrMod.resolveRequiredAttributes(context, this); if ( attr != null && attr.size() > 0 ) { break; } } Status status; if ( attr == null ) { status = new Status(code, message); } else { status = new Status(code, message, new StatusDetail(attr)); } EvaluationResult evalResult = new EvaluationResult(status); context.closeCurrentEvent(evalResult); return evalResult; } } // if we got here the bag wasn't empty, or mustBePresent was false, // so we just return the result context.closeCurrentEvent(result); return result; } /** * Encodes this designator 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 designator 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 = ""; if (this.xacmlVersion > Constants.XACML_VERSION_2_0) { tag = "<AttributeDesignator Category=\"" + this.category.toString() + "\" "; } else { if (this.category.equals(Constants.RESOURCE_CAT)) { tag = "<ResourceAttributeDesignator "; } else if (this.category.equals(Constants.ACTION_CAT)) { tag = "<ActionAttributeDesignator "; } else if (this.xacmlVersion == Constants.XACML_VERSION_2_0 && this.category.equals(Constants.ENVIRONMENT_CAT)) { tag = "<EnvironmentAttributeDesignator "; } else { //For backwards compatibility all unknown categories are //cast to subject categories. if (this.withSubjectCategory) { tag = "<SubjectAttributeDesignator SubjectCategory=\"" + this.category.toString() + "\" "; } else { tag = "<SubjectAttributeDesignator "; } } } tag += "AttributeId=\"" + this.id.toString() + "\" DataType=\"" + this.type.toString() + "\""; if (this.issuer != null) { tag += " Issuer=\"" + this.issuer.toString() + "\""; } if (this.mustBePresent) { tag += " MustBePresent=\"true\""; } tag += "/>"; out.println(indent + tag); } }