package org.fcrepo.server.security.xacml.pdp.data;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.fcrepo.server.security.xacml.pdp.finder.policy.PolicyReader;
import org.fcrepo.server.security.xacml.util.AttributeBean;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.jboss.security.xacml.sunxacml.AbstractPolicy;
import org.jboss.security.xacml.sunxacml.EvaluationCtx;
import org.jboss.security.xacml.sunxacml.ParsingException;
import org.jboss.security.xacml.sunxacml.Policy;
import org.jboss.security.xacml.sunxacml.PolicySet;
import org.jboss.security.xacml.sunxacml.attr.AttributeDesignator;
import org.jboss.security.xacml.sunxacml.attr.AttributeValue;
import org.jboss.security.xacml.sunxacml.attr.BagAttribute;
import org.jboss.security.xacml.sunxacml.cond.EvaluationResult;
import org.jboss.security.xacml.sunxacml.finder.PolicyFinder;
/**
* Base abstract class for all PolicyIndex implementations.
*
* Gets the index configuration common to all implementations.
*
* @author Stephen Bayliss
* @version $Id$
*/
public abstract class PolicyIndexBase
implements PolicyIndex {
protected static final String SUBJECT_KEY = "subjectAttributes";
protected static final String RESOURCE_KEY = "resourceAttributes";
protected static final String ACTION_KEY = "actionAttributes";
protected static final String ENVIRONMENT_KEY = "environmentAttributes";
protected static final URI SUBJECT_CATEGORY_DEFAULT =
URI.create(AttributeDesignator.SUBJECT_CATEGORY_DEFAULT);
// used in testing - indicates if the implementation returns indexed results
// or if false indicates that all policies are returned irrespective of the request
public boolean indexed = true;
protected Map<String, Map<String, String>> indexMap = null;
protected PolicyReader m_policyReader;
protected static final String METADATA_POLICY_NS = "metadata";
// xacml namespaces and prefixes
public static final Map<String, String> namespaces = new HashMap<String, String>();
static {
namespaces.put("p", XACML20_POLICY_NS);
namespaces.put("m", METADATA_POLICY_NS); // appears to dbxml specific and probably not used
}
protected PolicyIndexBase(PolicyReader policyReader) throws PolicyIndexException {
m_policyReader = policyReader;
String[] indexMapElements =
{SUBJECT_KEY, RESOURCE_KEY,
ACTION_KEY, ENVIRONMENT_KEY};
indexMap = new HashMap<String, Map<String, String>>();
for (String s : indexMapElements) {
indexMap.put(s, new HashMap<String, String>());
}
}
public void setSubjectAttributes(Map<String, String> attributeMap) {
setAttributeMap(SUBJECT_KEY, attributeMap);
}
public void setResourceAttributes(Map<String, String> attributeMap) {
setAttributeMap(RESOURCE_KEY, attributeMap);
}
public void setActionAttributes(Map<String, String> attributeMap) {
setAttributeMap(ACTION_KEY, attributeMap);
}
public void setEnvironmentAttributes(Map<String, String> attributeMap) {
setAttributeMap(ENVIRONMENT_KEY, attributeMap);
}
protected void setAttributeMap(String mapKey, Map<String, String> attributeMap) {
indexMap.get(mapKey).putAll(attributeMap);
}
/**
* This method extracts the attributes listed in the indexMap from the given
* evaluation context.
*
* @param eval
* the Evaluation Context from which to extract Attributes
* @return a Map of Attributes for each category (Subject, Resource, Action,
* Environment)
* @throws URISyntaxException
*/
@SuppressWarnings("unchecked")
protected Map<String, Collection<AttributeBean>> getAttributeMap(EvaluationCtx eval) throws URISyntaxException {
final URI defaultCategoryURI = SUBJECT_CATEGORY_DEFAULT;
Map<String, String> im = null;
Map<String, Collection<AttributeBean>> attributeMap =
new HashMap<String, Collection<AttributeBean>>();
Map<String, AttributeBean> attributeBeans = null;
im = indexMap.get(SUBJECT_KEY);
attributeBeans = new HashMap<String, AttributeBean>();
for (String attributeId : im.keySet()) {
EvaluationResult result =
eval.getSubjectAttribute(new URI(im.get(attributeId)),
new URI(attributeId),
defaultCategoryURI);
if (result.getStatus() == null && !result.indeterminate()) {
AttributeValue attr = result.getAttributeValue();
if (attr.returnsBag()) {
Iterator<AttributeValue> i =
((BagAttribute) attr).iterator();
if (i.hasNext()) {
while (i.hasNext()) {
AttributeValue value = i.next();
String attributeType = im.get(attributeId);
AttributeBean ab = attributeBeans.get(attributeId);
if (ab == null) {
ab = new AttributeBean();
ab.setId(attributeId);
ab.setType(attributeType);
attributeBeans.put(attributeId, ab);
}
ab.addValue(value.encode());
}
}
}
}
}
attributeMap.put(SUBJECT_KEY, attributeBeans.values());
im = indexMap.get(RESOURCE_KEY);
attributeBeans = new HashMap<String, AttributeBean>();
for (String attributeId : im.keySet()) {
EvaluationResult result =
eval.getResourceAttribute(new URI(im.get(attributeId)),
new URI(attributeId),
null);
if (result.getStatus() == null && !result.indeterminate()) {
AttributeValue attr = result.getAttributeValue();
if (attr.returnsBag()) {
Iterator<AttributeValue> i =
((BagAttribute) attr).iterator();
if (i.hasNext()) {
while (i.hasNext()) {
AttributeValue value = i.next();
String attributeType = im.get(attributeId);
AttributeBean ab = attributeBeans.get(attributeId);
if (ab == null) {
ab = new AttributeBean();
ab.setId(attributeId);
ab.setType(attributeType);
attributeBeans.put(attributeId, ab);
}
if (attributeId.equals(XACML_RESOURCE_ID)
&& value.encode().startsWith("/")) {
String[] components =
makeComponents(value.encode());
if (components != null && components.length > 0) {
for (String c : components) {
ab.addValue(c);
}
} else {
ab.addValue(value.encode());
}
} else {
ab.addValue(value.encode());
}
}
}
}
}
}
attributeMap.put(RESOURCE_KEY, attributeBeans.values());
im = indexMap.get(ACTION_KEY);
attributeBeans = new HashMap<String, AttributeBean>();
for (String attributeId : im.keySet()) {
EvaluationResult result =
eval.getActionAttribute(new URI(im.get(attributeId)),
new URI(attributeId),
null);
if (result.getStatus() == null && !result.indeterminate()) {
AttributeValue attr = result.getAttributeValue();
if (attr.returnsBag()) {
Iterator<AttributeValue> i =
((BagAttribute) attr).iterator();
if (i.hasNext()) {
while (i.hasNext()) {
AttributeValue value = i.next();
String attributeType = im.get(attributeId);
AttributeBean ab = attributeBeans.get(attributeId);
if (ab == null) {
ab = new AttributeBean();
ab.setId(attributeId);
ab.setType(attributeType);
attributeBeans.put(attributeId, ab);
}
ab.addValue(value.encode());
}
}
}
}
}
attributeMap.put(ACTION_KEY, attributeBeans.values());
im = indexMap.get(ENVIRONMENT_KEY);
attributeBeans = new HashMap<String, AttributeBean>();
for (String attributeId : im.keySet()) {
URI imAttrId = new URI(im.get(attributeId));
URI attrId = new URI(attributeId);
EvaluationResult result =
eval.getEnvironmentAttribute(imAttrId, attrId, null);
if (result.getStatus() == null && !result.indeterminate()) {
AttributeValue attr = result.getAttributeValue();
if (attr.returnsBag()) {
Iterator<AttributeValue> i =
((BagAttribute) attr).iterator();
if (i.hasNext()) {
while (i.hasNext()) {
AttributeValue value = i.next();
String attributeType = im.get(attributeId);
AttributeBean ab = attributeBeans.get(attributeId);
if (ab == null) {
ab = new AttributeBean();
ab.setId(attributeId);
ab.setType(attributeType);
attributeBeans.put(attributeId, ab);
}
ab.addValue(value.encode());
}
}
}
}
}
attributeMap.put(ENVIRONMENT_KEY, attributeBeans.values());
return attributeMap;
}
/**
* A private method that handles reading the policy and creates the correct
* kind of AbstractPolicy.
* Because this makes use of the policyFinder, it cannot be reused between finders.
* Consider moving to policyManager, which is not intended to be reused outside
* of a policyFinderModule, which is not intended to be reused amongst PolicyFinder instances.
*/
protected AbstractPolicy handleDocument(Document doc, PolicyFinder policyFinder) throws ParsingException {
// handle the policy, if it's a known type
Element root = doc.getDocumentElement();
String name = root.getTagName();
// see what type of policy this is
if (name.equals("Policy")) {
return Policy.getInstance(root);
} else if (name.equals("PolicySet")) {
return PolicySet.getInstance(root, policyFinder);
} else {
// this isn't a root type that we know how to handle
throw new ParsingException("Unknown root document type: " + name);
}
}
/**
* Splits a XACML hierarchical resource-id value into a set of resource-id values
* that can be matched against a policy.
*
* Eg an incoming request for /res1/res2/res3/.* should match
* /res1/.*
* /res1/res2/.*
* /res1/res2/res3/.*
*
* in policies.
*
* @param resourceId XACML hierarchical resource-id value
* @return array of individual resource-id values that can be used to match against policies
*/
protected static String[] makeComponents(String resourceId) {
if (resourceId == null || resourceId.isEmpty()
|| !resourceId.startsWith("/")) {
return null;
}
List<String> components = new ArrayList<String>();
String[] parts = resourceId.split("\\/");
int bufPrimer = 0;
for (int x = 1; x < parts.length; x++) {
bufPrimer = Math.max(bufPrimer, (parts[x].length() + 1));
StringBuilder sb = new StringBuilder(bufPrimer);
for (int y = 0; y < x; y++) {
sb.append("/");
sb.append(parts[y + 1]);
}
String componentBase = sb.toString();
bufPrimer = componentBase.length() + 16;
components.add(componentBase);
if (x != parts.length - 1) {
components.add(componentBase.concat("/.*"));
} else {
components.add(componentBase.concat("$"));
}
}
return components.toArray(parts);
}
}