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