/*
* @(#)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.util.logging.Logger;
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>");
}
}
}