2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2019 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=========================================================
21 package org.onap.policy.xacml.util;
23 import com.att.research.xacml.api.AttributeAssignment;
24 import com.att.research.xacml.std.IdentifierImpl;
25 import com.att.research.xacml.std.StdAttribute;
26 import com.att.research.xacml.std.StdAttributeAssignment;
27 import com.att.research.xacml.std.StdAttributeValue;
28 import com.att.research.xacml.std.StdMutableAdvice;
29 import com.att.research.xacml.std.StdMutableObligation;
30 import com.att.research.xacml.util.XACMLPolicyScanner.Callback;
31 import com.att.research.xacml.util.XACMLPolicyScanner.CallbackResult;
33 import java.io.IOException;
34 import java.io.InputStream;
35 import java.nio.file.Files;
36 import java.nio.file.Path;
37 import java.util.Arrays;
38 import java.util.Iterator;
39 import java.util.List;
41 import javax.xml.bind.JAXBContext;
42 import javax.xml.bind.JAXBElement;
43 import javax.xml.bind.Unmarshaller;
44 import javax.xml.parsers.DocumentBuilder;
45 import javax.xml.parsers.DocumentBuilderFactory;
47 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionType;
48 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionsType;
49 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AllOfType;
50 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AnyOfType;
51 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType;
52 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType;
53 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeSelectorType;
54 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
55 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ConditionType;
56 import oasis.names.tc.xacml._3_0.core.schema.wd_17.MatchType;
57 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType;
58 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionsType;
59 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicySetType;
60 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
61 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RuleType;
62 import oasis.names.tc.xacml._3_0.core.schema.wd_17.TargetType;
63 import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableDefinitionType;
65 import org.apache.commons.logging.Log;
66 import org.apache.commons.logging.LogFactory;
67 import org.onap.policy.common.logging.eelf.MessageCodes;
68 import org.onap.policy.common.logging.eelf.PolicyLogger;
69 import org.w3c.dom.Document;
70 import org.w3c.dom.Element;
73 * class XACMLPolicyScanner.
75 * <p>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;
89 * @param filename Path
90 * @param callback Callback
92 public XACMLPolicyScanner(Path filename, Callback callback) {
93 try (InputStream is = Files.newInputStream(filename)) {
94 this.policyObject = XACMLPolicyScanner.readPolicy(is);
95 } catch (IOException e) {
96 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "XACMLPolicyScanner", "Failed to read policy");
98 this.callback = callback;
104 * @param filename InputStream
105 * @param callback Callback
107 public XACMLPolicyScanner(InputStream filename, Callback callback) {
108 try (InputStream is = filename) {
109 this.policyObject = XACMLPolicyScanner.readPolicy(is);
110 } catch (IOException e) {
111 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "XACMLPolicyScanner", "Failed to read policy");
113 this.callback = callback;
116 public XACMLPolicyScanner(PolicySetType policySet, Callback callback) {
117 this.policyObject = policySet;
118 this.callback = callback;
121 public XACMLPolicyScanner(PolicySetType policySet) {
122 this(policySet, null);
125 public XACMLPolicyScanner(PolicyType policy, Callback callback) {
126 this.policyObject = policy;
127 this.callback = callback;
130 public XACMLPolicyScanner(PolicyType policy) {
135 * Sets the callback interface to be used.
137 * @param cb Callback object
139 public void setCallback(Callback cb) {
144 * Saves the given callback object then calls the scan() method.
146 * @param cb Callback object
149 public Object scan(Callback cb) {
155 * This begins the scanning of the contained object.
157 * @return - The PolicySet/Policy that was scanned.
159 public Object scan() {
160 if (this.policyObject == null) {
163 if (this.callback != null && this.callback.onBeginScan(this.policyObject) == CallbackResult.STOP) {
164 return this.policyObject;
166 if (this.policyObject instanceof PolicyType) {
167 this.scanPolicy(null, (PolicyType) this.policyObject);
168 } else if (this.policyObject instanceof PolicySetType) {
169 this.scanPolicySet(null, (PolicySetType) this.policyObject);
171 PolicyLogger.error(MessageCodes.ERROR_PROCESS_FLOW + "Unknown class type: "
172 + this.policyObject.getClass().getCanonicalName());
174 if (this.callback != null) {
175 this.callback.onFinishScan(this.policyObject);
177 return this.policyObject;
181 * This performs the scan of a PolicySet.
183 * @param parent - Its parent PolicySet. Can be null if this is the root.
184 * @param policySet - The PolicySet object.
185 * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning.
187 protected CallbackResult scanPolicySet(PolicySetType parent, PolicySetType policySet) {
188 if (logger.isTraceEnabled()) {
189 logger.trace("scanning policy set: " + policySet.getPolicySetId() + " " + policySet.getDescription());
191 if (this.callback != null && this.callback.onPreVisitPolicySet(parent, policySet) == CallbackResult.STOP) {
192 return CallbackResult.STOP;
197 if (this.scanTarget(policySet, policySet.getTarget()) == CallbackResult.STOP) {
198 return CallbackResult.STOP;
200 if (this.scanObligations(policySet, policySet.getObligationExpressions()) == CallbackResult.STOP) {
201 return CallbackResult.STOP;
203 if (this.scanAdvice(policySet, policySet.getAdviceExpressions()) == CallbackResult.STOP) {
204 return CallbackResult.STOP;
207 // Iterate the policy sets and/or policies
209 List<JAXBElement<?>> list = policySet.getPolicySetOrPolicyOrPolicySetIdReference();
210 for (JAXBElement<?> element : list) {
211 if ("PolicySet".equals(element.getName().getLocalPart())
212 && this.scanPolicySet(policySet, (PolicySetType) element.getValue()) == CallbackResult.STOP) {
213 return CallbackResult.STOP;
214 } else if ("Policy".equals(element.getName().getLocalPart())
215 && this.scanPolicy(policySet, (PolicyType) element.getValue()) == CallbackResult.STOP) {
216 return CallbackResult.STOP;
218 logger.warn("generating policy sets found unsupported element: " + element.getName().getNamespaceURI());
221 if (this.callback != null && this.callback.onPostVisitPolicySet(parent, policySet) == CallbackResult.STOP) {
222 return CallbackResult.STOP;
224 return CallbackResult.CONTINUE;
228 * This performs scanning of the Policy object.
230 * @param parent - The parent PolicySet of the policy. This can be null if this is a root Policy.
231 * @param policy - The policy being scanned.
232 * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning.
234 protected CallbackResult scanPolicy(PolicySetType parent, PolicyType policy) {
235 if (logger.isTraceEnabled()) {
236 logger.trace("scanning policy: " + policy.getPolicyId() + " " + policy.getDescription());
238 if (this.callback != null && this.callback.onPreVisitPolicy(parent, policy) == CallbackResult.STOP) {
239 return CallbackResult.STOP;
244 if (this.scanTarget(policy, policy.getTarget()) == CallbackResult.STOP) {
245 return CallbackResult.STOP;
247 if (this.scanVariables(policy,
248 policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition()) == CallbackResult.STOP) {
249 return CallbackResult.STOP;
251 if (this.scanObligations(policy, policy.getObligationExpressions()) == CallbackResult.STOP) {
252 return CallbackResult.STOP;
254 if (this.scanAdvice(policy, policy.getAdviceExpressions()) == CallbackResult.STOP) {
255 return CallbackResult.STOP;
260 List<Object> list = policy.getCombinerParametersOrRuleCombinerParametersOrVariableDefinition();
261 for (Object o : list) {
262 if (o instanceof RuleType) {
263 RuleType rule = (RuleType) o;
264 if (logger.isTraceEnabled()) {
265 logger.trace("scanning rule: " + rule.getRuleId() + " " + rule.getDescription());
267 if (this.callback != null && this.callback.onPreVisitRule(policy, rule) == CallbackResult.STOP) {
268 return CallbackResult.STOP;
270 if (this.scanTarget(rule, rule.getTarget()) == CallbackResult.STOP) {
271 return CallbackResult.STOP;
273 if (this.scanConditions(rule, rule.getCondition()) == CallbackResult.STOP) {
274 return CallbackResult.STOP;
276 if (this.scanObligations(rule, rule.getObligationExpressions()) == CallbackResult.STOP) {
277 return CallbackResult.STOP;
279 if (this.scanAdvice(rule, rule.getAdviceExpressions()) == CallbackResult.STOP) {
280 return CallbackResult.STOP;
282 if (this.callback != null && this.callback.onPostVisitRule(policy, rule) == CallbackResult.STOP) {
283 return CallbackResult.STOP;
285 } else if (o instanceof VariableDefinitionType) {
286 if (this.callback != null
287 && this.callback.onVariable(policy, (VariableDefinitionType) o) == CallbackResult.STOP) {
288 return CallbackResult.STOP;
291 if (logger.isDebugEnabled()) {
292 logger.debug("scanning policy rules found unsupported object:" + o.toString());
296 if (this.callback != null && this.callback.onPostVisitPolicy(parent, policy) == CallbackResult.STOP) {
297 return CallbackResult.STOP;
299 return CallbackResult.CONTINUE;
303 * Scans the given target for attributes. Its sole purpose is to return attributes found.
305 * @param parent - The parent PolicySet/Policy/Rule for the target.
306 * @param target - The target.
307 * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning.
309 protected CallbackResult scanTarget(Object parent, TargetType target) {
310 if (target == null) {
311 return CallbackResult.CONTINUE;
313 for (AnyOfType anyOf : target.getAnyOf()) {
314 for (AllOfType allOf : anyOf.getAllOf()) {
315 for (MatchType match : allOf.getMatch()) {
317 // Finally down to the actual attribute
319 StdAttribute attribute = null;
320 AttributeValueType value = match.getAttributeValue();
321 if (match.getAttributeDesignator() != null && value != null) {
322 AttributeDesignatorType designator = match.getAttributeDesignator();
324 // The content may be tricky
326 attribute = new StdAttribute(new IdentifierImpl(designator.getCategory()),
327 new IdentifierImpl(designator.getAttributeId()),
328 new StdAttributeValue<List<?>>(new IdentifierImpl(value.getDataType()),
330 designator.getIssuer(), false);
331 } else if (match.getAttributeSelector() != null && value != null) {
332 AttributeSelectorType selector = match.getAttributeSelector();
333 attribute = new StdAttribute(new IdentifierImpl(selector.getCategory()),
334 new IdentifierImpl(selector.getContextSelectorId()),
335 new StdAttributeValue<List<?>>(new IdentifierImpl(value.getDataType()),
339 logger.warn("NULL designator/selector or value for match.");
341 if (attribute != null && this.callback != null && this.callback.onAttribute(parent,
342 target, attribute) == CallbackResult.STOP) {
343 return CallbackResult.STOP;
348 return CallbackResult.CONTINUE;
352 * Scan the list of obligations.
354 * @param parent - The parent PolicySet/Policy/Rule for the obligation.
355 * @param obligationExpressionsType - All the obligation expressions.
356 * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning.
358 protected CallbackResult scanObligations(Object parent, ObligationExpressionsType obligationExpressionsType) {
359 if (obligationExpressionsType == null) {
360 return CallbackResult.CONTINUE;
362 List<ObligationExpressionType> expressions = obligationExpressionsType.getObligationExpression();
363 for (ObligationExpressionType expression : expressions) {
364 StdMutableObligation ob = new StdMutableObligation(new IdentifierImpl(expression.getObligationId()));
365 for (AttributeAssignmentExpressionType assignment : expression.getAttributeAssignmentExpression()) {
366 // category is optional and may be null
367 IdentifierImpl categoryId = null;
368 if (assignment.getCategory() != null) {
369 categoryId = new IdentifierImpl(assignment.getCategory());
371 AttributeAssignment attribute =
372 new StdAttributeAssignment(categoryId, new IdentifierImpl(assignment.getAttributeId()),
373 assignment.getIssuer(), new StdAttributeValue<Object>(null, null));
374 ob.addAttributeAssignment(attribute);
376 if (this.callback != null && this.callback.onObligation(parent, expression, ob) == CallbackResult.STOP) {
377 return CallbackResult.STOP;
380 return CallbackResult.CONTINUE;
384 * Scans the list of advice expressions returning each individually.
386 * @param parent - The parent PolicySet/Policy/Rule for the advice.
387 * @param adviceExpressionstype - The list of advice expressions.
388 * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning.
390 protected CallbackResult scanAdvice(Object parent, AdviceExpressionsType adviceExpressionstype) {
391 if (adviceExpressionstype == null) {
392 return CallbackResult.CONTINUE;
394 List<AdviceExpressionType> expressions = adviceExpressionstype.getAdviceExpression();
395 for (AdviceExpressionType expression : expressions) {
396 StdMutableAdvice ob = new StdMutableAdvice(new IdentifierImpl(expression.getAdviceId()));
397 for (AttributeAssignmentExpressionType assignment : expression.getAttributeAssignmentExpression()) {
398 IdentifierImpl categoryId = null;
399 if (assignment.getCategory() != null) {
400 categoryId = new IdentifierImpl(assignment.getCategory());
402 AttributeAssignment attribute =
403 new StdAttributeAssignment(categoryId, new IdentifierImpl(assignment.getAttributeId()),
404 assignment.getIssuer(), new StdAttributeValue<Object>(null, null));
405 ob.addAttributeAssignment(attribute);
407 if (this.callback != null && this.callback.onAdvice(parent, expression, ob) == CallbackResult.STOP) {
408 return CallbackResult.STOP;
411 return CallbackResult.CONTINUE;
415 * Scans the list of variable definitions.
417 * @param policy - Policy object containing the variable definition.
418 * @param list - List of variable definitions.
419 * @return CallbackResult - CONTINUE to continue, STOP to terminate scanning.
421 protected CallbackResult scanVariables(PolicyType policy, List<Object> list) {
423 return CallbackResult.CONTINUE;
425 for (Object o : list) {
426 if (o instanceof VariableDefinitionType && this.callback != null
427 && this.callback.onVariable(policy, (VariableDefinitionType) o) == CallbackResult.STOP) {
428 return CallbackResult.STOP;
432 return CallbackResult.CONTINUE;
436 * Scans the list of conditions.
438 * @param rule RuleType
439 * @param condition ConditionType
440 * @return CallbackResult
442 protected CallbackResult scanConditions(RuleType rule, ConditionType condition) {
443 if (condition != null && this.callback != null
444 && this.callback.onCondition(rule, condition) == CallbackResult.STOP) {
445 return CallbackResult.STOP;
447 return CallbackResult.CONTINUE;
451 * Reads the XACML XML policy file in and returns the version contained in the root Policy/PolicySet element.
453 * @param policy - The policy file.
454 * @return - The version string from the file (uninterpreted)
455 * @throws IOException IOException
457 public static String getVersion(Path policy) throws IOException {
459 try (InputStream is = Files.newInputStream(policy)) {
460 data = XACMLPolicyScanner.readPolicy(is);
461 } catch (IOException e) {
462 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE, e, "XACMLPolicyScanner", "Failed to read policy");
466 logger.warn("Version is null.");
469 return getVersion(data);
473 * Reads the Policy/PolicySet element object and returns its current version.
475 * @param data - Either a PolicySet or Policy XACML type object.
476 * @return - The integer version value. -1 if it doesn't exist or was un-parsable.
478 public static String getVersion(Object data) {
479 String version = null;
480 if (data instanceof PolicySetType) {
481 version = ((PolicySetType) data).getVersion();
482 } else if (data instanceof PolicyType) {
483 version = ((PolicyType) data).getVersion();
486 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE + "Expecting a PolicySet/Policy/Rule object. Got: "
487 + data.getClass().getCanonicalName());
491 if (version != null && version.length() > 0) {
494 logger.warn("No version set in policy");
499 * Returns the Policy or PolicySet ID.
501 * @param data - A XACML 3.0 Policy or PolicySet element object.
502 * @return The policy/policyset's policy ID
504 public static String getID(Object data) {
505 if (data instanceof PolicySetType) {
506 return ((PolicySetType) data).getPolicySetId();
507 } else if (data instanceof PolicyType) {
508 return ((PolicyType) data).getPolicyId();
510 PolicyLogger.error(MessageCodes.ERROR_DATA_ISSUE + "Expecting a PolicySet/Policy/Rule object. Got: "
511 + data.getClass().getCanonicalName());
517 * getCreatedByModifiedBy.
519 * @param policyPath Path
520 * @return List of String
521 * @throws IOException IOException
523 public static List<String> getCreatedByModifiedBy(Path policyPath) throws IOException {
524 String createdBy = "";
525 String modifiedBy = "";
526 String createdValue = "@CreatedBy:";
527 String modifiedValue = "@ModifiedBy:";
528 for (String line : Files.readAllLines(policyPath)) {
529 line = line.replaceAll("\\s+", "");
530 if (line.isEmpty()) {
533 if (line.contains("<Description>") && line.contains(createdValue) && line.contains(modifiedValue)) {
534 createdBy = line.substring(line.indexOf(createdValue) + createdValue.length(),
535 line.lastIndexOf(createdValue));
536 modifiedBy = line.substring(line.indexOf(modifiedValue) + modifiedValue.length(),
537 line.lastIndexOf(modifiedValue));
541 return Arrays.asList(createdBy, modifiedBy);
544 // get the Created Name of the User on reading the Xml file
548 * @param policyPath Path
550 * @throws IOException IOException
552 public static String getCreatedBy(Path policyPath) throws IOException {
554 String value = "@CreatedBy:";
555 for (String line : Files.readAllLines(policyPath)) {
556 line = line.replaceAll("\\s+", "");
557 if (line.isEmpty()) {
560 if (line.contains("<Description>") && line.contains(value)) {
561 userId = line.substring(line.indexOf(value) + value.length(), line.lastIndexOf(value));
568 // get the Modified Name of the User on reading the Xml file
572 * @param policyPath Path
574 * @throws IOException IOException
576 public static String getModifiedBy(Path policyPath) throws IOException {
577 String modifiedBy = "";
578 String value = "@ModifiedBy:";
579 for (String line : Files.readAllLines(policyPath)) {
580 line = line.replaceAll("\\s+", "");
581 if (line.isEmpty()) {
584 if (line.contains("<Description>") && line.contains(value)) {
585 modifiedBy = line.substring(line.indexOf(value) + value.length(), line.lastIndexOf(value));
593 * readPolicy - does the work to read in policy data from a file.
595 * @param is - The path to the policy file.
596 * @return - The policy data object. This *should* be either a PolicySet or a Policy.
598 public static Object readPolicy(InputStream is) {
601 // Create a DOM parser
603 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
604 dbf.setNamespaceAware(true);
605 DocumentBuilder db = dbf.newDocumentBuilder();
607 // Parse the policy file
609 Document doc = db.parse(is);
610 Element element = doc.getDocumentElement();
612 // Is it a 3.0 policy?
614 if ("urn:oasis:names:tc:xacml:3.0:core:schema:wd-17".equals(element.getNamespaceURI())) {
616 // A policyset or policy could be the root
618 if (element.getNodeName().endsWith("Policy")) {
620 // Now we can create the context for the policy set
621 // and unmarshall the policy into a class.
623 JAXBContext context = JAXBContext.newInstance(PolicyType.class);
624 Unmarshaller um = context.createUnmarshaller();
625 JAXBElement<PolicyType> root = um.unmarshal(element, PolicyType.class);
627 // Here is our policy set class
629 return root.getValue();
630 } else if (element.getNodeName().endsWith("PolicySet")) {
632 // Now we can create the context for the policy set
633 // and unmarshall the policy into a class.
635 JAXBContext context = JAXBContext.newInstance(PolicySetType.class);
636 Unmarshaller um = context.createUnmarshaller();
637 JAXBElement<PolicySetType> root = um.unmarshal(element, PolicySetType.class);
639 // Here is our policy set class
641 return root.getValue();
643 if (logger.isDebugEnabled()) {
644 logger.debug("Not supported yet: " + element.getNodeName());
648 logger.warn("unsupported namespace: " + element.getNamespaceURI());
650 } catch (Exception e) {
651 PolicyLogger.error(MessageCodes.ERROR_SCHEMA_INVALID, e, "XACMLPolicyScanner", "Exception in readPolicy");
659 * @return the policyObject
661 public Object getPolicyObject() {