Change RestServerParameters to BusTopicParams
[policy/xacml-pdp.git] / applications / guard / src / test / java / org / onap / policy / xacml / pdp / application / guard / GuardPdpApplicationTest.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2019-2021 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2021 Nordix Foundation.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  * SPDX-License-Identifier: Apache-2.0
21  * ============LICENSE_END=========================================================
22  */
23
24 package org.onap.policy.xacml.pdp.application.guard;
25
26 import static org.assertj.core.api.Assertions.assertThat;
27 import static org.assertj.core.api.Assertions.assertThatCode;
28
29 import com.att.research.xacml.api.Response;
30 import java.io.File;
31 import java.io.FileNotFoundException;
32 import java.io.IOException;
33 import java.sql.Date;
34 import java.time.Instant;
35 import java.time.OffsetDateTime;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Properties;
40 import java.util.ServiceLoader;
41 import java.util.UUID;
42 import javax.persistence.EntityManager;
43 import javax.persistence.Persistence;
44 import org.apache.commons.lang3.tuple.Pair;
45 import org.junit.AfterClass;
46 import org.junit.Before;
47 import org.junit.BeforeClass;
48 import org.junit.ClassRule;
49 import org.junit.FixMethodOrder;
50 import org.junit.Test;
51 import org.junit.rules.TemporaryFolder;
52 import org.junit.runners.MethodSorters;
53 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
54 import org.onap.policy.common.utils.coder.CoderException;
55 import org.onap.policy.common.utils.coder.StandardCoder;
56 import org.onap.policy.common.utils.resources.TextFileUtils;
57 import org.onap.policy.guard.OperationsHistory;
58 import org.onap.policy.models.decisions.concepts.DecisionRequest;
59 import org.onap.policy.models.decisions.concepts.DecisionResponse;
60 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
61 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
62 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationException;
63 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider;
64 import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils;
65 import org.onap.policy.pdp.xacml.application.common.operationshistory.CountRecentOperationsPip;
66 import org.onap.policy.pdp.xacml.xacmltest.TestUtils;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 @FixMethodOrder(MethodSorters.NAME_ASCENDING)
71 public class GuardPdpApplicationTest {
72
73     private static final Logger LOGGER = LoggerFactory.getLogger(GuardPdpApplicationTest.class);
74     private static Properties properties = new Properties();
75     private static File propertiesFile;
76     private static BusTopicParams clientParams = new BusTopicParams();
77     private static XacmlApplicationServiceProvider service;
78     private static DecisionRequest requestVfCount;
79     private static StandardCoder gson = new StandardCoder();
80     private static EntityManager em;
81     private static final String DENY = "Deny";
82     private static final String PERMIT = "Permit";
83
84     @ClassRule
85     public static final TemporaryFolder policyFolder = new TemporaryFolder();
86
87     /**
88      * Copies the xacml.properties and policies files into temporary folder and loads the service provider saving
89      * instance of provider off for other tests to use.
90      */
91     @BeforeClass
92     public static void setup() throws Exception {
93         LOGGER.info("Setting up class");
94         //
95         // Setup our temporary folder
96         //
97         XacmlPolicyUtils.FileCreator myCreator = (String filename) -> policyFolder.newFile(filename);
98         propertiesFile = XacmlPolicyUtils.copyXacmlPropertiesContents("src/test/resources/xacml.properties", properties,
99                 myCreator);
100         //
101         // Load service
102         //
103         ServiceLoader<XacmlApplicationServiceProvider> applicationLoader =
104                 ServiceLoader.load(XacmlApplicationServiceProvider.class);
105         //
106         // Find the guard service application and save for use in all the tests
107         //
108         StringBuilder strDump = new StringBuilder("Loaded applications:" + XacmlPolicyUtils.LINE_SEPARATOR);
109         Iterator<XacmlApplicationServiceProvider> iterator = applicationLoader.iterator();
110         while (iterator.hasNext()) {
111             XacmlApplicationServiceProvider application = iterator.next();
112             //
113             // Is it our service?
114             //
115             if (application instanceof GuardPdpApplication) {
116                 //
117                 // Should be the first and only one
118                 //
119                 assertThat(service).isNull();
120                 service = application;
121             }
122             strDump.append(application.applicationName());
123             strDump.append(" supports ");
124             strDump.append(application.supportedPolicyTypes());
125             strDump.append(XacmlPolicyUtils.LINE_SEPARATOR);
126         }
127         LOGGER.info("{}", strDump);
128         //
129         // Tell it to initialize based on the properties file
130         // we just built for it.
131         //
132         service.initialize(propertiesFile.toPath().getParent(), clientParams);
133         //
134         // Load Decision Requests
135         //
136         requestVfCount =
137                 gson.decode(TextFileUtils.getTextFileAsString("src/test/resources/requests/guard.vfCount.json"),
138                         DecisionRequest.class);
139         //
140         // Create EntityManager for manipulating DB
141         //
142         String persistenceUnit = CountRecentOperationsPip.ISSUER_NAME + ".persistenceunit";
143         em = Persistence
144                 .createEntityManagerFactory(GuardPdpApplicationTest.properties.getProperty(persistenceUnit), properties)
145                 .createEntityManager();
146     }
147
148     /**
149      * Close the entity manager.
150      */
151     @AfterClass
152     public static void cleanup() throws Exception {
153         if (em != null) {
154             em.close();
155         }
156     }
157
158     /**
159      * Clears the database before each test so there are no operations in it.
160      *
161      */
162     @Before
163     public void startClean() throws Exception {
164         em.getTransaction().begin();
165         em.createQuery("DELETE FROM OperationsHistory").executeUpdate();
166         em.getTransaction().commit();
167     }
168
169     /**
170      * Check that decision matches expectation.
171      *
172      * @param expected from the response
173      * @param response received
174      *
175      **/
176     public void checkDecision(String expected, DecisionResponse response) throws CoderException {
177         LOGGER.info("Looking for {} Decision", expected);
178         assertThat(response).isNotNull();
179         assertThat(response.getStatus()).isNotNull();
180         assertThat(response.getStatus()).isEqualTo(expected);
181         //
182         // Dump it out as Json
183         //
184         LOGGER.info(gson.encode(response));
185     }
186
187     /**
188      * Request a decision and check that it matches expectation.
189      *
190      * @param request to send to Xacml PDP
191      * @param expected from the response
192      *
193      **/
194     public void requestAndCheckDecision(DecisionRequest request, String expected) throws CoderException {
195         //
196         // Ask for a decision
197         //
198         Pair<DecisionResponse, Response> decision = service.makeDecision(request, null);
199         //
200         // Check decision
201         //
202         checkDecision(expected, decision.getKey());
203     }
204
205     @Test
206     public void test1Basics() throws CoderException, IOException {
207         LOGGER.info("**************** Running test1Basics ****************");
208         //
209         // Make sure there's an application name
210         //
211         assertThat(service.applicationName()).isNotEmpty();
212         //
213         // Decisions
214         //
215         assertThat(service.actionDecisionsSupported().size()).isEqualTo(1);
216         assertThat(service.actionDecisionsSupported()).contains("guard");
217         //
218         // Ensure it has the supported policy types and
219         // can support the correct policy types.
220         //
221         assertThat(service.supportedPolicyTypes()).isNotEmpty();
222         assertThat(service.supportedPolicyTypes().size()).isEqualTo(5);
223         assertThat(service.canSupportPolicyType(
224                 new ToscaConceptIdentifier("onap.policies.controlloop.guard.common.FrequencyLimiter", "1.0.0")))
225                         .isTrue();
226         assertThat(service.canSupportPolicyType(
227                 new ToscaConceptIdentifier("onap.policies.controlloop.guard.common.FrequencyLimiter", "1.0.1")))
228                         .isFalse();
229         assertThat(service.canSupportPolicyType(
230                 new ToscaConceptIdentifier("onap.policies.controlloop.guard.common.MinMax", "1.0.0"))).isTrue();
231         assertThat(service.canSupportPolicyType(
232                 new ToscaConceptIdentifier("onap.policies.controlloop.guard.common.MinMax", "1.0.1"))).isFalse();
233         assertThat(service.canSupportPolicyType(
234                 new ToscaConceptIdentifier("onap.policies.controlloop.guard.common.Blacklist", "1.0.0"))).isTrue();
235         assertThat(service.canSupportPolicyType(
236                 new ToscaConceptIdentifier("onap.policies.controlloop.guard.common.Blacklist", "1.0.1"))).isFalse();
237         assertThat(service.canSupportPolicyType(new ToscaConceptIdentifier(
238                 "onap.policies.controlloop.guard.coordination.FirstBlocksSecond", "1.0.0"))).isTrue();
239         assertThat(service.canSupportPolicyType(new ToscaConceptIdentifier(
240                 "onap.policies.controlloop.guard.coordination.FirstBlocksSecond", "1.0.1"))).isFalse();
241         assertThat(service.canSupportPolicyType(new ToscaConceptIdentifier("onap.foo", "1.0.1"))).isFalse();
242         assertThat(service.canSupportPolicyType(
243                 new ToscaConceptIdentifier("onap.policies.controlloop.guard.common.Filter", "1.0.0"))).isTrue();
244     }
245
246     @Test
247     public void test2NoPolicies() throws CoderException {
248         LOGGER.info("**************** Running test2NoPolicies ****************");
249         assertThatCode(() -> requestAndCheckDecision(requestVfCount, PERMIT)).doesNotThrowAnyException();
250     }
251
252     @Test
253     public void test3FrequencyLimiter()
254             throws CoderException, FileNotFoundException, IOException, XacmlApplicationException {
255         LOGGER.info("**************** Running test3FrequencyLimiter ****************");
256         //
257         // Now load the vDNS frequency limiter Policy - make sure
258         // the pdp can support it and have it load
259         // into the PDP.
260         //
261         List<ToscaPolicy> loadedPolicies =
262                 TestUtils.loadPolicies("policies/vDNS.policy.guard.frequencylimiter.input.tosca.yaml", service);
263         assertThat(loadedPolicies).hasSize(1);
264         assertThat(loadedPolicies.get(0).getName()).isEqualTo("guard.frequency.scaleout");
265         //
266         // Zero recent actions: should get permit
267         //
268         requestAndCheckDecision(requestVfCount, PERMIT);
269         //
270         // Add entry into operations history DB
271         //
272         insertOperationEvent(requestVfCount);
273         //
274         // Two recent actions, more than specified limit of 2: should get deny
275         //
276         requestAndCheckDecision(requestVfCount, DENY);
277     }
278
279     @SuppressWarnings("unchecked")
280     @Test
281     public void test4MinMax() throws CoderException, FileNotFoundException, IOException, XacmlApplicationException {
282         LOGGER.info("**************** Running test4MinMax ****************");
283         //
284         // Now load the vDNS min max Policy - make sure
285         // the pdp can support it and have it load
286         // into the PDP.
287         //
288         List<ToscaPolicy> loadedPolicies =
289                 TestUtils.loadPolicies("policies/vDNS.policy.guard.minmaxvnfs.input.tosca.yaml", service);
290         assertThat(loadedPolicies).hasSize(1);
291         assertThat(loadedPolicies.get(0).getName()).isEqualTo("guard.minmax.scaleout");
292         //
293         // vfcount=0 below min of 1: should get a Permit
294         //
295         requestAndCheckDecision(requestVfCount, PERMIT);
296         //
297         // vfcount=1 between min of 1 and max of 2: should get a Permit
298         //
299         ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("vfCount", 1);
300         requestAndCheckDecision(requestVfCount, PERMIT);
301         //
302         // vfcount=2 hits the max of 2: should get a Deny
303         //
304         ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("vfCount", 2);
305         requestAndCheckDecision(requestVfCount, DENY);
306         //
307         // vfcount=3 above max of 2: should get a Deny
308         //
309         ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("vfCount", 3);
310         requestAndCheckDecision(requestVfCount, DENY);
311         //
312         // Insert entry into operations history DB - to indicate a successful
313         // VF Module Create.
314         //
315         insertOperationEvent(requestVfCount);
316         //
317         // vfcount=1 between min of 1 and max of 2; MinMax should succeed,
318         // BUT the frequency limiter should fail
319         //
320         ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("vfCount", 1);
321         requestAndCheckDecision(requestVfCount, DENY);
322     }
323
324     @SuppressWarnings("unchecked")
325     @Test
326     public void test5Blacklist() throws CoderException, XacmlApplicationException {
327         LOGGER.info("**************** Running test5Blacklist ****************");
328         //
329         // Load the blacklist policy in with the others.
330         //
331         List<ToscaPolicy> loadedPolicies =
332                 TestUtils.loadPolicies("policies/vDNS.policy.guard.blacklist.input.tosca.yaml", service);
333         assertThat(loadedPolicies).hasSize(1);
334         assertThat(loadedPolicies.get(0).getName()).isEqualTo("guard.blacklist.scaleout");
335         //
336         // vfcount=0 below min of 1: should get a Permit because target is NOT blacklisted
337         //
338         requestAndCheckDecision(requestVfCount, PERMIT);
339         //
340         // vfcount=1 between min of 1 and max of 2: change the
341         //
342         ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("target",
343                 "the-vfmodule-where-root-is-true");
344         //
345         // vfcount=0 below min of 1: should get a Deny because target IS blacklisted
346         //
347         requestAndCheckDecision(requestVfCount, DENY);
348         //
349         // vfcount=1 between min of 1 and max of 2: change the
350         //
351         ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("target",
352                 "another-vfmodule-where-root-is-true");
353         //
354         // vfcount=0 below min of 1: should get a Deny because target IS blacklisted
355         //
356         requestAndCheckDecision(requestVfCount, DENY);
357     }
358
359     @SuppressWarnings("unchecked")
360     @Test
361     public void test6Filters() throws Exception {
362         LOGGER.info("**************** Running test6Filters ****************");
363         //
364         // Re-Load Decision Request - so we can start from scratch
365         //
366         requestVfCount =
367                 gson.decode(TextFileUtils.getTextFileAsString("src/test/resources/requests/guard.vfCount.json"),
368                         DecisionRequest.class);
369         //
370         // Ensure we are a permit to start
371         //
372         requestAndCheckDecision(requestVfCount, PERMIT);
373         //
374         // Load the filter policy in with the others.
375         //
376         List<ToscaPolicy> loadedPolicies =
377                 TestUtils.loadPolicies("src/test/resources/test.policy.guard.filters.yaml", service);
378         assertThat(loadedPolicies).hasSize(2);
379         //
380         // Although the region is blacklisted, the id is not
381         //
382         requestAndCheckDecision(requestVfCount, PERMIT);
383         //
384         // Put in a different vnf id
385         //
386         ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("generic-vnf.vnf-id",
387                 "different-vnf-id-should-be-denied");
388         //
389         // The region is blacklisted, and the id is not allowed
390         //
391         requestAndCheckDecision(requestVfCount, DENY);
392         //
393         // Let's switch to a different region
394         //
395         ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("cloud-region.cloud-region-id",
396                 "RegionTwo");
397         //
398         // The region is whitelisted, and the id is also allowed
399         //
400         requestAndCheckDecision(requestVfCount, PERMIT);
401         //
402         // Put in a blacklisted vnf id
403         //
404         ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("generic-vnf.vnf-id",
405                 "f17face5-69cb-4c88-9e0b-7426db7edddd");
406         //
407         // Although region is whitelisted,  the id is blacklisted
408         //
409         requestAndCheckDecision(requestVfCount, DENY);
410         //
411         // Let's switch to a different region
412         //
413         ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("cloud-region.cloud-region-id",
414                 "RegionThree");
415         //
416         // There is no filter for this region, but the id is still blacklisted
417         //
418         requestAndCheckDecision(requestVfCount, DENY);
419         //
420         // Put in a different vnf id
421         //
422         ((Map<String, Object>) requestVfCount.getResource().get("guard")).put("generic-vnf.vnf-id",
423                 "different-vnf-id-should-be-permitted");
424         //
425         // There is no filter for this region, and the id is not blacklisted
426         //
427         requestAndCheckDecision(requestVfCount, PERMIT);
428     }
429
430     @Test
431     public void test7TimeInRange() throws Exception {
432         LOGGER.info("**************** Running test7TimeInRange ****************");
433         //
434         // Re-Load Decision Request - so we can start from scratch
435         //
436         DecisionRequest requestInRange =
437                 gson.decode(TextFileUtils.getTextFileAsString("src/test/resources/requests/guard.timeinrange.json"),
438                         DecisionRequest.class);
439         //
440         // Load the test policy in with the others.
441         //
442         List<ToscaPolicy> loadedPolicies =
443                 TestUtils.loadPolicies("src/test/resources/test-time-in-range.yaml", service);
444         assertThat(loadedPolicies).hasSize(1);
445         //
446         // Mock what the current date and time is. Set to 12 Noon
447         // We actually do not care about time zone or the date yet, but these are here
448         // for future.
449         //
450         OffsetDateTime offsetDateTime = OffsetDateTime.parse("2020-01-01T12:00:00+05:00");
451         requestInRange.setCurrentDateTime(offsetDateTime);
452         requestInRange.setCurrentDate(offsetDateTime.toLocalDate());
453         requestInRange.setCurrentTime(offsetDateTime.toOffsetTime());
454         requestInRange.setTimeZone(offsetDateTime.getOffset());
455
456         requestAndCheckDecision(requestInRange, PERMIT);
457
458         offsetDateTime = OffsetDateTime.parse("2020-01-01T07:59:59+05:00");
459         requestInRange.setCurrentDateTime(offsetDateTime);
460         requestInRange.setCurrentDate(offsetDateTime.toLocalDate());
461         requestInRange.setCurrentTime(offsetDateTime.toOffsetTime());
462         requestInRange.setTimeZone(offsetDateTime.getOffset());
463
464         requestAndCheckDecision(requestInRange, DENY);
465
466         offsetDateTime = OffsetDateTime.parse("2020-01-01T08:00:00+05:00");
467         requestInRange.setCurrentDateTime(offsetDateTime);
468         requestInRange.setCurrentDate(offsetDateTime.toLocalDate());
469         requestInRange.setCurrentTime(offsetDateTime.toOffsetTime());
470         requestInRange.setTimeZone(offsetDateTime.getOffset());
471
472         requestAndCheckDecision(requestInRange, PERMIT);
473
474         offsetDateTime = OffsetDateTime.parse("2020-01-01T23:59:59+05:00");
475         requestInRange.setCurrentDateTime(offsetDateTime);
476         requestInRange.setCurrentDate(offsetDateTime.toLocalDate());
477         requestInRange.setCurrentTime(offsetDateTime.toOffsetTime());
478         requestInRange.setTimeZone(offsetDateTime.getOffset());
479
480         requestAndCheckDecision(requestInRange, PERMIT);
481
482         offsetDateTime = OffsetDateTime.parse("2020-01-01T00:00:00+05:00");
483         requestInRange.setCurrentDateTime(offsetDateTime);
484         requestInRange.setCurrentDate(offsetDateTime.toLocalDate());
485         requestInRange.setCurrentTime(offsetDateTime.toOffsetTime());
486         requestInRange.setTimeZone(offsetDateTime.getOffset());
487
488         requestAndCheckDecision(requestInRange, DENY);
489     }
490
491     @SuppressWarnings("unchecked")
492     private void insertOperationEvent(DecisionRequest request) {
493         //
494         // Get the properties
495         //
496         Map<String, Object> properties = (Map<String, Object>) request.getResource().get("guard");
497         assertThat(properties).isNotNull();
498         //
499         // Add an entry
500         //
501         OperationsHistory newEntry = new OperationsHistory();
502         newEntry.setActor(properties.get("actor").toString());
503         newEntry.setOperation(properties.get("operation").toString());
504         newEntry.setClosedLoopName(properties.get("clname").toString());
505         newEntry.setOutcome("SUCCESS");
506         newEntry.setStarttime(Date.from(Instant.now().minusMillis(20000)));
507         newEntry.setEndtime(Date.from(Instant.now()));
508         newEntry.setRequestId(UUID.randomUUID().toString());
509         newEntry.setTarget(properties.get("target").toString());
510         LOGGER.info("Inserting {}", newEntry);
511         em.getTransaction().begin();
512         em.persist(newEntry);
513         em.getTransaction().commit();
514     }
515
516 }