/*
* @(#)Target.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;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.StringWriter;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Represents the TargetType XML type in XACML. This also stores several
* other XML types: Subjects, Resources, Actions, and Environments (in XACML
* 2.0 and later). The target is used to quickly identify whether the parent
* element (a policy set, policy, or rule) is applicable to a given request.
*
* @since 1.0
* @author Seth Proctor
*/
public class Target
{
// the four sections of a Target
private TargetSection subjectsSection;
private TargetSection resourcesSection;
private TargetSection actionsSection;
private TargetSection environmentsSection;
// the version of XACML of the policy containing this target
private int xacmlVersion;
// the logger we'll use for all messages
private static final Logger logger =
Logger.getLogger(Target.class.getName());
/**
* Constructor that creates an XACML 1.x <code>Target</code> from
* components. Each of the sections must be non-null, but they may match
* any request. Because this is only used for 1.x Targets, there is no
* Environments section.
*
* @param subjectsSection a <code>TargetSection</code> representing
* the Subjects section of this target
* @param resourcesSection a <code>TargetSection</code> representing
* the Resources section of this target
* @param actionsSection a <code>TargetSection</code> representing
* the Actions section of this target
*/
public Target(TargetSection subjectsSection,
TargetSection resourcesSection,
TargetSection actionsSection) {
if ((subjectsSection == null) || (resourcesSection == null) ||
(actionsSection == null))
throw new ProcessingException("All sections of a Target must " +
"be non-null");
this.subjectsSection = subjectsSection;
this.resourcesSection = resourcesSection;
this.actionsSection = actionsSection;
this.environmentsSection =
new TargetSection(null, TargetMatch.ENVIRONMENT,
PolicyMetaData.XACML_VERSION_1_0);
this.xacmlVersion = PolicyMetaData.XACML_VERSION_1_0;
}
/**
* Constructor that creates an XACML 2.0 <code>Target</code> from
* components. Each of the sections must be non-null, but they may
* match any request.
*
* @param subjectsSection a <code>TargetSection</code> representing
* the Subjects section of this target
* @param resourcesSection a <code>TargetSection</code> representing
* the Resources section of this target
* @param actionsSection a <code>TargetSection</code> representing
* the Actions section of this target
* @param environmentsSection a <code>TargetSection</code> representing
* the Environments section of this target
*/
public Target(TargetSection subjectsSection,
TargetSection resourcesSection,
TargetSection actionsSection,
TargetSection environmentsSection)
{
if ((subjectsSection == null) || (resourcesSection == null) ||
(actionsSection == null) || (environmentsSection == null))
throw new ProcessingException("All sections of a Target must " +
"be non-null");
this.subjectsSection = subjectsSection;
this.resourcesSection = resourcesSection;
this.actionsSection = actionsSection;
this.environmentsSection = environmentsSection;
this.xacmlVersion = PolicyMetaData.XACML_VERSION_2_0;
}
/**
* Creates a <code>Target</code> by parsing a node.
*
* @deprecated As of 2.0 you should avoid using this method and should
* instead use the version that takes a
* <code>PolicyMetaData</code> instance. This method will
* only work for XACML 1.x policies.
*
* @param root the node to parse for the <code>Target</code>
* @param xpathVersion the XPath version to use in any selectors, or
* null if this is unspecified (ie, not supplied in
* the defaults section of the policy)
*
* @return a new <code>Target</code> constructed by parsing
*
* @throws ParsingException if the DOM node is invalid
*/
public static Target getInstance(Node root, String xpathVersion)
throws ParsingException
{
return getInstance(root,
new PolicyMetaData(
PolicyMetaData.XACML_1_0_IDENTIFIER,
xpathVersion));
}
/**
* Creates a <code>Target</code> by parsing a node.
*
* @param root the node to parse for the <code>Target</code>
* @return a new <code>Target</code> constructed by parsing
*
* @throws ParsingException if the DOM node is invalid
*/
public static Target getInstance(Node root, PolicyMetaData metaData)
throws ParsingException
{
TargetSection subjects = null;
TargetSection resources = null;
TargetSection actions = null;
TargetSection environments = null;
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String name = child.getNodeName();
if (name.equals("Subjects")) {
subjects =
TargetSection.getInstance(child, TargetMatch.SUBJECT,
metaData);
} else if (name.equals("Resources")) {
resources =
TargetSection.getInstance(child, TargetMatch.RESOURCE,
metaData);
} else if (name.equals("Actions")) {
actions =
TargetSection.getInstance(child, TargetMatch.ACTION,
metaData);
} else if (name.equals("Environments")) {
environments =
TargetSection.getInstance(child, TargetMatch.ENVIRONMENT,
metaData);
}
}
// starting in 2.0 an any-matching section is represented by a
// missing element, and in 1.x there were no Environments elements,
// so these need to get turned into non-null arguments
int version = metaData.getXACMLVersion();
if (subjects == null)
subjects = new TargetSection(null, TargetMatch.SUBJECT, version);
if (resources == null)
resources = new TargetSection(null, TargetMatch.RESOURCE, version);
if (actions == null)
actions = new TargetSection(null, TargetMatch.ACTION, version);
if (version == PolicyMetaData.XACML_VERSION_2_0) {
if (environments == null)
environments = new TargetSection(null, TargetMatch.ENVIRONMENT,
version);
return new Target(subjects, resources, actions, environments);
} else {
return new Target(subjects, resources, actions);
}
}
/**
* Returns the Subjects section of this Target.
*
* @return a <code>TargetSection</code> representing the Subjects
*/
public TargetSection getSubjectsSection() {
return subjectsSection;
}
/**
* Returns the Resources section of this Target.
*
* @return a <code>TargetSection</code> representing the Resources
*/
public TargetSection getResourcesSection() {
return resourcesSection;
}
/**
* Returns the Actions section of this Target.
*
* @return a <code>TargetSection</code> representing the Actions
*/
public TargetSection getActionsSection() {
return actionsSection;
}
/**
* Returns the Environments section of this Target. Note that if this is
* an XACML 1.x policy, then the section will always match anything,
* since XACML 1.x doesn't support matching on the Environment.
*
* @return a <code>TargetSection</code> representing the Environments
*/
public TargetSection getEnvironmentsSection() {
return environmentsSection;
}
/**
* Returns whether or not this <code>Target</code> matches any request.
*
* @return true if this Target matches any request, false otherwise
*/
public boolean matchesAny() {
return subjectsSection.matchesAny() && resourcesSection.matchesAny() &&
actionsSection.matchesAny() && environmentsSection.matchesAny();
}
/**
* Determines whether this <code>Target</code> matches
* the input request (whether it is applicable).
*
* @param context the representation of the request
*
* @return the result of trying to match the target and the request
*/
public MatchResult match(EvaluationCtx context) {
MatchResult result = null;
// before matching, see if this target matches any request
if (matchesAny())
return new MatchResult(MatchResult.MATCH);
// first, try matching the Subjects section
result = subjectsSection.match(context);
if (result.getResult() != MatchResult.MATCH) {
logger.finer("failed to match Subjects section of Target");
return result;
}
// now try matching the Resources section
result = resourcesSection.match(context);
if (result.getResult() != MatchResult.MATCH) {
logger.finer("failed to match Resources section of Target");
return result;
}
// next, look at the Actions section
result = actionsSection.match(context);
if (result.getResult() != MatchResult.MATCH) {
logger.finer("failed to match Actions section of Target");
return result;
}
// finally, match the Environments section
result = environmentsSection.match(context);
if (result.getResult() != MatchResult.MATCH) {
logger.finer("failed to match Environments section of Target");
return result;
}
// if we got here, then everything matched
return result;
}
/**
* Encodes this <code>Target</code> 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
*/
public void encode(OutputStream output) {
encode(output, new Indenter(0));
}
/**
* Encodes this <code>Target</code> 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 indenter an object that creates indentation strings
*/
public void encode(OutputStream output, Indenter indenter) {
PrintStream out = new PrintStream(output);
String indent = indenter.makeString();
// see if this Target matches anything
boolean matchesAny = (subjectsSection.matchesAny() &&
resourcesSection.matchesAny() &&
actionsSection.matchesAny() &&
environmentsSection.matchesAny());
if (matchesAny && (xacmlVersion == PolicyMetaData.XACML_VERSION_2_0)) {
// in 2.0, if all the sections match any request, then the Target
// element is empty and should be encoded simply as en empty tag
out.println("<Target/>");
} else {
out.println(indent + "<Target>");
indenter.in();
subjectsSection.encode(output, indenter);
resourcesSection.encode(output, indenter);
actionsSection.encode(output, indenter);
// we should only do this if we're a 2.0 policy
if (xacmlVersion == PolicyMetaData.XACML_VERSION_2_0)
environmentsSection.encode(output, indenter);
indenter.out();
out.println(indent + "</Target>");
}
}
public Element toOWLNode(Document doc) {
Element target = doc.createElement("Target");
target.setAttribute("rdf:ID", this.toString().replace('@', '_'));
Element subject = subjectsSection.toOWLNode(doc);
Element resource = resourcesSection.toOWLNode(doc);
Element action = actionsSection.toOWLNode(doc);
Element environment = environmentsSection.toOWLNode(doc);
if (subject!=null) {
target.appendChild(subject);
}
if (resource!=null) {
target.appendChild(resource);
}
if (action!=null) {
target.appendChild(action);
}
if (environment!=null) {
target.appendChild(environment);
}
return target;
}
}