Final blacklist fix for multiple entries
[policy/xacml-pdp.git] / applications / guard / src / test / java / org / onap / policy / xacml / pdp / application / guard / GuardPdpApplicationTest.java
index 656c727..f5392cf 100644 (file)
@@ -2,7 +2,7 @@
  * ============LICENSE_START=======================================================
  * ONAP
  * ================================================================================
- * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * Copyright (C) 2019-2020 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.
@@ -25,72 +25,356 @@ package org.onap.policy.xacml.pdp.application.guard;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatCode;
 
-import com.att.research.xacml.std.annotations.XACMLAction;
-import com.att.research.xacml.std.annotations.XACMLRequest;
-import com.att.research.xacml.std.annotations.XACMLResource;
-import com.att.research.xacml.std.annotations.XACMLSubject;
-
+import com.att.research.xacml.api.Response;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.sql.Date;
+import java.time.Instant;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.ServiceLoader;
+import java.util.UUID;
+import javax.persistence.EntityManager;
+import javax.persistence.Persistence;
+import org.apache.commons.lang3.tuple.Pair;
+import org.junit.AfterClass;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.ClassRule;
+import org.junit.FixMethodOrder;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
+import org.junit.runners.MethodSorters;
+import org.onap.policy.common.endpoints.parameters.RestServerParameters;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.resources.TextFileUtils;
+import org.onap.policy.models.decisions.concepts.DecisionRequest;
+import org.onap.policy.models.decisions.concepts.DecisionResponse;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
+import org.onap.policy.pdp.xacml.application.common.XacmlApplicationException;
+import org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider;
+import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils;
+import org.onap.policy.pdp.xacml.application.common.operationshistory.CountRecentOperationsPip;
+import org.onap.policy.pdp.xacml.application.common.operationshistory.Dbao;
+import org.onap.policy.pdp.xacml.xacmltest.TestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
 public class GuardPdpApplicationTest {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(GuardPdpApplicationTest.class);
+    private static Properties properties = new Properties();
+    private static File propertiesFile;
+    private static RestServerParameters clientParams = new RestServerParameters();
+    private static XacmlApplicationServiceProvider service;
+    private static DecisionRequest requestVfCount;
+    private static StandardCoder gson = new StandardCoder();
+    private static EntityManager em;
+    private static final String DENY = "Deny";
+    private static final String PERMIT = "Permit";
+
     @ClassRule
     public static final TemporaryFolder policyFolder = new TemporaryFolder();
 
     /**
-     * This is a simple annotation class to simulate
-     * requests coming in.
+     * Copies the xacml.properties and policies files into temporary folder and loads the service provider saving
+     * instance of provider off for other tests to use.
      */
-    @XACMLRequest(ReturnPolicyIdList = true)
-    public class MyXacmlRequest {
+    @BeforeClass
+    public static void setup() throws Exception {
+        LOGGER.info("Setting up class");
+        //
+        // Setup our temporary folder
+        //
+        XacmlPolicyUtils.FileCreator myCreator = (String filename) -> policyFolder.newFile(filename);
+        propertiesFile = XacmlPolicyUtils.copyXacmlPropertiesContents("src/test/resources/xacml.properties", properties,
+                myCreator);
+        //
+        // Load service
+        //
+        ServiceLoader<XacmlApplicationServiceProvider> applicationLoader =
+                ServiceLoader.load(XacmlApplicationServiceProvider.class);
+        //
+        // Find the guard service application and save for use in all the tests
+        //
+        StringBuilder strDump = new StringBuilder("Loaded applications:" + XacmlPolicyUtils.LINE_SEPARATOR);
+        Iterator<XacmlApplicationServiceProvider> iterator = applicationLoader.iterator();
+        while (iterator.hasNext()) {
+            XacmlApplicationServiceProvider application = iterator.next();
+            //
+            // Is it our service?
+            //
+            if (application instanceof GuardPdpApplication) {
+                //
+                // Should be the first and only one
+                //
+                assertThat(service).isNull();
+                service = application;
+            }
+            strDump.append(application.applicationName());
+            strDump.append(" supports ");
+            strDump.append(application.supportedPolicyTypes());
+            strDump.append(XacmlPolicyUtils.LINE_SEPARATOR);
+        }
+        LOGGER.info("{}", strDump);
+        //
+        // Tell it to initialize based on the properties file
+        // we just built for it.
+        //
+        service.initialize(propertiesFile.toPath().getParent(), clientParams);
+        //
+        // Load Decision Requests
+        //
+        requestVfCount =
+                gson.decode(TextFileUtils.getTextFileAsString("src/test/resources/requests/guard.vfCount.json"),
+                        DecisionRequest.class);
+        //
+        // Create EntityManager for manipulating DB
+        //
+        String persistenceUnit = CountRecentOperationsPip.ISSUER_NAME + ".persistenceunit";
+        em = Persistence
+                .createEntityManagerFactory(GuardPdpApplicationTest.properties.getProperty(persistenceUnit), properties)
+                .createEntityManager();
+    }
 
-        @XACMLSubject(includeInResults = true)
-        String onapName = "Drools";
+    /**
+     * Close the entity manager.
+     */
+    @AfterClass
+    public static void cleanup() throws Exception {
+        if (em != null) {
+            em.close();
+        }
+    }
 
-        @XACMLResource(includeInResults = true)
-        String resource = "onap.policies.Guard";
+    /**
+     * Clears the database before each test so there are no operations in it.
+     *
+     */
+    @Before
+    public void startClean() throws Exception {
+        em.getTransaction().begin();
+        em.createQuery("DELETE FROM Dbao").executeUpdate();
+        em.getTransaction().commit();
+    }
 
-        @XACMLAction()
-        String action = "guard";
+    /**
+     * Check that decision matches expectation.
+     *
+     * @param expected from the response
+     * @param response received
+     *
+     **/
+    public void checkDecision(String expected, DecisionResponse response) throws CoderException {
+        LOGGER.info("Looking for {} Decision", expected);
+        assertThat(response).isNotNull();
+        assertThat(response.getStatus()).isNotNull();
+        assertThat(response.getStatus()).isEqualTo(expected);
+        //
+        // Dump it out as Json
+        //
+        LOGGER.info(gson.encode(response));
     }
 
-    @Before
-    public void setUp() throws Exception {
+    /**
+     * Request a decision and check that it matches expectation.
+     *
+     * @param request to send to Xacml PDP
+     * @param expected from the response
+     *
+     **/
+    public void requestAndCheckDecision(DecisionRequest request, String expected) throws CoderException {
+        //
+        // Ask for a decision
+        //
+        Pair<DecisionResponse, Response> decision = service.makeDecision(request, null);
+        //
+        // Check decision
+        //
+        checkDecision(expected, decision.getKey());
+    }
 
+    @Test
+    public void test1Basics() throws CoderException, IOException {
+        LOGGER.info("**************** Running test1Basics ****************");
+        //
+        // Make sure there's an application name
+        //
+        assertThat(service.applicationName()).isNotEmpty();
+        //
+        // Decisions
+        //
+        assertThat(service.actionDecisionsSupported().size()).isEqualTo(1);
+        assertThat(service.actionDecisionsSupported()).contains("guard");
+        //
+        // Ensure it has the supported policy types and
+        // can support the correct policy types.
+        //
+        assertThat(service.supportedPolicyTypes()).isNotEmpty();
+        assertThat(service.supportedPolicyTypes().size()).isEqualTo(4);
+        assertThat(service.canSupportPolicyType(
+                new ToscaPolicyTypeIdentifier("onap.policies.controlloop.guard.common.FrequencyLimiter", "1.0.0")))
+                        .isTrue();
+        assertThat(service.canSupportPolicyType(
+                new ToscaPolicyTypeIdentifier("onap.policies.controlloop.guard.common.FrequencyLimiter", "1.0.1")))
+                        .isFalse();
+        assertThat(service.canSupportPolicyType(
+                new ToscaPolicyTypeIdentifier("onap.policies.controlloop.guard.common.MinMax", "1.0.0"))).isTrue();
+        assertThat(service.canSupportPolicyType(
+                new ToscaPolicyTypeIdentifier("onap.policies.controlloop.guard.common.MinMax", "1.0.1"))).isFalse();
+        assertThat(service.canSupportPolicyType(
+                new ToscaPolicyTypeIdentifier("onap.policies.controlloop.guard.common.Blacklist", "1.0.0"))).isTrue();
+        assertThat(service.canSupportPolicyType(
+                new ToscaPolicyTypeIdentifier("onap.policies.controlloop.guard.common.Blacklist", "1.0.1"))).isFalse();
+        assertThat(service.canSupportPolicyType(new ToscaPolicyTypeIdentifier(
+                "onap.policies.controlloop.guard.coordination.FirstBlocksSecond", "1.0.0"))).isTrue();
+        assertThat(service.canSupportPolicyType(new ToscaPolicyTypeIdentifier(
+                "onap.policies.controlloop.guard.coordination.FirstBlocksSecond", "1.0.1"))).isFalse();
+        assertThat(service.canSupportPolicyType(new ToscaPolicyTypeIdentifier("onap.foo", "1.0.1"))).isFalse();
     }
 
     @Test
-    public void testBasics() {
-        assertThatCode(() -> {
-            GuardPdpApplication guard = new GuardPdpApplication();
-            //
-            // Set the path
-            //
-            guard.initialize(policyFolder.getRoot().toPath());
-            //
-            // Application name
-            //
-            assertThat(guard.applicationName()).isNotEmpty();
-            //
-            // Decisions
-            //
-            assertThat(guard.actionDecisionsSupported().size()).isEqualTo(1);
-            assertThat(guard.actionDecisionsSupported()).contains("guard");
-            //
-            // Supported policy types
-            //
-            assertThat(guard.supportedPolicyTypes()).isNotEmpty();
-            assertThat(guard.supportedPolicyTypes().size()).isEqualTo(2);
-            assertThat(guard.canSupportPolicyType("onap.policies.controlloop.guard.FrequencyLimiter", "1.0.0"))
-                .isTrue();
-            assertThat(guard.canSupportPolicyType("onap.policies.controlloop.guard.FrequencyLimiter", "1.0.1"))
-                .isFalse();
-            assertThat(guard.canSupportPolicyType("onap.policies.controlloop.guard.MinMax", "1.0.0")).isTrue();
-            assertThat(guard.canSupportPolicyType("onap.policies.controlloop.guard.MinMax", "1.0.1")).isFalse();
-            assertThat(guard.canSupportPolicyType("onap.foo", "1.0.1")).isFalse();
-        }).doesNotThrowAnyException();
+    public void test2NoPolicies() throws CoderException {
+        LOGGER.info("**************** Running test2NoPolicies ****************");
+        assertThatCode(() -> requestAndCheckDecision(requestVfCount, PERMIT)).doesNotThrowAnyException();
+    }
+
+    @Test
+    public void test3FrequencyLimiter()
+            throws CoderException, FileNotFoundException, IOException, XacmlApplicationException {
+        LOGGER.info("**************** Running test3FrequencyLimiter ****************");
+        //
+        // Now load the vDNS frequency limiter Policy - make sure
+        // the pdp can support it and have it load
+        // into the PDP.
+        //
+        List<ToscaPolicy> loadedPolicies =
+                TestUtils.loadPolicies("policies/vDNS.policy.guard.frequencylimiter.input.tosca.yaml", service);
+        assertThat(loadedPolicies).hasSize(1);
+        assertThat(loadedPolicies.get(0).getName()).isEqualTo("guard.frequency.scaleout");
+        //
+        // Zero recent actions: should get permit
+        //
+        requestAndCheckDecision(requestVfCount, PERMIT);
+        //
+        // Add entry into operations history DB
+        //
+        insertOperationEvent(requestVfCount);
+        //
+        // Two recent actions, more than specified limit of 2: should get deny
+        //
+        requestAndCheckDecision(requestVfCount, DENY);
     }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void test4MinMax() throws CoderException, FileNotFoundException, IOException, XacmlApplicationException {
+        LOGGER.info("**************** Running test4MinMax ****************");
+        //
+        // Now load the vDNS min max Policy - make sure
+        // the pdp can support it and have it load
+        // into the PDP.
+        //
+        List<ToscaPolicy> loadedPolicies =
+                TestUtils.loadPolicies("policies/vDNS.policy.guard.minmaxvnfs.input.tosca.yaml", service);
+        assertThat(loadedPolicies).hasSize(1);
+        assertThat(loadedPolicies.get(0).getName()).isEqualTo("guard.minmax.scaleout");
+        //
+        // vfcount=0 below min of 1: should get a Permit
+        //
+        requestAndCheckDecision(requestVfCount, PERMIT);
+        //
+        // vfcount=1 between min of 1 and max of 2: should get a Permit
+        //
+        ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("vfCount", 1);
+        requestAndCheckDecision(requestVfCount, PERMIT);
+        //
+        // vfcount=2 hits the max of 2: should get a Deny
+        //
+        ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("vfCount", 2);
+        requestAndCheckDecision(requestVfCount, DENY);
+        //
+        // vfcount=3 above max of 2: should get a Deny
+        //
+        ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("vfCount", 3);
+        requestAndCheckDecision(requestVfCount, DENY);
+        //
+        // Insert entry into operations history DB - to indicate a successful
+        // VF Module Create.
+        //
+        insertOperationEvent(requestVfCount);
+        //
+        // vfcount=1 between min of 1 and max of 2; MinMax should succeed,
+        // BUT the frequency limiter should fail
+        //
+        ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("vfCount", 1);
+        requestAndCheckDecision(requestVfCount, DENY);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void test5Blacklist() throws CoderException, XacmlApplicationException {
+        LOGGER.info("**************** Running test5Blacklist ****************");
+        //
+        // Load the blacklist policy in with the others.
+        //
+        List<ToscaPolicy> loadedPolicies =
+                TestUtils.loadPolicies("policies/vDNS.policy.guard.blacklist.input.tosca.yaml", service);
+        assertThat(loadedPolicies).hasSize(1);
+        assertThat(loadedPolicies.get(0).getName()).isEqualTo("guard.blacklist.scaleout");
+        //
+        // vfcount=0 below min of 1: should get a Permit because target is NOT blacklisted
+        //
+        requestAndCheckDecision(requestVfCount, PERMIT);
+        //
+        // vfcount=1 between min of 1 and max of 2: change the
+        //
+        ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("target",
+                "the-vfmodule-where-root-is-true");
+        //
+        // vfcount=0 below min of 1: should get a Deny because target IS blacklisted
+        //
+        requestAndCheckDecision(requestVfCount, DENY);
+        //
+        // vfcount=1 between min of 1 and max of 2: change the
+        //
+        ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("target",
+                "another-vfmodule-where-root-is-true");
+        //
+        // vfcount=0 below min of 1: should get a Deny because target IS blacklisted
+        //
+        requestAndCheckDecision(requestVfCount, DENY);
+    }
+
+    @SuppressWarnings("unchecked")
+    private void insertOperationEvent(DecisionRequest request) {
+        //
+        // Get the properties
+        //
+        Map<String, Object> properties = (Map<String, Object>) request.getResource().get("guard");
+        assertThat(properties).isNotNull();
+        //
+        // Add an entry
+        //
+        Dbao newEntry = new Dbao();
+        newEntry.setActor(properties.get("actor").toString());
+        newEntry.setOperation(properties.get("operation").toString());
+        newEntry.setClosedLoopName(properties.get("clname").toString());
+        newEntry.setOutcome("SUCCESS");
+        newEntry.setStarttime(Date.from(Instant.now().minusMillis(20000)));
+        newEntry.setEndtime(Date.from(Instant.now()));
+        newEntry.setRequestId(UUID.randomUUID().toString());
+        newEntry.setTarget(properties.get("target").toString());
+        LOGGER.info("Inserting {}", newEntry);
+        em.getTransaction().begin();
+        em.persist(newEntry);
+        em.getTransaction().commit();
+    }
+
 }