package org.fcrepo.server.security.xacml.pdp.data; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.fcrepo.server.security.xacml.pdp.finder.policy.PolicyReader; import org.fcrepo.server.security.xacml.util.AttributeBean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Abstract class for XPath-based policy index * * Provides helper functions for generating xpath queries, decomposing * request details etc * * * @author Stephen Bayliss * @version $Id$ */ public abstract class XPathPolicyIndex extends PolicyIndexBase implements PolicyIndex { private static final Logger log = LoggerFactory.getLogger(XPathPolicyIndex.class.getName()); // specifies the XACML policy targest // the attribute map uses these in the format // lowerCase(target) + "Attributes" // and the names are used in the generation of the xpath // eg target + "s" for the target element, target for each individual element beneath that, etc private static String[] targets = new String[] {"Subject", "Resource", "Action", "Environment"}; protected XPathPolicyIndex(PolicyReader policyReader) throws PolicyIndexException { super(policyReader); } /** * Get XPath variables to use against an xpath query * @param attributeMap * @return a map of variable name / variable values */ // FIXME: public for some external testing. -> protected public static Map<String, String> getXpathVariables(Map<String, Collection<AttributeBean>> attributeMap) { // Set all the bind variables in the query context Map<String, String> variables = new HashMap<String, String>(); for (String t : targets) { int count = 0; for (AttributeBean bean : attributeMap.get(t.toLowerCase() + "Attributes")) { if (bean.getId().equals(XACML_RESOURCE_ID)) { variables.put("XacmlResourceId", bean.getId()); if (log.isDebugEnabled()) { log.debug("XacmlResourceId = '" + bean.getId() + "'"); } int c = 0; for (String value : bean.getValues()) { variables.put("XacmlResourceIdValue" + c, value); if (log.isDebugEnabled()) { log.debug("XacmlResourceIdValue" + c + " = '" + value + "'"); } c++; } } else { variables.put(t + "Id" + count, bean.getId()); if (log.isDebugEnabled()) { log.debug(t + "Id" + count + " = '" + bean.getId() + "'"); } int valueCount = 0; for (String value : bean.getValues()) { variables.put(t + "Id" + count + "-Value" + valueCount, value); if (log.isDebugEnabled()) { log.debug(t + "Id" + count + "-Value" + valueCount + " = '" + value + "'"); } valueCount++; } count++; } } } return variables; } /** * Creates an XPath query from the attributes * @param attributeMap attributes from request * @return String */ protected static String getXpath(Map<String, Collection<AttributeBean>> attributeMap) { StringBuilder sb = new StringBuilder(); getXpath(attributeMap, sb); return sb.toString(); } protected static void getXpath(Map<String, Collection<AttributeBean>> attributeMap, StringBuilder sb) { int sections = 0; // FIXME: // this from the original dbxml implementation // "r" is the count of resource values where the attribute ID is xacml resource-id // not clear why this is actually needed but the query generator tests for a zero value int resourceValueCount = 0; for (AttributeBean b : attributeMap.get(RESOURCE_KEY)) { if (b.getId().equals(XACML_RESOURCE_ID)) { resourceValueCount = resourceValueCount + b.getValues().size(); } } // FIXME: will not work with policy sets sb.append("/p:Policy/p:Target["); for (String t : targets) { if (attributeMap.get(t.toLowerCase() + "Attributes").size() == 0) { continue; } if (sections > 0) { sb.append(" and "); } sections++; // Target:Start sb.append("("); // TargetNotSpecified:Start // selects policies which do not specify resources/subjects etc sb.append("("); // true if no resouces targets found in policy sb.append("not(p:" + t+ "s)"); sb.append(" or "); // matches the legacy 1.0 "AnyResource etc - should not be required sb.append("p:" + t + "s/p:Any" + t); sb.append(")"); // TargetNotSpecified:End int count = 0; // test for a policy attribute id not specified in the request // (as the request doesn't (yet) know about the attribute, there should be a match; eg custom RI attribute) sb.append(" or ("); sb.append("p:" + t + "s" + "/p:" + t + "/p:" + t + "Match/p:" + t + "AttributeDesignator/@AttributeId[" ); // do each attribute ID boolean firstBean = true; for (AttributeBean bean : attributeMap.get(t.toLowerCase() + "Attributes")) { if (!firstBean) { sb.append(" and "); } // special case for XACML_RESOURCE_ID (but see FIXME note below...) if (bean.getId().equals(XACML_RESOURCE_ID) && resourceValueCount > 0) { // tests that policy attribute id is not the request attribute id sb.append(". != $XacmlResourceId"); firstBean = false; } else { // same for non XACML_RESOURCE_ID sb.append("$" + t + "Id" + count); firstBean = false; } } sb.append("]"); sb.append(")"); // Do each target attribute id for (AttributeBean bean : attributeMap.get(t.toLowerCase() + "Attributes")) { sb.append(" or "); sb.append("("); // FIXME: r = 0 seems to cater for a case where there is/are resource attributeBeans, but // no attribute values are specified - could this actually happen? if (bean.getId().equals(XACML_RESOURCE_ID) && resourceValueCount > 0) { sb.append("p:" + t + "s/p:" + t + "/p:" + t + "Match/"); sb.append("p:" + t + "AttributeDesignator/@AttributeId = "); sb.append("$XacmlResourceId"); sb.append(" and "); /* * sb.append("p:" + t + "s/p:" + t + "/p:" + t + "Match/"); * sb.append("p:" + t + "AttributeDesignator/@DataType = "); * sb.append("$XacmlResourceType"); sb.append(" and "); */ sb.append("("); for (int i = 0; i < bean.getValues().size(); i++) { if (i > 0) { sb.append(" or "); } sb.append("p:" + t + "s/p:" + t + "/p:" + t + "Match/"); sb.append("p:AttributeValue = "); sb.append("$XacmlResourceIdValue" + i); } sb.append(")"); } else { sb.append("p:" + t + "s/p:" + t + "/p:" + t + "Match/"); sb.append("p:" + t + "AttributeDesignator/@AttributeId = "); sb.append("$" + t + "Id" + count); sb.append(" and "); sb.append("("); /* * sb.append("p:" + t + "s/p:" + t + "/p:" + t + "Match/"); * sb.append("p:" + t + "AttributeDesignator/@DataType = "); * sb.append("$" + t + "Type" + count); sb.append(" and "); */ for (int valueCount = 0; valueCount < bean.getValues() .size(); valueCount++) { if (valueCount > 0) { sb.append(" or "); } sb.append("p:" + t + "s/p:" + t + "/p:" + t + "Match/"); sb.append("p:AttributeValue = "); sb.append("$" + t + "Id" + count + "-Value" + valueCount); } sb.append(")"); count++; } sb.append(")"); } sb.append(")"); // Target:End } sb.append("]"); } }