/*- * ============LICENSE_START======================================================= * ONAP-XACML * ================================================================================ * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved. * ================================================================================ * 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. * ============LICENSE_END========================================================= */ package org.onap.policy.xacml.util; import com.att.research.xacml.api.AttributeAssignment; import com.att.research.xacml.std.IdentifierImpl; import com.att.research.xacml.std.StdAttribute; import com.att.research.xacml.std.StdAttributeAssignment; import com.att.research.xacml.std.StdAttributeValue; import com.att.research.xacml.std.StdMutableAdvice; import com.att.research.xacml.std.StdMutableObligation; import com.att.research.xacml.util.XACMLPolicyScanner.Callback; import com.att.research.xacml.util.XACMLPolicyScanner.CallbackResult; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; import java.util.Iterator; import java.util.List; import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBElement; import javax.xml.bind.Unmarshaller; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionsType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AllOfType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeSelectorType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.ConditionType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionsType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicySetType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType; import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableDefinitionType; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.onap.policy.common.logging.eelf.MessageCodes; import org.onap.policy.common.logging.eelf.PolicyLogger; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * class XACMLPolicyScanner. * *

This class traverses the hierarchy of a XACML 3.0 policy. You can optionally pass a Callback class * and override any desired methods to retrieve information from a policy. * * */ public class XACMLPolicyScanner { private static final Log logger = LogFactory.getLog(XACMLPolicyScanner.class); private Object policyObject = null; private Callback callback = null; /** * constructor. * * @param filename Path * @param callback Callback */ public XACMLPolicyScanner(Path filename, Callback callback) { try (InputStream is = Files.newInputStream(filename)) { this.policyObject = XACMLPolicyScanner.readPolicy(is); } catch (IOException e) { PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "XACMLPolicyScanner", "Failed to read policy"); } this.callback = callback; } /** * Constructor. * * @param filename InputStream * @param callback Callback */ public XACMLPolicyScanner(InputStream filename, Callback callback) { try (InputStream is = filename) { this.policyObject = XACMLPolicyScanner.readPolicy(is); } catch (IOException e) { PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "XACMLPolicyScanner", "Failed to read policy"); } this.callback = callback; } public XACMLPolicyScanner(PolicySetType policySet, Callback callback) { this.policyObject = policySet; this.callback = callback; } public XACMLPolicyScanner(PolicySetType policySet) { this(policySet, null); } public XACMLPolicyScanner(PolicyType policy, Callback callback) { this.policyObject = policy; this.callback = callback; } public XACMLPolicyScanner(PolicyType policy) { this(policy, null); } /** * Sets the callback interface to be used. * * @param cb Callback object */ public void setCallback(Callback cb) { this.callback = cb; } /** * Saves the given callback object then calls the scan() method. * * @param cb Callback object * @return */ public Object scan(Callback cb) { this.callback = cb; return this.scan(); } /** * This begins the scanning of the contained object. * * @return - The PolicySet/Policy that was scanned. */ public Object scan() { if (this.policyObject == null) { return null; } if (this.callback != null && this.callback.onBeginScan(this.policyObject) == CallbackResult.STOP) { return this.policyObject; } if (this.policyObject instanceof PolicyType) { this.scanPolicy(null, (PolicyType) this.policyObject); } else if (this.policyObject instanceof PolicySetType) { this.scanPolicySet(null, (PolicySetType) this.policyObject); } else { PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW + "Unknown class type: " + this.policyObject.getClass().getCanonicalName()); } if (this.callback != null) { this.callback.onFinishScan(this.policyObject); } return this.policyObject; } /** * This performs the scan of a PolicySet. * * @param parent - Its parent PolicySet. Can be null if this is the root. * @param policySet - The PolicySet object. * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning. */ protected CallbackResult scanPolicySet(PolicySetType parent, PolicySetType policySet) { if (logger.isTraceEnabled()) { logger.trace("scanning policy set: " + policySet.getPolicySetId() + " " + policySet.getDescription()); } if (this.callback != null && this.callback.onPreVisitPolicySet(parent, policySet) == CallbackResult.STOP) { return CallbackResult.STOP; } // // Scan its info // if (this.scanTarget(policySet, policySet.getTarget()) == CallbackResult.STOP) { return CallbackResult.STOP; } if (this.scanObligations(policySet, policySet.getObligationExpressions()) == CallbackResult.STOP) { return CallbackResult.STOP; } if (this.scanAdvice(policySet, policySet.getAdviceExpressions()) == CallbackResult.STOP) { return CallbackResult.STOP; } // // Iterate the policy sets and/or policies // List> list = policySet.getPolicySetOrPolicyOrPolicySetIdReference(); for (JAXBElement element : list) { if ("PolicySet".equals(element.getName().getLocalPart()) && this.scanPolicySet(policySet, (PolicySetType) element.getValue()) == CallbackResult.STOP) { return CallbackResult.STOP; } else if ("Policy".equals(element.getName().getLocalPart()) && this.scanPolicy(policySet, (PolicyType) element.getValue()) == CallbackResult.STOP) { return CallbackResult.STOP; } else { logger.warn("generating policy sets found unsupported element: " + element.getName().getNamespaceURI()); } } if (this.callback != null && this.callback.onPostVisitPolicySet(parent, policySet) == CallbackResult.STOP) { return CallbackResult.STOP; } return CallbackResult.CONTINUE; } /** * This performs scanning of the Policy object. * * @param parent - The parent PolicySet of the policy. This can be null if this is a root Policy. * @param policy - The policy being scanned. * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning. */ protected CallbackResult scanPolicy(PolicySetType parent, PolicyType policy) { if (logger.isTraceEnabled()) { logger.trace("scanning policy: " + policy.getPolicyId() + " " + policy.getDescription()); } if (this.callback != null && this.callback.onPreVisitPolicy(parent, policy) == CallbackResult.STOP) { return CallbackResult.STOP; } // // Scan its info // if (this.scanTarget(policy, policy.getTarget()) == CallbackResult.STOP) { return CallbackResult.STOP; } if (this.scanVariables(policy, policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition()) == CallbackResult.STOP) { return CallbackResult.STOP; } if (this.scanObligations(policy, policy.getObligationExpressions()) == CallbackResult.STOP) { return CallbackResult.STOP; } if (this.scanAdvice(policy, policy.getAdviceExpressions()) == CallbackResult.STOP) { return CallbackResult.STOP; } // // Iterate the rules // List list = policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition(); for (Object o : list) { if (o instanceof RuleType) { RuleType rule = (RuleType) o; if (logger.isTraceEnabled()) { logger.trace("scanning rule: " + rule.getRuleId() + " " + rule.getDescription()); } if (this.callback != null && this.callback.onPreVisitRule(policy, rule) == CallbackResult.STOP) { return CallbackResult.STOP; } if (this.scanTarget(rule, rule.getTarget()) == CallbackResult.STOP) { return CallbackResult.STOP; } if (this.scanConditions(rule, rule.getCondition()) == CallbackResult.STOP) { return CallbackResult.STOP; } if (this.scanObligations(rule, rule.getObligationExpressions()) == CallbackResult.STOP) { return CallbackResult.STOP; } if (this.scanAdvice(rule, rule.getAdviceExpressions()) == CallbackResult.STOP) { return CallbackResult.STOP; } if (this.callback != null && this.callback.onPostVisitRule(policy, rule) == CallbackResult.STOP) { return CallbackResult.STOP; } } else if (o instanceof VariableDefinitionType) { if (this.callback != null && this.callback.onVariable(policy, (VariableDefinitionType) o) == CallbackResult.STOP) { return CallbackResult.STOP; } } else { if (logger.isDebugEnabled()) { logger.debug("scanning policy rules found unsupported object:" + o.toString()); } } } if (this.callback != null && this.callback.onPostVisitPolicy(parent, policy) == CallbackResult.STOP) { return CallbackResult.STOP; } return CallbackResult.CONTINUE; } /** * Scans the given target for attributes. Its sole purpose is to return attributes found. * * @param parent - The parent PolicySet/Policy/Rule for the target. * @param target - The target. * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning. */ protected CallbackResult scanTarget(Object parent, TargetType target) { if (target == null) { return CallbackResult.CONTINUE; } for (AnyOfType anyOf : target.getAnyOf()) { for (AllOfType allOf : anyOf.getAllOf()) { for (MatchType match : allOf.getMatch()) { // // Finally down to the actual attribute // StdAttribute attribute = null; AttributeValueType value = match.getAttributeValue(); if (match.getAttributeDesignator() != null && value != null) { AttributeDesignatorType designator = match.getAttributeDesignator(); // // The content may be tricky // attribute = new StdAttribute(new IdentifierImpl(designator.getCategory()), new IdentifierImpl(designator.getAttributeId()), new StdAttributeValue>(new IdentifierImpl(value.getDataType()), value.getContent()), designator.getIssuer(), false); } else if (match.getAttributeSelector() != null && value != null) { AttributeSelectorType selector = match.getAttributeSelector(); attribute = new StdAttribute(new IdentifierImpl(selector.getCategory()), new IdentifierImpl(selector.getContextSelectorId()), new StdAttributeValue>(new IdentifierImpl(value.getDataType()), value.getContent()), null, false); } else { logger.warn("NULL designator/selector or value for match."); } if (attribute != null && this.callback != null && this.callback.onAttribute(parent, target, attribute) == CallbackResult.STOP) { return CallbackResult.STOP; } } } } return CallbackResult.CONTINUE; } /** * Scan the list of obligations. * * @param parent - The parent PolicySet/Policy/Rule for the obligation. * @param obligationExpressionsType - All the obligation expressions. * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning. */ protected CallbackResult scanObligations(Object parent, ObligationExpressionsType obligationExpressionsType) { if (obligationExpressionsType == null) { return CallbackResult.CONTINUE; } List expressions = obligationExpressionsType.getObligationExpression(); for (ObligationExpressionType expression : expressions) { StdMutableObligation ob = new StdMutableObligation(new IdentifierImpl(expression.getObligationId())); for (AttributeAssignmentExpressionType assignment : expression.getAttributeAssignmentExpression()) { // category is optional and may be null IdentifierImpl categoryId = null; if (assignment.getCategory() != null) { categoryId = new IdentifierImpl(assignment.getCategory()); } AttributeAssignment attribute = new StdAttributeAssignment(categoryId, new IdentifierImpl(assignment.getAttributeId()), assignment.getIssuer(), new StdAttributeValue(null, null)); ob.addAttributeAssignment(attribute); } if (this.callback != null && this.callback.onObligation(parent, expression, ob) == CallbackResult.STOP) { return CallbackResult.STOP; } } return CallbackResult.CONTINUE; } /** * Scans the list of advice expressions returning each individually. * * @param parent - The parent PolicySet/Policy/Rule for the advice. * @param adviceExpressionstype - The list of advice expressions. * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning. */ protected CallbackResult scanAdvice(Object parent, AdviceExpressionsType adviceExpressionstype) { if (adviceExpressionstype == null) { return CallbackResult.CONTINUE; } List expressions = adviceExpressionstype.getAdviceExpression(); for (AdviceExpressionType expression : expressions) { StdMutableAdvice ob = new StdMutableAdvice(new IdentifierImpl(expression.getAdviceId())); for (AttributeAssignmentExpressionType assignment : expression.getAttributeAssignmentExpression()) { IdentifierImpl categoryId = null; if (assignment.getCategory() != null) { categoryId = new IdentifierImpl(assignment.getCategory()); } AttributeAssignment attribute = new StdAttributeAssignment(categoryId, new IdentifierImpl(assignment.getAttributeId()), assignment.getIssuer(), new StdAttributeValue(null, null)); ob.addAttributeAssignment(attribute); } if (this.callback != null && this.callback.onAdvice(parent, expression, ob) == CallbackResult.STOP) { return CallbackResult.STOP; } } return CallbackResult.CONTINUE; } /** * Scans the list of variable definitions. * * @param policy - Policy object containing the variable definition. * @param list - List of variable definitions. * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning. */ protected CallbackResult scanVariables(PolicyType policy, List list) { if (list == null) { return CallbackResult.CONTINUE; } for (Object o : list) { if (o instanceof VariableDefinitionType && this.callback != null && this.callback.onVariable(policy, (VariableDefinitionType) o) == CallbackResult.STOP) { return CallbackResult.STOP; } } return CallbackResult.CONTINUE; } /** * Scans the list of conditions. * * @param rule RuleType * @param condition ConditionType * @return CallbackResult */ protected CallbackResult scanConditions(RuleType rule, ConditionType condition) { if (condition != null && this.callback != null && this.callback.onCondition(rule, condition) == CallbackResult.STOP) { return CallbackResult.STOP; } return CallbackResult.CONTINUE; } /** * Reads the XACML XML policy file in and returns the version contained in the root Policy/PolicySet element. * * @param policy - The policy file. * @return - The version string from the file (uninterpreted) * @throws IOException IOException */ public static String getVersion(Path policy) throws IOException { Object data = null; try (InputStream is = Files.newInputStream(policy)) { data = XACMLPolicyScanner.readPolicy(is); } catch (IOException e) { PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "XACMLPolicyScanner", "Failed to read policy"); throw e; } if (data == null) { logger.warn("Version is null."); return null; } return getVersion(data); } /** * Reads the Policy/PolicySet element object and returns its current version. * * @param data - Either a PolicySet or Policy XACML type object. * @return - The integer version value. -1 if it doesn't exist or was un-parsable. */ public static String getVersion(Object data) { String version = null; if (data instanceof PolicySetType) { version = ((PolicySetType) data).getVersion(); } else if (data instanceof PolicyType) { version = ((PolicyType) data).getVersion(); } else { if (data != null) { PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE + "Expecting a PolicySet/Policy/Rule object. Got: " + data.getClass().getCanonicalName()); } return null; } if (version != null && version.length() > 0) { return version; } logger.warn("No version set in policy"); return null; } /** * Returns the Policy or PolicySet ID. * * @param data - A XACML 3.0 Policy or PolicySet element object. * @return The policy/policyset's policy ID */ public static String getID(Object data) { if (data instanceof PolicySetType) { return ((PolicySetType) data).getPolicySetId(); } else if (data instanceof PolicyType) { return ((PolicyType) data).getPolicyId(); } else { PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE + "Expecting a PolicySet/Policy/Rule object. Got: " + data.getClass().getCanonicalName()); return null; } } /** * getCreatedByModifiedBy. * * @param policyPath Path * @return List of String * @throws IOException IOException */ public static List getCreatedByModifiedBy(Path policyPath) throws IOException { String createdBy = ""; String modifiedBy = ""; String createdValue = "@CreatedBy:"; String modifiedValue = "@ModifiedBy:"; for (String line : Files.readAllLines(policyPath)) { line = line.replaceAll("\\s+", ""); if (line.isEmpty()) { continue; } if (line.contains("") && line.contains(createdValue) && line.contains(modifiedValue)) { createdBy = line.substring(line.indexOf(createdValue) + createdValue.length(), line.lastIndexOf(createdValue)); modifiedBy = line.substring(line.indexOf(modifiedValue) + modifiedValue.length(), line.lastIndexOf(modifiedValue)); break; } } return Arrays.asList(createdBy, modifiedBy); } // get the Created Name of the User on reading the Xml file /** * getCreatedBy. * * @param policyPath Path * @return String * @throws IOException IOException */ public static String getCreatedBy(Path policyPath) throws IOException { String userId = ""; String value = "@CreatedBy:"; for (String line : Files.readAllLines(policyPath)) { line = line.replaceAll("\\s+", ""); if (line.isEmpty()) { continue; } if (line.contains("") && line.contains(value)) { userId = line.substring(line.indexOf(value) + value.length(), line.lastIndexOf(value)); break; } } return userId; } // get the Modified Name of the User on reading the Xml file /** * getModifiedBy. * * @param policyPath Path * @return String * @throws IOException IOException */ public static String getModifiedBy(Path policyPath) throws IOException { String modifiedBy = ""; String value = "@ModifiedBy:"; for (String line : Files.readAllLines(policyPath)) { line = line.replaceAll("\\s+", ""); if (line.isEmpty()) { continue; } if (line.contains("") && line.contains(value)) { modifiedBy = line.substring(line.indexOf(value) + value.length(), line.lastIndexOf(value)); break; } } return modifiedBy; } /** * readPolicy - does the work to read in policy data from a file. * * @param is - The path to the policy file. * @return - The policy data object. This *should* be either a PolicySet or a Policy. */ public static Object readPolicy(InputStream is) { try { // // Create a DOM parser // DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); dbf.setNamespaceAware(true); DocumentBuilder db = dbf.newDocumentBuilder(); // // Parse the policy file // Document doc = db.parse(is); Element element = doc.getDocumentElement(); // // Is it a 3.0 policy? // if ("urn:oasis:names:tc:xacml:3.0:core:schema:wd-17".equals(element.getNamespaceURI())) { // // A policyset or policy could be the root // if (element.getNodeName().endsWith("Policy")) { // // Now we can create the context for the policy set // and unmarshall the policy into a class. // JAXBContext context = JAXBContext.newInstance(PolicyType.class); Unmarshaller um = context.createUnmarshaller(); JAXBElement root = um.unmarshal(element, PolicyType.class); // // Here is our policy set class // return root.getValue(); } else if (element.getNodeName().endsWith("PolicySet")) { // // Now we can create the context for the policy set // and unmarshall the policy into a class. // JAXBContext context = JAXBContext.newInstance(PolicySetType.class); Unmarshaller um = context.createUnmarshaller(); JAXBElement root = um.unmarshal(element, PolicySetType.class); // // Here is our policy set class // return root.getValue(); } else { if (logger.isDebugEnabled()) { logger.debug("Not supported yet: " + element.getNodeName()); } } } else { logger.warn("unsupported namespace: " + element.getNamespaceURI()); } } catch (Exception e) { PolicyLogger.error(MessageCodes.ERROR_SCHEMA_INVALID, e, "XACMLPolicyScanner", "Exception in readPolicy"); } return null; } /** * getPolicyObject. * * @return the policyObject */ public Object getPolicyObject() { return policyObject; } }