/*
* @(#)TestPolicyFinderModule.java
*
* Copyright 2004-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.test;
import java.io.FileInputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import com.sun.xacml.AbstractPolicy;
import com.sun.xacml.EvaluationCtx;
import com.sun.xacml.MatchResult;
import com.sun.xacml.Policy;
import com.sun.xacml.PolicyMetaData;
import com.sun.xacml.PolicyReference;
import com.sun.xacml.PolicySet;
import com.sun.xacml.VersionConstraints;
import com.sun.xacml.ctx.Status;
import com.sun.xacml.finder.PolicyFinder;
import com.sun.xacml.finder.PolicyFinderModule;
import com.sun.xacml.finder.PolicyFinderResult;
/**
* A <code>PolicyFinderModule</code> used to handle all policies in the conformance tests. It
* supports retrevial for requests and for references, but is tightly coupled with the structure of
* the conformance tests, and is definately not thread-safe.
*
* @author Seth Proctor
*/
public class TestPolicyFinderModule extends PolicyFinderModule {
// the finder that owns this module
private PolicyFinder finder = null;
// the policies we're currently using for request-based retrieval
private Set<AbstractPolicy> policies = null;
// a map of URIs to policies for the reference-based policies we're
// currently providing, and the current namespace prefix
private Map<String, String> policyRefs = null;
private String policyRefPrefix;
// a map of URIs to policies for the reference-based policy sets we're
// currently providing, and the current namespace prefix
private Map<String, String> policySetRefs = null;
private String policySetRefPrefix;
/**
* Default constructor.
*/
public TestPolicyFinderModule() {
policies = new HashSet<AbstractPolicy>();
}
/**
* Initializes this module with the given finder.
*
* @param finder
* the <code>PolicyFinder</code> that owns this module
*/
public void init(PolicyFinder finder) {
this.finder = finder;
}
/**
* Always returns true, since request-based retrieval is supported.
*
* @return true
*/
public boolean isRequestSupported() {
return true;
}
/**
* Always returns true, since reference-based retrieval is supported.
*
* @return true
*/
public boolean isIdReferenceSupported() {
return true;
}
/**
* Re-sets the policies known to this module to the single policy provided in the given file.
*
* @param policyFile
* a file containing a policy or policy set
*
* @throws Exception
* if the policy cannot be loaded
*/
public void setPolicies(String policyFile) throws Exception {
policies.clear();
AbstractPolicy policy = loadPolicy(policyFile, finder);
if (policy == null)
throw new Exception("failed to load policy");
policies.add(policy);
}
/**
* Re-sets the policies known to this module to those contained in the given files.
*
* @param policyFiles
* <code>String</code>s specifying files that contain policies or policy sets
*
* @throws Exception
* if the any of the policies cannot be loaded
*/
public void setPolicies(Set<String> policyFiles) throws Exception {
Iterator<String> it = policyFiles.iterator();
policies.clear();
while (it.hasNext()) {
AbstractPolicy policy = loadPolicy(it.next(), finder);
if (policy == null)
throw new Exception("failed to load policy");
policies.add(policy);
}
}
/**
* Re-sets the policy reference mapping used for policies.
*
* @param policyRefs
* the reference mapping
* @param prefix
* the prefix for these references
*/
public void setPolicyRefs(Map<String, String> policyRefs, String prefix) {
this.policyRefs = policyRefs;
policyRefPrefix = prefix;
}
/**
* Re-sets the policy reference mapping used for policy sets.
*
* @param policySetRefs
* the reference mapping
* @param prefix
* the prefix for these references
*/
public void setPolicySetRefs(Map<String, String> policySetRefs, String prefix) {
this.policySetRefs = policySetRefs;
policySetRefPrefix = prefix;
}
/**
* Finds the applicable policy (if there is one) for the given context.
*
* @param context
* the evaluation context
*
* @return an applicable policy, if one exists, or an error
*/
public PolicyFinderResult findPolicy(EvaluationCtx context) {
AbstractPolicy selectedPolicy = null;
Iterator<AbstractPolicy> it = policies.iterator();
// iterate through all the policies we currently have loaded
while (it.hasNext()) {
AbstractPolicy policy = it.next();
MatchResult match = policy.match(context);
int result = match.getResult();
// if target matching was indeterminate, then return the error
if (result == MatchResult.INDETERMINATE)
return new PolicyFinderResult(match.getStatus());
// see if the target matched
if (result == MatchResult.MATCH) {
// see if we previously found another match
if (selectedPolicy != null) {
// we found a match before, so this is an error
ArrayList<String> code = new ArrayList<String>();
code.add(Status.STATUS_PROCESSING_ERROR);
Status status = new Status(code, "too many applicable " + "top-level policies");
return new PolicyFinderResult(status);
}
// this is the first match we've found, so remember it
selectedPolicy = policy;
}
}
// return the single applicable policy (if there was one)
return new PolicyFinderResult(selectedPolicy);
}
/**
* Resolves the reference to a policy or policy set, if possible.
*
* @param idReference
* the reference to resolve
* @param type
* policy or policy set
* @param constraints
* ignored since this test uses only pre-2.0 policies
*
* @return the referenced policy, if one exists, or an error
*/
public PolicyFinderResult findPolicy(URI idReference, int type, VersionConstraints constraints,
PolicyMetaData metaData) {
String fileName = null;
// based on the type, see if we have any references available, and
// if we do then get the filename
if (type == PolicyReference.POLICY_REFERENCE) {
if (policyRefs == null)
return new PolicyFinderResult();
fileName = (String) (policyRefs.get(idReference.toString()));
} else {
if (policySetRefs == null)
return new PolicyFinderResult();
fileName = (String) (policySetRefs.get(idReference.toString()));
}
// if we had no mapping available, return with no referenced policy
if (fileName == null)
return new PolicyFinderResult();
// append the correct prefix to the filename
if (type == PolicyReference.POLICY_REFERENCE)
fileName = policyRefPrefix + fileName;
else
fileName = policySetRefPrefix + fileName;
// load the referenced policy
AbstractPolicy policy = loadPolicy(fileName, finder);
// if there was an error loading the policy, return the error
if (policy == null) {
ArrayList<String> code = new ArrayList<String>();
code.add(Status.STATUS_PROCESSING_ERROR);
Status status = new Status(code, "couldn't load referenced policy");
return new PolicyFinderResult(status);
}
// return the referenced policy
return new PolicyFinderResult(policy);
}
/**
* Private helper that tries to load the given file-based policy, and returns null if any error
* occurs.
*/
private AbstractPolicy loadPolicy(String filename, PolicyFinder finder) {
try {
// create the factory
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setIgnoringComments(true);
factory.setNamespaceAware(true);
factory.setValidating(false);
// create a builder based on the factory & try to load the policy
DocumentBuilder db = factory.newDocumentBuilder();
Document doc = db.parse(new FileInputStream(filename));
// handle the policy, if it's a known type
Element root = doc.getDocumentElement();
String name = root.getLocalName();
if (name.equals("Policy")) {
return Policy.getInstance(root);
} else if (name.equals("PolicySet")) {
return PolicySet.getInstance(root, finder);
}
} catch (Exception e) {
}
// a default fall-through in the case of an error
return null;
}
}