2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2018 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 * ============LICENSE_END=========================================================
20 package org.onap.policy.xacml.util;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.nio.file.Files;
25 import java.nio.file.Path;
26 import java.util.Arrays;
27 import java.util.Iterator;
28 import java.util.List;
30 import javax.xml.bind.JAXBContext;
31 import javax.xml.bind.JAXBElement;
32 import javax.xml.bind.Unmarshaller;
33 import javax.xml.parsers.DocumentBuilder;
34 import javax.xml.parsers.DocumentBuilderFactory;
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38 import org.w3c.dom.Document;
39 import org.w3c.dom.Element;
41 import org.onap.policy.common.logging.eelf.MessageCodes;
42 import org.onap.policy.common.logging.eelf.PolicyLogger;
44 import com.att.research.xacml.api.AttributeAssignment;
45 import com.att.research.xacml.std.IdentifierImpl;
46 import com.att.research.xacml.std.StdAttribute;
47 import com.att.research.xacml.std.StdAttributeAssignment;
48 import com.att.research.xacml.std.StdAttributeValue;
49 import com.att.research.xacml.std.StdMutableAdvice;
50 import com.att.research.xacml.std.StdMutableObligation;
51 import com.att.research.xacml.util.XACMLPolicyScanner.Callback;
52 import com.att.research.xacml.util.XACMLPolicyScanner.CallbackResult;
54 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionType;
55 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionsType;
56 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AllOfType;
57 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
58 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType;
59 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType;
60 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeSelectorType;
61 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
62 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ConditionType;
63 import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
64 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType;
65 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionsType;
66 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicySetType;
67 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
68 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
69 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
70 import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableDefinitionType;
73 * class XACMLPolicyScanner
75 * This class traverses the hierarchy of a XACML 3.0 policy. You can optionally pass a Callback class
76 * and override any desired methods to retrieve information from a policy.
80 public class XACMLPolicyScanner {
82 private static final Log logger = LogFactory.getLog(XACMLPolicyScanner.class);
83 private Object policyObject = null;
84 private Callback callback = null;
86 public XACMLPolicyScanner(Path filename, Callback callback) {
87 try (InputStream is = Files.newInputStream(filename)) {
88 this.policyObject = XACMLPolicyScanner.readPolicy(is);
89 } catch (IOException e) {
90 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "XACMLPolicyScanner", "Failed to read policy");
92 this.callback = callback;
95 public XACMLPolicyScanner(InputStream filename, Callback callback) {
96 try (InputStream is = filename) {
97 this.policyObject = XACMLPolicyScanner.readPolicy(is);
98 } catch (IOException e) {
99 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "XACMLPolicyScanner", "Failed to read policy");
101 this.callback = callback;
104 public XACMLPolicyScanner(PolicySetType policySet, Callback callback) {
105 this.policyObject = policySet;
106 this.callback = callback;
109 public XACMLPolicyScanner(PolicySetType policySet) {
110 this(policySet, null);
113 public XACMLPolicyScanner(PolicyType policy, Callback callback) {
114 this.policyObject = policy;
115 this.callback = callback;
118 public XACMLPolicyScanner(PolicyType policy) {
123 * Sets the callback interface to be used.
127 public void setCallback(Callback cb) {
132 * Saves the given callback object then calls the scan() method.
137 public Object scan(Callback cb) {
144 * This begins the scanning of the contained object.
146 * @return - The PolicySet/Policy that was scanned.
148 public Object scan() {
149 if (this.policyObject == null) {
152 if (this.callback != null && this.callback.onBeginScan(this.policyObject) == CallbackResult.STOP) {
153 return this.policyObject;
155 if (this.policyObject instanceof PolicyType) {
156 this.scanPolicy(null, (PolicyType) this.policyObject);
157 } else if (this.policyObject instanceof PolicySetType) {
158 this.scanPolicySet(null, (PolicySetType) this.policyObject);
160 PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW + "Unknown class type: " + this.policyObject.getClass().getCanonicalName());
162 if (this.callback != null) {
163 this.callback.onFinishScan(this.policyObject);
165 return this.policyObject;
169 * This performs the scan of a PolicySet
171 * @param parent - Its parent PolicySet. Can be null if this is the root.
172 * @param policySet - The PolicySet object.
173 * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning.
180 protected CallbackResult scanPolicySet(PolicySetType parent, PolicySetType policySet) {
181 if (logger.isTraceEnabled()) {
182 logger.trace("scanning policy set: " + policySet.getPolicySetId() + " " + policySet.getDescription());
184 if (this.callback != null && this.callback.onPreVisitPolicySet(parent, policySet) == CallbackResult.STOP) {
185 return CallbackResult.STOP;
190 if (this.scanTarget(policySet, policySet.getTarget()) == CallbackResult.STOP) {
191 return CallbackResult.STOP;
193 if (this.scanObligations(policySet, policySet.getObligationExpressions()) == CallbackResult.STOP) {
194 return CallbackResult.STOP;
196 if (this.scanAdvice(policySet, policySet.getAdviceExpressions()) == CallbackResult.STOP) {
197 return CallbackResult.STOP;
200 // Iterate the policy sets and/or policies
202 List<JAXBElement<?>> list = policySet.getPolicySetOrPolicyOrPolicySetIdReference();
203 for (JAXBElement<?> element: list) {
204 if ("PolicySet".equals(element.getName().getLocalPart()) &&
205 this.scanPolicySet(policySet, (PolicySetType)element.getValue()) == CallbackResult.STOP) {
206 return CallbackResult.STOP;
207 } else if ("Policy".equals(element.getName().getLocalPart()) &&
208 this.scanPolicy(policySet, (PolicyType)element.getValue()) == CallbackResult.STOP) {
209 return CallbackResult.STOP;
211 logger.warn("generating policy sets found unsupported element: " + element.getName().getNamespaceURI());
214 if (this.callback != null && this.callback.onPostVisitPolicySet(parent, policySet) == CallbackResult.STOP) {
215 return CallbackResult.STOP;
217 return CallbackResult.CONTINUE;
222 * This performs scanning of the Policy object.
224 * @param parent - The parent PolicySet of the policy. This can be null if this is a root Policy.
225 * @param policy - The policy being scanned.
226 * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning.
228 protected CallbackResult scanPolicy(PolicySetType parent, PolicyType policy) {
229 if (logger.isTraceEnabled()) {
230 logger.trace("scanning policy: " + policy.getPolicyId() + " " + policy.getDescription());
232 if (this.callback != null && this.callback.onPreVisitPolicy(parent, policy) == CallbackResult.STOP) {
233 return CallbackResult.STOP;
238 if (this.scanTarget(policy, policy.getTarget()) == CallbackResult.STOP) {
239 return CallbackResult.STOP;
241 if (this.scanVariables(policy, policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition()) == CallbackResult.STOP) {
242 return CallbackResult.STOP;
244 if (this.scanObligations(policy, policy.getObligationExpressions()) == CallbackResult.STOP) {
245 return CallbackResult.STOP;
247 if (this.scanAdvice(policy, policy.getAdviceExpressions()) == CallbackResult.STOP) {
248 return CallbackResult.STOP;
253 List<Object> list = policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition();
254 for (Object o: list) {
255 if (o instanceof RuleType) {
256 RuleType rule = (RuleType) o;
257 if (logger.isTraceEnabled()) {
258 logger.trace("scanning rule: " + rule.getRuleId() + " " + rule.getDescription());
260 if (this.callback != null && this.callback.onPreVisitRule(policy, rule) == CallbackResult.STOP) {
261 return CallbackResult.STOP;
263 if (this.scanTarget(rule, rule.getTarget()) == CallbackResult.STOP) {
264 return CallbackResult.STOP;
266 if (this.scanConditions(rule, rule.getCondition()) == CallbackResult.STOP) {
267 return CallbackResult.STOP;
269 if (this.scanObligations(rule, rule.getObligationExpressions()) == CallbackResult.STOP) {
270 return CallbackResult.STOP;
272 if (this.scanAdvice(rule, rule.getAdviceExpressions()) == CallbackResult.STOP) {
273 return CallbackResult.STOP;
275 if (this.callback != null && this.callback.onPostVisitRule(policy, rule) == CallbackResult.STOP) {
276 return CallbackResult.STOP;
278 } else if (o instanceof VariableDefinitionType) {
279 if (this.callback != null && this.callback.onVariable(policy, (VariableDefinitionType) o) == CallbackResult.STOP) {
280 return CallbackResult.STOP;
283 if (logger.isDebugEnabled()) {
284 logger.debug("scanning policy rules found unsupported object:" + o.toString());
288 if (this.callback != null && this.callback.onPostVisitPolicy(parent, policy) == CallbackResult.STOP) {
289 return CallbackResult.STOP;
291 return CallbackResult.CONTINUE;
295 * Scans the given target for attributes. Its sole purpose is to return attributes found.
297 * @param parent - The parent PolicySet/Policy/Rule for the target.
298 * @param target - The target.
299 * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning.
301 protected CallbackResult scanTarget(Object parent, TargetType target) {
302 if (target == null) {
303 return CallbackResult.CONTINUE;
305 List<AnyOfType> anyOfList = target.getAnyOf();
306 if (anyOfList != null) {
307 Iterator<AnyOfType> iterAnyOf = anyOfList.iterator();
308 while (iterAnyOf.hasNext()) {
309 AnyOfType anyOf = iterAnyOf.next();
310 List<AllOfType> allOfList = anyOf.getAllOf();
311 if (allOfList != null) {
312 Iterator<AllOfType> iterAllOf = allOfList.iterator();
313 while (iterAllOf.hasNext()) {
314 AllOfType allOf = iterAllOf.next();
315 List<MatchType> matchList = allOf.getMatch();
316 if (matchList != null) {
317 Iterator<MatchType> iterMatch = matchList.iterator();
318 while (iterMatch.hasNext()) {
319 MatchType match = iterMatch.next();
321 // Finally down to the actual attribute
323 StdAttribute attribute = null;
324 AttributeValueType value = match.getAttributeValue();
325 if (match.getAttributeDesignator() != null && value != null) {
326 AttributeDesignatorType designator = match.getAttributeDesignator();
328 // The content may be tricky
330 attribute = new StdAttribute(new IdentifierImpl(designator.getCategory()),
331 new IdentifierImpl(designator.getAttributeId()),
332 new StdAttributeValue<List<?>>(new IdentifierImpl(value.getDataType()), value.getContent()),
333 designator.getIssuer(),
335 } else if (match.getAttributeSelector() != null && value != null) {
336 AttributeSelectorType selector = match.getAttributeSelector();
337 attribute = new StdAttribute(new IdentifierImpl(selector.getCategory()),
338 new IdentifierImpl(selector.getContextSelectorId()),
339 new StdAttributeValue<List<?>>(new IdentifierImpl(value.getDataType()), value.getContent()),
343 logger.warn("NULL designator/selector or value for match.");
345 if (attribute != null && this.callback != null && this.callback.onAttribute(parent, target, attribute) == CallbackResult.STOP) {
346 return CallbackResult.STOP;
354 return CallbackResult.CONTINUE;
358 * Scan the list of obligations.
360 * @param parent - The parent PolicySet/Policy/Rule for the obligation.
361 * @param obligationExpressionsType - All the obligation expressions.
362 * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning.
364 protected CallbackResult scanObligations(Object parent, ObligationExpressionsType obligationExpressionsType) {
365 if (obligationExpressionsType == null) {
366 return CallbackResult.CONTINUE;
368 List<ObligationExpressionType> expressions = obligationExpressionsType.getObligationExpression();
369 if (expressions == null || expressions.isEmpty()) {
370 return CallbackResult.CONTINUE;
372 for (ObligationExpressionType expression : expressions) {
373 StdMutableObligation ob = new StdMutableObligation(new IdentifierImpl(expression.getObligationId()));
374 List<AttributeAssignmentExpressionType> assignments = expression.getAttributeAssignmentExpression();
375 if (assignments != null) {
376 for (AttributeAssignmentExpressionType assignment : assignments) {
377 // category is optional and may be null
378 IdentifierImpl categoryId = null;
379 if (assignment.getCategory() != null) {
380 categoryId = new IdentifierImpl(assignment.getCategory());
382 AttributeAssignment attribute = new StdAttributeAssignment(
384 new IdentifierImpl(assignment.getAttributeId()),
385 assignment.getIssuer(),
386 new StdAttributeValue<Object>(null, null)
388 ob.addAttributeAssignment(attribute);
391 if (this.callback != null && this.callback.onObligation(parent, expression, ob) == CallbackResult.STOP) {
392 return CallbackResult.STOP;
395 return CallbackResult.CONTINUE;
400 * Scans the list of advice expressions returning each individually.
402 * @param parent - The parent PolicySet/Policy/Rule for the advice.
403 * @param adviceExpressionstype - The list of advice expressions.
404 * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning.
406 protected CallbackResult scanAdvice(Object parent, AdviceExpressionsType adviceExpressionstype) {
407 if (adviceExpressionstype == null) {
408 return CallbackResult.CONTINUE;
410 List<AdviceExpressionType> expressions = adviceExpressionstype.getAdviceExpression();
411 if (expressions == null || expressions.isEmpty()) {
412 return CallbackResult.CONTINUE;
414 for (AdviceExpressionType expression : expressions) {
415 StdMutableAdvice ob = new StdMutableAdvice(new IdentifierImpl(expression.getAdviceId()));
416 List<AttributeAssignmentExpressionType> assignments = expression.getAttributeAssignmentExpression();
417 if (assignments != null) {
418 for (AttributeAssignmentExpressionType assignment : assignments) {
419 IdentifierImpl categoryId = null;
420 if (assignment.getCategory() != null) {
421 categoryId = new IdentifierImpl(assignment.getCategory());
423 AttributeAssignment attribute = new StdAttributeAssignment(
425 new IdentifierImpl(assignment.getAttributeId()),
426 assignment.getIssuer(),
427 new StdAttributeValue<Object>(null, null)
429 ob.addAttributeAssignment(attribute);
432 if (this.callback != null && this.callback.onAdvice(parent, expression, ob) == CallbackResult.STOP) {
433 return CallbackResult.STOP;
436 return CallbackResult.CONTINUE;
440 * Scans the list of variable definitions.
442 * @param policy - Policy object containing the variable definition.
443 * @param list - List of variable definitions.
444 * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning.
446 protected CallbackResult scanVariables(PolicyType policy, List<Object> list) {
448 return CallbackResult.CONTINUE;
450 for (Object o : list) {
451 if (o instanceof VariableDefinitionType && this.callback != null && this.callback.onVariable(policy, (VariableDefinitionType) o) == CallbackResult.STOP) {
452 return CallbackResult.STOP;
456 return CallbackResult.CONTINUE;
460 * Scans the list of conditions.
466 protected CallbackResult scanConditions(RuleType rule, ConditionType condition) {
467 if (condition != null && this.callback != null && this.callback.onCondition(rule, condition) == CallbackResult.STOP) {
468 return CallbackResult.STOP;
470 return CallbackResult.CONTINUE;
474 * Reads the XACML XML policy file in and returns the version contained in the root Policy/PolicySet element.
476 * @param policy - The policy file.
477 * @return - The version string from the file (uninterpreted)
478 * @throws IOException
480 public static String getVersion(Path policy) throws IOException {
482 try (InputStream is = Files.newInputStream(policy)) {
483 data = XACMLPolicyScanner.readPolicy(is);
484 } catch (IOException e) {
485 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "XACMLPolicyScanner", "Failed to read policy");
489 logger.warn("Version is null.");
492 return getVersion(data);
496 * Reads the Policy/PolicySet element object and returns its current version.
498 * @param data - Either a PolicySet or Policy XACML type object.
499 * @return - The integer version value. -1 if it doesn't exist or was un-parsable.
501 public static String getVersion(Object data) {
502 String version = null;
504 if (data instanceof PolicySetType) {
505 version = ((PolicySetType)data).getVersion();
506 } else if (data instanceof PolicyType) {
507 version = ((PolicyType)data).getVersion();
510 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE + "Expecting a PolicySet/Policy/Rule object. Got: " + data.getClass().getCanonicalName());
514 if (version != null && version.length() > 0) {
517 logger.warn("No version set in policy");
519 } catch (NumberFormatException e) {
520 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "XACMLPolicyScanner", "Invalid version contained in policy: " + version);
527 * Returns the Policy or PolicySet ID.
529 * @param data - A XACML 3.0 Policy or PolicySet element object.
530 * @return The policy/policyset's policy ID
532 public static String getID(Object data) {
533 if (data instanceof PolicySetType) {
534 return ((PolicySetType)data).getPolicySetId();
535 } else if (data instanceof PolicyType) {
536 return ((PolicyType)data).getPolicyId();
538 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE + "Expecting a PolicySet/Policy/Rule object. Got: " + data.getClass().getCanonicalName());
543 public static List<String> getCreatedByModifiedBy(Path policyPath) throws IOException{
544 String createdBy = "";
545 String modifiedBy= "";
546 String cValue = "@CreatedBy:";
547 String mValue = "@ModifiedBy:";
548 for(String line: Files.readAllLines(policyPath)){
549 line = line.replaceAll("\\s+", "");
553 if(line.contains("<Description>") && line.contains(cValue) && line.contains(mValue)){
554 createdBy = line.substring(line.indexOf(cValue) + cValue.length(), line.lastIndexOf(cValue));
555 modifiedBy = line.substring(line.indexOf(mValue) + mValue.length(), line.lastIndexOf(mValue));
559 return Arrays.asList(createdBy, modifiedBy);
562 //get the Created Name of the User on reading the Xml file
563 public static String getCreatedBy(Path policyPath) throws IOException{
565 String value = "@CreatedBy:";
566 for(String line: Files.readAllLines(policyPath)){
567 line = line.replaceAll("\\s+", "");
571 if(line.contains("<Description>") && line.contains(value)){
572 userId = line.substring(line.indexOf(value) + value.length(), line.lastIndexOf(value));
579 //get the Modified Name of the User on reading the Xml file
580 public static String getModifiedBy(Path policyPath) throws IOException{
581 String modifiedBy = "";
582 String value = "@ModifiedBy:";
583 for(String line: Files.readAllLines(policyPath)){
584 line = line.replaceAll("\\s+", "");
588 if(line.contains("<Description>") && line.contains(value)){
589 modifiedBy = line.substring(line.indexOf(value) + value.length(), line.lastIndexOf(value));
597 * readPolicy - does the work to read in policy data from a file.
599 * @param policy - The path to the policy file.
600 * @return - The policy data object. This *should* be either a PolicySet or a Policy.
602 public static Object readPolicy(InputStream is) {
605 // Create a DOM parser
607 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
608 dbf.setNamespaceAware(true);
609 DocumentBuilder db = dbf.newDocumentBuilder();
611 // Parse the policy file
613 Document doc = db.parse(is);
614 Element e = doc.getDocumentElement();
616 // Is it a 3.0 policy?
618 if ("urn:oasis:names:tc:xacml:3.0:core:schema:wd-17".equals(e.getNamespaceURI())) {
620 // A policyset or policy could be the root
622 if (e.getNodeName().endsWith("Policy")) {
624 // Now we can create the context for the policy set
625 // and unmarshall the policy into a class.
627 JAXBContext context = JAXBContext.newInstance(PolicyType.class);
628 Unmarshaller um = context.createUnmarshaller();
629 JAXBElement<PolicyType> root = um.unmarshal(e, PolicyType.class);
631 // Here is our policy set class
633 return root.getValue();
634 } else if (e.getNodeName().endsWith("PolicySet")) {
636 // Now we can create the context for the policy set
637 // and unmarshall the policy into a class.
639 JAXBContext context = JAXBContext.newInstance(PolicySetType.class);
640 Unmarshaller um = context.createUnmarshaller();
641 JAXBElement<PolicySetType> root = um.unmarshal(e, PolicySetType.class);
643 // Here is our policy set class
645 return root.getValue();
647 if (logger.isDebugEnabled()) {
648 logger.debug("Not supported yet: " + e.getNodeName());
652 logger.warn("unsupported namespace: " + e.getNamespaceURI());
654 } catch (Exception e) {
655 PolicyLogger.error(MessageCodes.ERROR_SCHEMA_INVALID, e, "XACMLPolicyScanner", "Exception in readPolicy");
661 * @return the policyObject
663 public Object getPolicyObject() {