/*
* File: PolicyManager.java
*
* Copyright 2007 Macquarie E-Learning Centre Of Excellence
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package melcoe.xacml.pdp.finder.policy;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import melcoe.xacml.pdp.MelcoePDP;
import melcoe.xacml.pdp.data.PolicyDataManager;
import melcoe.xacml.pdp.data.PolicyDataManagerException;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.sun.xacml.AbstractPolicy;
import com.sun.xacml.EvaluationCtx;
import com.sun.xacml.MatchResult;
import com.sun.xacml.ParsingException;
import com.sun.xacml.PolicyMetaData;
import com.sun.xacml.PolicySet;
import com.sun.xacml.Target;
import com.sun.xacml.TargetMatch;
import com.sun.xacml.TargetSection;
import com.sun.xacml.combine.PolicyCombiningAlgorithm;
import com.sun.xacml.ctx.Status;
import com.sun.xacml.finder.PolicyFinder;
/**
* This class interacts with the policy store on behalf of the PolicyFinder
* modules. It also does the matching of the policies and creation of policy
* sets.
*
* @author nishen@melcoe.mq.edu.au
*/
public class PolicyManager {
private static final Logger log =
Logger.getLogger(PolicyManager.class.getName());
private PolicyDataManager policyDataManager = null;
private PolicyCombiningAlgorithm combiningAlg = null;
private Target target = null;
private PolicyReader policyReader = null;
// the policy identifier for any policy sets we dynamically create
private static final String PARENT_POLICY_ID =
"urn:com:sun:xacml:support:finder:dynamic-policy-set";
private static URI parentPolicyId = null;
/**
* This constructor creates a PolicyManager instance. It takes a
* PolicyFinder its argument. Its purpose is to obtain a set of policies
* from the Policy Store, match them against the evaluation context and
* return a policy or policy set that conforms to the evaluation context.
*
* @param polFinder
* the policy finder
* @throws URISyntaxException
* @throws {@link PolicyDataManagerException}
*/
public PolicyManager(PolicyFinder polFinder)
throws URISyntaxException, PolicyDataManagerException {
String home = MelcoePDP.PDP_HOME.getAbsolutePath();
String filename = home + "/conf/config-policy-manager.xml";
File f = new File(filename);
if (!f.exists()) {
throw new PolicyDataManagerException("Could not locate config file: "
+ f.getAbsolutePath());
}
try {
String policyDataManagerClassname = null;
String policyCombiningAlgorithmClassname = null;
DocumentBuilderFactory factory =
DocumentBuilderFactory.newInstance();
DocumentBuilder docBuilder = factory.newDocumentBuilder();
Document doc = docBuilder.parse(new FileInputStream(f));
NodeList nodes =
doc.getElementsByTagName("PolicyManager").item(0)
.getChildNodes();
for (int x = 0; x < nodes.getLength(); x++) {
Node node = nodes.item(x);
if (node.getNodeType() == Node.ELEMENT_NODE) {
if (node.getNodeName().equals("PolicyDataManager")) {
policyDataManagerClassname =
node.getFirstChild().getNodeValue();
} else if (node.getNodeName()
.equals("PolicyCombiningAlgorithm")) {
policyCombiningAlgorithmClassname =
node.getFirstChild().getNodeValue();
}
}
}
policyDataManager =
(PolicyDataManager) Class
.forName(policyDataManagerClassname).newInstance();
combiningAlg =
(PolicyCombiningAlgorithm) Class
.forName(policyCombiningAlgorithmClassname)
.newInstance();
} catch (Exception e) {
throw new PolicyDataManagerException(e);
}
policyReader = new PolicyReader(polFinder, null);
parentPolicyId = new URI(PARENT_POLICY_ID);
target =
new Target(new TargetSection(null,
TargetMatch.SUBJECT,
PolicyMetaData.XACML_VERSION_2_0),
new TargetSection(null,
TargetMatch.RESOURCE,
PolicyMetaData.XACML_VERSION_2_0),
new TargetSection(null,
TargetMatch.ACTION,
PolicyMetaData.XACML_VERSION_2_0),
new TargetSection(null,
TargetMatch.ENVIRONMENT,
PolicyMetaData.XACML_VERSION_2_0));
}
/**
* Obtains a policy or policy set of matching policies from the policy
* store. If more than one policy is returned it creates a dynamic policy
* set that contains all the applicable policies.
*
* @param eval
* the Evaluation Context
* @return the Policy/PolicySet that applies to this EvaluationCtx
* @throws TopLevelPolicyException
* @throws {@link PolicyDataManagerException}
*/
public AbstractPolicy getPolicy(EvaluationCtx eval)
throws TopLevelPolicyException, PolicyDataManagerException {
Map<String, byte[]> potentialPolicies =
policyDataManager.getPolicies(eval);
log.debug("Obtained policies: " + potentialPolicies.size());
AbstractPolicy policy = matchPolicies(eval, potentialPolicies);
log.debug("Matched policies and created abstract policy.");
return policy;
}
/**
* Given and Evaluation Context and a list of potential policies, this
* method matches each policy against the Evaluation Context and extracts
* only the ones that match. If there is more than one policy, a new dynamic
* policy set is created and returned. Otherwise the policy that is found is
* returned.
*
* @param eval
* the Evaluation Context
* @param policyList
* the list of policies as a map with PolicyId as key and policy as a
* byte array as the value
* @return the Policy/PolicySet that applies to this EvaluationCtx
* @throws {@link TopLevelPolicyException}
*/
private AbstractPolicy matchPolicies(EvaluationCtx eval,
Map<String, byte[]> policyList)
throws TopLevelPolicyException {
// setup a list of matching policies
Map<String, AbstractPolicy> list =
new HashMap<String, AbstractPolicy>();
// get an iterator over all the identifiers
for (String policyId : policyList.keySet()) {
try {
byte[] pol = policyList.get(policyId);
AbstractPolicy policy =
policyReader.readPolicy(new ByteArrayInputStream(pol));
MatchResult match = policy.match(eval);
int result = match.getResult();
if (result == MatchResult.INDETERMINATE) {
throw new TopLevelPolicyException(match.getStatus());
}
// if we matched, we keep track of the matching policy...
if (result == MatchResult.MATCH) {
// ...first checking if this is the first match and if
// we automaticlly nest policies
if (combiningAlg == null && list.size() > 0) {
ArrayList<String> code = new ArrayList<String>();
code.add(Status.STATUS_PROCESSING_ERROR);
Status status =
new Status(code, "too many applicable"
+ " top-level policies");
throw new TopLevelPolicyException(status);
}
if (log.isDebugEnabled()) {
log.debug("Matched policy: " + policyId);
}
list.put(policyId, policy);
}
} catch (ParsingException pe) {
log.error("Error parsing policy: " + policyId + " ("
+ pe.getMessage() + ")");
}
}
// no errors happened during the search, so now take the right
// action based on how many policies we found
switch (list.size()) {
case 0:
return null;
case 1:
Iterator<AbstractPolicy> i = list.values().iterator();
AbstractPolicy p = i.next();
return p;
default:
return new PolicySet(parentPolicyId,
combiningAlg,
target,
new ArrayList<AbstractPolicy>(list
.values()));
}
}
}