2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 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.openecomp.policy.pdp.test.policy;
23 import java.io.IOException;
24 import java.net.MalformedURLException;
25 import java.nio.file.FileVisitResult;
26 import java.nio.file.Files;
27 import java.nio.file.Path;
28 import java.nio.file.Paths;
29 import java.nio.file.SimpleFileVisitor;
30 import java.nio.file.attribute.BasicFileAttributes;
31 import java.util.ArrayList;
32 import java.util.Collection;
33 import java.util.Collections;
34 import java.util.HashSet;
35 import java.util.List;
38 import java.util.regex.Matcher;
40 import javax.xml.bind.JAXBContext;
41 import javax.xml.bind.JAXBElement;
42 import javax.xml.bind.Marshaller;
44 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeType;
45 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
46 import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributesType;
47 import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObjectFactory;
48 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicySetType;
49 import oasis.names.tc.xacml._3_0.core.schema.wd_17.PolicyType;
50 import oasis.names.tc.xacml._3_0.core.schema.wd_17.RequestType;
52 import org.apache.commons.cli.CommandLine;
53 import org.apache.commons.cli.GnuParser;
54 import org.apache.commons.cli.Option;
55 import org.apache.commons.cli.ParseException;
56 import org.apache.commons.logging.Log;
57 import org.apache.commons.logging.LogFactory;
58 import org.openecomp.policy.pdp.test.TestBase;
59 import org.openecomp.policy.xacml.util.XACMLPolicyScanner;
61 import com.att.research.xacml.api.AttributeValue;
62 import com.att.research.xacml.api.DataType;
63 import com.att.research.xacml.api.DataTypeException;
64 import com.att.research.xacml.api.DataTypeFactory;
65 import com.att.research.xacml.api.Identifier;
66 import com.att.research.xacml.api.XACML3;
67 import com.att.research.xacml.std.IdentifierImpl;
68 import com.att.research.xacml.util.FactoryException;
69 import com.att.research.xacml.util.XACMLObjectCopy;
70 import com.att.research.xacml.util.XACMLPolicyAggregator;
71 import com.att.research.xacml.util.XACMLProperties;
75 * This class reads the policy in and extracts all the attributes and their values that is contained
76 * in the Policy. It then generates a request every single combination of attributes found.
78 * The attributes mostly come from the Target Match elements, since they have both an attribute designator/selector
79 * matched with an attribute value.
83 public class TestPolicy extends TestBase {
84 private static Log logger = LogFactory.getLog(TestPolicy.class);
88 private XACMLPolicyAggregator aggregator = new XACMLPolicyAggregator();
92 // Our command line parameters
94 public static final String OPTION_POLICY = "policy";
95 public static final String OPTION_SKIP_GENERATE = "skip";
98 options.addOption(new Option(OPTION_POLICY, true, "Path to the policy file."));
99 options.addOption(new Option(OPTION_SKIP_GENERATE, false, "Skip generating requests."));
102 public class FlattenerObject {
105 Identifier attribute;
106 Set<AttributeValue<?>> values;
110 * This application exercises a policy by producing ALL the possible request combinations for a policy.
112 * -policy Path to a policy file
115 * @throws HelpException
116 * @throws ParseException
117 * @throws MalformedURLException
120 public TestPolicy(String[] args) throws MalformedURLException, ParseException, HelpException {
125 * Look for the -policy command line argument. This application needs a pointer to a specific policy
130 * @see org.openecomp.policy.pdp.test.TestBase#parseCommands(java.lang.String[])
133 protected void parseCommands(String[] args) throws ParseException, MalformedURLException, HelpException {
135 // Have our super do its job
137 super.parseCommands(args);
139 // Look for the policy option
142 cl = new GnuParser().parse(options, args);
143 if (cl.hasOption(OPTION_POLICY)) {
144 this.policy = Paths.get(cl.getOptionValue(OPTION_POLICY));
148 if (Files.notExists(this.policy)) {
149 throw new ParseException("Policy file does not exist.");
152 throw new ParseException("You need to specify the policy file to be used.");
154 if (cl.hasOption(OPTION_SKIP_GENERATE)) {
162 * We override this method because here is where we want to scan the policy and aggregate all
163 * the attributes that are defined within the policy. This routine will then dump all the possible
164 * requests into the requests sub-directory. Thus, when this method returns the TestBase can proceed
165 * to iterate each generated request and run it against the PDP engine.
168 * @see org.openecomp.policy.pdp.test.TestBase#configure()
171 protected void configure() throws FactoryException {
173 // Have our base class do its thing
177 // Setup where the PDP can find the policy
179 System.setProperty(XACMLProperties.PROP_ROOTPOLICIES, "policy");
180 System.setProperty("policy.file", this.policy.toString());
182 // Determine if they want us to skip generation. This helps when a huge number of
183 // requests will get generated for a policy and can take some time to do so. The user
184 // can generate the requests once and then start testing a policy against the requests. Thus,
185 // the attributes never changed but the policy logic did (saves time).
191 // Now we will scan the policy and get all the attributes.
193 XACMLPolicyScanner scanner = new XACMLPolicyScanner(this.policy, this.aggregator);
195 // The scanner returns us a policy object
197 Object policyObject = scanner.scan();
199 // Just dump some info
201 if (policyObject instanceof PolicySetType) {
202 logger.info("Creating requests for policyset: " + ((PolicySetType)policyObject).getDescription());
203 } else if (policyObject instanceof PolicyType) {
204 logger.info("Creating requests for policy: " + ((PolicyType)policyObject).getDescription());
207 // Call the function to create the requests
209 if (policyObject != null) {
210 this.createRequests();
213 logger.info("Completed Generating requests.");
216 @SuppressWarnings("unchecked")
217 protected void createRequests() {
219 // Clear out our request directory
221 this.removeRequests();
225 Map<Identifier, Map<Identifier, Map<Identifier, Set<AttributeValue<?>>>>> attributeMap = this.aggregator.getAttributeMap();
227 // We're going to create an initial flat list of requests for each unique attribute ID. Unique being the
228 // category, datatype and attribute id.
230 // By flattening the list, it makes it easier to then generate all the combinations of possible requests.
232 List<FlattenerObject> attributes = new ArrayList<FlattenerObject>();
234 // Iterate through all the maps, we are going to flatten it
235 // out into an array list.
237 for (Map.Entry<Identifier, Map<Identifier, Map<Identifier, Set<AttributeValue<?>>>>> categoryEntry : attributeMap.entrySet()) {
238 String category = categoryEntry.getKey().toString();
239 if (logger.isDebugEnabled()) {
240 logger.debug("Category: " + category);
242 Map<Identifier, Map<Identifier, Set<AttributeValue<?>>>> datatypeMap = categoryEntry.getValue();
243 for (Map.Entry<Identifier, Map<Identifier, Set<AttributeValue<?>>>> datatypeEntry : datatypeMap.entrySet()) {
244 String datatype = datatypeEntry.getKey().toString();
245 if (logger.isDebugEnabled()) {
246 logger.debug("\tData Type: " + datatype);
248 Map<Identifier, Set<AttributeValue<?>>> attributeIDMap = datatypeEntry.getValue();
249 for (Map.Entry<Identifier, Set<AttributeValue<?>>> attributeIDEntry : attributeIDMap.entrySet()) {
250 String attributeID = attributeIDEntry.getKey().toString();
251 if (logger.isDebugEnabled()) {
252 logger.debug("\t\tAttribute ID: " + attributeID);
254 Set<AttributeValue<?>> attributeValueSet = attributeIDEntry.getValue();
256 // Sanity check to see if there are any values. Sometimes there isn't if an attribute
257 // is a designator that is part of a condition or variable.
259 if (attributeValueSet.isEmpty()) {
260 if (logger.isDebugEnabled()) {
261 logger.debug("No values for attribute " + attributeIDEntry.getKey().stringValue());
264 // Check for the boolean datatype, in that case we can safely
265 // assume the true/false are ALL the possible values.
267 if (datatypeEntry.getKey().equals(XACML3.ID_DATATYPE_BOOLEAN) == false) {
269 // Not boolean, so skip it
273 if (logger.isDebugEnabled()) {
274 logger.debug("No values but its a boolean datatype, we will include it anyway.");
278 // Create our flattener object
280 FlattenerObject flat = new FlattenerObject();
281 flat.category = categoryEntry.getKey();
282 flat.datatype = datatypeEntry.getKey();
283 flat.attribute = attributeIDEntry.getKey();
284 flat.values = new HashSet<AttributeValue<?>>();
285 if (datatypeEntry.getKey().equals(XACML3.ID_DATATYPE_BOOLEAN)) {
287 // There are only 2 possible values, true or false
289 flat.values.add(this.createAttributeValue(flat.datatype, true));
290 flat.values.add(this.createAttributeValue(flat.datatype, false));
292 flat.values.addAll(attributeValueSet);
294 attributes.add(flat);
298 if (attributes.size() <= 1) {
300 // Only one attribute, why bother
302 logger.info("Not enough attributes in policy: " + attributes.size());
306 * PLD work more on this later. This combinatorial formula is only accurate if each
307 * attribute has one value.
310 if (logger.isDebugEnabled()) {
312 // This isn't really accurate, if an attribute has more than one value
314 logger.debug(attributes.size() + " will generate " + computePossibleCombinations(attributes.size()));
317 for (int i = 0; i < attributes.size(); i++) {
318 FlattenerObject flat = attributes.get(i);
319 for (AttributeValue<?> value : flat.values) {
321 // Create a basic request object for just that attribute value.
323 RequestType request = new RequestType();
325 AttributesType attrs = new AttributesType();
326 attrs.setCategory(flat.category.stringValue());
327 request.getAttributes().add(attrs);
329 AttributeType attr = new AttributeType();
330 attr.setAttributeId(flat.attribute.stringValue());
331 attrs.getAttribute().add(attr);
333 AttributeValueType val = new AttributeValueType();
334 val.setDataType(flat.datatype.stringValue());
335 if (value.getValue() instanceof Collection) {
336 val.getContent().addAll((Collection<? extends Object>) value.getValue());
338 val.getContent().add(value.getValue().toString());
341 attr.getAttributeValue().add(val);
345 this.writeRequest(request);
347 // Initiate recursive call to add other attributes to the request
349 this.recursivelyGenerateRequests(request, i + 1, attributes);
354 protected void recursivelyGenerateRequests(RequestType request, int i, List<FlattenerObject> attributes) {
355 if (logger.isTraceEnabled()) {
356 logger.trace("recursiveGenerate index: " + index + " i: " + i);
358 for ( ; i < attributes.size(); i++) {
359 FlattenerObject flat = attributes.get(i);
360 for (AttributeValue<?> value : flat.values) {
362 // Make a copy of the request
364 RequestType copyRequest = XACMLObjectCopy.deepCopy(request);
366 // Create the value object
368 AttributeValueType newValue = new AttributeValueType();
369 newValue.setDataType(flat.datatype.stringValue());
370 if (value.getValue() instanceof Collection) {
371 for (Object v : (Collection<?>) value.getValue()) {
372 newValue.getContent().add(v.toString());
375 newValue.getContent().add(value.getValue().toString());
378 // Add the value to the request
380 this.addAttribute(copyRequest, flat.category.stringValue(), flat.attribute.stringValue(), newValue);
384 this.writeRequest(copyRequest);
386 // Recursively go through the rest of the attributes
388 this.recursivelyGenerateRequests(copyRequest, i + 1, attributes);
393 public static long computePossibleCombinations(long numberOfAttributes) {
395 for (long i = numberOfAttributes; i > 0; i--) {
396 num += computeCombinations(numberOfAttributes, i);
401 public static long computeFactorial(long n) {
403 for (long i = 1; i <= n; i++) {
409 public static long computePermutationsWithoutRepetition(long n, long r) {
417 for (long i = n; i > 1; i--) {
421 for (long i = (n - r); i > 1; i--) {
424 return nPrime / n_rPrime;
427 public static long computeCombinations(long n, long r) {
437 for (long i = n; i > 1; i--) {
441 for (long i = r; i > 1; i--) {
445 for (long i = (n - r); i > 1; i--) {
449 return nPrime / (rPrime * n_rPrime);
452 protected Set<AttributeValue<?>> getAttributeValues(RequestType request) {
456 Map<Identifier, Map<Identifier, Map<Identifier, Set<AttributeValue<?>>>>> attributeMap = this.aggregator.getAttributeMap();
458 // Find the attribute
460 AttributesType attrs = request.getAttributes().get(0);
461 Map<Identifier, Map<Identifier, Set<AttributeValue<?>>>> categoryMap = attributeMap.get(new IdentifierImpl(attrs.getCategory()));
462 if (categoryMap != null) {
463 AttributeType a = attrs.getAttribute().get(0);
464 Map<Identifier, Set<AttributeValue<?>>> datatypeMap = categoryMap.get(new IdentifierImpl(a.getAttributeValue().get(0).getDataType()));
465 if (datatypeMap != null) {
466 Set<AttributeValue<?>> values = datatypeMap.get(new IdentifierImpl(a.getAttributeId()));
467 if (values != null) {
472 return Collections.emptySet();
475 protected AttributeValue<?> createAttributeValue(Identifier datatype, Object value) {
476 DataTypeFactory dataTypeFactory = null;
478 dataTypeFactory = DataTypeFactory.newInstance();
479 if (dataTypeFactory == null) {
480 logger.error("Could not create data type factory");
483 } catch (FactoryException e) {
484 logger.error("Can't get Data type Factory: " + e.getLocalizedMessage());
487 DataType<?> dataTypeExtended = dataTypeFactory.getDataType(datatype);
488 if (dataTypeExtended == null) {
489 logger.error("Unknown datatype: " + datatype);
493 return dataTypeExtended.createAttributeValue(value);
494 } catch (DataTypeException e) {
500 protected void removeRequests() {
502 // Delete any existing request files that we generate. i.e. Have the Unknown in the file name.
505 Files.walkFileTree(Paths.get(this.directory.toString(), "requests"), new SimpleFileVisitor<Path>() {
508 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
510 // Sanity check the file name
512 Matcher matcher = pattern.matcher(file.getFileName().toString());
513 if (matcher.matches()) {
516 // Pull what this request is supposed to be
519 int count = matcher.groupCount();
521 group = matcher.group(count-1);
526 if (group.equals("Unknown")) {
532 } catch (Exception e) {
537 return super.visitFile(file, attrs);
540 } catch (IOException e) {
541 logger.error("Failed to removeRequests from " + this.directory + " " + e);
545 protected void addRequests(RequestType request, List<RequestType> requests, int index) {
546 for (RequestType req : requests) {
548 // There really should only be one attribute
550 for (AttributesType attrs : req.getAttributes()) {
551 for (AttributeType attr : attrs.getAttribute()) {
552 for (AttributeValueType value : attr.getAttributeValue()) {
553 if (this.addAttribute(request, attrs.getCategory(), attr.getAttributeId(), value)) {
554 this.writeRequest(request);
563 * Writes the request into the "requests" sub-directory, relative to the value of the "directory" setup
564 * during initialization.
566 * Writing the requests out allows one to go back and easily refer to the request when analyzing the responses
567 * generated after the PDP decide() call. Also, one can then use the generated requests into any test tools
568 * they wish to build.
570 * @param request - The request to be written.
572 protected void writeRequest(RequestType request) {
573 if (logger.isTraceEnabled()) {
574 logger.trace("writeRequest: " + index);
577 ObjectFactory of = new ObjectFactory();
578 JAXBElement<RequestType> requestElement = of.createRequest(request);
579 JAXBContext context = JAXBContext.newInstance(RequestType.class);
580 Marshaller m = context.createMarshaller();
581 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
582 Path outFile = Paths.get(this.directory, "requests", String.format("Request.%06d.Unknown.xml", this.index));
583 m.marshal(requestElement, outFile.toFile());
584 } catch (Exception e) {
585 logger.error("Failed to write request: " + e.getMessage());
590 protected boolean addAttribute(RequestType request, String category, String id, AttributeValueType value) {
592 // See if the category exists
594 for (AttributesType attrs : request.getAttributes()) {
595 if (attrs.getCategory().equals(category)) {
597 // It does have the category. But does it have the attribute ID?
599 for (AttributeType attr : attrs.getAttribute()) {
600 if (attr.getAttributeId().equals(id)) {
602 // Yes, check for the same datatype
604 for (AttributeValueType val : attr.getAttributeValue()) {
605 if (val.getDataType().equals(value.getDataType())) {
607 // We have something already there
613 // The ID exists, but not the datatype
615 attr.getAttributeValue().add(value);
620 // If we get here, the ID does not exist
622 AttributeType attr = new AttributeType();
623 attr.setAttributeId(id);
624 attr.getAttributeValue().add(value);
625 attrs.getAttribute().add(attr);
630 // If we get here, the category does not exist. So add it in.
632 AttributesType attrs = new AttributesType();
633 attrs.setCategory(category);
634 AttributeType attr = new AttributeType();
635 attr.setAttributeId(id);
636 attr.getAttributeValue().add(value);
637 attrs.getAttribute().add(attr);
638 request.getAttributes().add(attrs);
642 public static void main(String[] args) {
644 new TestPolicy(args).run();
645 } catch (ParseException | IOException | FactoryException e) {
647 } catch (HelpException e) {
652 // Map<CATEGORY, MAP<DATATYPE, MAP<ATTRIBUTEID, SET<VALUES>>>
653 @SuppressWarnings("unchecked")
654 private void generateRequests(Map<Identifier, Map<Identifier, Map<Identifier, Set<AttributeValue<?>>>>> categoryMap) {
655 meta = new ArrayList<>();
657 for (Map.Entry<Identifier, Map<Identifier, Map<Identifier, Set<AttributeValue<?>>>>> categoryEntry : categoryMap.entrySet()) {
658 String category = categoryEntry.getKey().toString();
659 logger.debug("Category: " + category);
660 Map<Identifier, Map<Identifier, Set<AttributeValue<?>>>> datatypeMap = categoryEntry.getValue();
661 for (Map.Entry<Identifier, Map<Identifier, Set<AttributeValue<?>>>> datatypeEntry : datatypeMap.entrySet()) {
662 String datatype = datatypeEntry.getKey().toString();
663 logger.debug("\tData Type: " + datatype);
664 Map<Identifier, Set<AttributeValue<?>>> attributeIDMap = datatypeEntry.getValue();
665 for (Map.Entry<Identifier, Set<AttributeValue<?>>> attributeIDEntry : attributeIDMap.entrySet()) {
666 String attributeID = attributeIDEntry.getKey().toString();
667 logger.debug("\t\tAttribute ID: " + attributeID);
668 Set<AttributeValue<?>> attributeValueSet = attributeIDEntry.getValue();
669 for (AttributeValue<?> value : attributeValueSet) {
670 logger.debug("\t\t\tAttribute Value: " + value);
672 Iterator<AttributeValue<?>> iterator = attributeValueSet.iterator();
673 logger.debug("\t\t\t# of Attribute values: " + attributeValueSet.size());
674 meta.add(new Object[] {category, datatype, attributeID, iterator.next(), iterator, attributeValueSet});
680 for (File file : output.toFile().listFiles()) {
685 RequestType request = new RequestType();
686 request.setCombinedDecision(false);
687 request.setReturnPolicyIdList(false);
688 List<AttributesType> attributesList = request.getAttributes();
690 Map<String, AttributesType> category2Attribute= new HashMap<>();
691 for (int i = 0; i < meta.size(); i++) {
692 Object[] record = meta.get(i);
694 AttributesType attributes = null;
695 if (category2Attribute.containsKey(record[0].toString()))
696 attributes = category2Attribute.get(record[0].toString());
698 attributes = new AttributesType();
699 attributes.setCategory(record[0].toString());
700 category2Attribute.put(record[0].toString(), attributes);
701 attributesList.add(attributes);
703 // attributes.setId(record[2].toString());
704 List<AttributeType> attrList = attributes.getAttribute();
706 AttributeType attribute = new AttributeType();
707 attribute.setAttributeId(record[2].toString());
708 List<AttributeValueType> valueList = attribute.getAttributeValue();
710 AttributeValue<?> attributeValue = (AttributeValue<?>) record[3];
712 AttributeValueType value = new AttributeValueType();
713 value.setDataType(attributeValue.getDataTypeId().toString());
714 List<Object> content = value.getContent();
715 content.addAll((Collection<? extends Object>) attributeValue.getValue());
716 valueList.add(value);
718 attrList.add(attribute);
722 ObjectFactory of = new ObjectFactory();
723 JAXBElement<RequestType> requestElement = of.createRequest(request);
724 JAXBContext context = JAXBContext.newInstance(RequestType.class);
725 Marshaller m = context.createMarshaller();
726 m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
727 m.marshal(requestElement, output.resolve("request" + count + ".xml").toFile());
729 // if (count == 0) {//Just send the first request to the engine
730 StringWriter sw = new StringWriter();
731 m.marshal(requestElement, sw);
732 logger.info(sw.toString());
733 EngineCaller engine = new LocalEngineCaller();
734 if (engine.startEngine("")) {
735 String response = engine.decide(sw.toString(), "xml");
736 FileWriter writer = new FileWriter(output.resolve("response" + count + ".xml").toFile());
737 writer.write(response);
739 logger.info("Response received: \n" + response);
742 } catch (Exception e) {
747 } while (hasNextRequest());
749 logger.info("# of requests generated: " + count);
752 private boolean hasNextRequest() {
753 int i = meta.size() - 1;
754 Object[] record = meta.get(i);
756 @SuppressWarnings("unchecked")
757 Iterator<AttributeValue<?>> iterator = (Iterator<AttributeValue<?>>) record[4];
758 if (iterator.hasNext()) {
759 record[3] = iterator.next();
761 return recycleAttributeValue(i);
767 @SuppressWarnings("unchecked")
768 private boolean recycleAttributeValue(int position) {
774 Object[] record = meta.get(position);
775 Set<AttributeValue<?>> attributeValueSet = (Set<AttributeValue<?>>) record[5];
776 Iterator<AttributeValue<?>> newIt = attributeValueSet.iterator();
778 record[3] = newIt.next();
779 int i = position - 1;
780 Object[] previous = meta.get(i);
781 Iterator<AttributeValue<?>> preIt = (Iterator<AttributeValue<?>>) previous[4];
782 if (preIt.hasNext()) {
783 previous[3] = preIt.next();
785 rc = recycleAttributeValue(i);