Use lombok in xacml-pdp
[policy/xacml-pdp.git] / applications / common / src / main / java / org / onap / policy / pdp / xacml / application / common / std / StdXacmlApplicationServiceProvider.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.pdp.xacml.application.common.std;
25
26 import com.att.research.xacml.api.Request;
27 import com.att.research.xacml.api.Response;
28 import com.att.research.xacml.api.pdp.PDPEngine;
29 import com.att.research.xacml.api.pdp.PDPEngineFactory;
30 import com.att.research.xacml.api.pdp.PDPException;
31 import com.att.research.xacml.util.FactoryException;
32 import java.io.IOException;
33 import java.nio.charset.StandardCharsets;
34 import java.nio.file.Files;
35 import java.nio.file.Path;
36 import java.util.ArrayList;
37 import java.util.Collections;
38 import java.util.HashMap;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Properties;
42 import lombok.AccessLevel;
43 import lombok.Getter;
44 import lombok.NoArgsConstructor;
45 import org.apache.commons.lang3.tuple.Pair;
46 import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
47 import org.onap.policy.models.decisions.concepts.DecisionRequest;
48 import org.onap.policy.models.decisions.concepts.DecisionResponse;
49 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
50 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
51 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
52 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
53 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationException;
54 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider;
55 import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58
59 @NoArgsConstructor(access = AccessLevel.PROTECTED)
60 public abstract class StdXacmlApplicationServiceProvider implements XacmlApplicationServiceProvider {
61
62     private static final Logger LOGGER = LoggerFactory.getLogger(StdXacmlApplicationServiceProvider.class);
63
64     protected String applicationName = "Please Override";
65     protected List<String> actions = Collections.emptyList();
66     protected List<ToscaConceptIdentifier> supportedPolicyTypes = new ArrayList<>();
67
68     private Path pathForData = null;
69     @Getter
70     private BusTopicParams policyApiParameters;
71     private Properties pdpProperties = null;
72     private PDPEngine pdpEngine = null;
73     private Map<ToscaPolicy, Path> mapLoadedPolicies = new HashMap<>();
74
75     @Override
76     public String applicationName() {
77         return applicationName;
78     }
79
80     @Override
81     public List<String> actionDecisionsSupported() {
82         return actions;
83     }
84
85     @Override
86     public void initialize(Path pathForData, BusTopicParams policyApiParameters)
87             throws XacmlApplicationException {
88         //
89         // Save our path
90         //
91         this.pathForData = pathForData;
92         LOGGER.info("New Path is {}", this.pathForData.toAbsolutePath());
93         //
94         // Save our params
95         //
96         this.policyApiParameters = policyApiParameters;
97         //
98         // Look for and load the properties object
99         //
100         try {
101             pdpProperties = XacmlPolicyUtils.loadXacmlProperties(XacmlPolicyUtils.getPropertiesPath(pathForData));
102             LOGGER.info("{}", pdpProperties);
103         } catch (IOException e) {
104             throw new XacmlApplicationException("Failed to load " + XacmlPolicyUtils.XACML_PROPERTY_FILE, e);
105         }
106         //
107         // Create an engine
108         //
109         createEngine(pdpProperties);
110     }
111
112     @Override
113     public List<ToscaConceptIdentifier> supportedPolicyTypes() {
114         return supportedPolicyTypes;
115     }
116
117     @Override
118     public boolean canSupportPolicyType(ToscaConceptIdentifier policyTypeId) {
119         throw new UnsupportedOperationException("Please override and implement canSupportPolicyType");
120     }
121
122     @Override
123     public synchronized void loadPolicy(ToscaPolicy toscaPolicy) throws XacmlApplicationException {
124         try {
125             //
126             // Convert the policies first
127             //
128             Object xacmlPolicy = this.getTranslator(toscaPolicy.getType()).convertPolicy(toscaPolicy);
129             if (xacmlPolicy == null) {
130                 throw new ToscaPolicyConversionException("Failed to convert policy");
131             }
132             //
133             // Create a copy of the properties object
134             //
135             var newProperties = this.getProperties();
136             //
137             // Construct the filename
138             //
139             var refPath = XacmlPolicyUtils.constructUniquePolicyFilename(xacmlPolicy, this.getDataPath());
140             //
141             // Write the policy to disk
142             // Maybe check for an error
143             //
144             if (XacmlPolicyUtils.writePolicyFile(refPath, xacmlPolicy) == null) {
145                 throw new ToscaPolicyConversionException("Unable to writePolicyFile");
146             }
147             if (LOGGER.isInfoEnabled()) {
148                 LOGGER.info("Xacml Policy is {}{}", XacmlPolicyUtils.LINE_SEPARATOR,
149                     new String(Files.readAllBytes(refPath), StandardCharsets.UTF_8));
150             }
151             //
152             // Add root policy to properties object
153             //
154             XacmlPolicyUtils.addRootPolicy(newProperties, refPath);
155             //
156             // Write the properties to disk
157             //
158             XacmlPolicyUtils.storeXacmlProperties(newProperties,
159                     XacmlPolicyUtils.getPropertiesPath(this.getDataPath()));
160             //
161             // Reload the engine
162             //
163             this.createEngine(newProperties);
164             //
165             // Save the properties
166             //
167             this.pdpProperties = newProperties;
168             //
169             // Save in our map
170             //
171             this.mapLoadedPolicies.put(toscaPolicy, refPath);
172         } catch (IOException | ToscaPolicyConversionException e) {
173             throw new XacmlApplicationException("loadPolicy failed", e);
174         }
175     }
176
177     @Override
178     public synchronized boolean unloadPolicy(ToscaPolicy toscaPolicy) throws XacmlApplicationException {
179         //
180         // Find it in our map
181         //
182         Path refPolicy = this.mapLoadedPolicies.get(toscaPolicy);
183         if (refPolicy == null) {
184             LOGGER.error("Failed to find ToscaPolicy {} in our map size {}", toscaPolicy.getMetadata(),
185                     this.mapLoadedPolicies.size());
186             return false;
187         }
188         //
189         // Create a copy of the properties object
190         //
191         var newProperties = this.getProperties();
192         //
193         // Remove it from the properties
194         //
195         XacmlPolicyUtils.removeRootPolicy(newProperties, refPolicy);
196         //
197         // We can delete the file
198         //
199         try {
200             Files.delete(refPolicy);
201         } catch (IOException e) {
202             LOGGER.error("Failed to delete policy {} from disk {}", toscaPolicy.getMetadata(),
203                     refPolicy.toAbsolutePath(), e);
204         }
205         //
206         // Write the properties to disk
207         //
208         try {
209             XacmlPolicyUtils.storeXacmlProperties(newProperties,
210                     XacmlPolicyUtils.getPropertiesPath(this.getDataPath()));
211         } catch (IOException e) {
212             LOGGER.error("Failed to save the properties to disk {}", newProperties, e);
213         }
214         //
215         // Reload the engine
216         //
217         this.createEngine(newProperties);
218         //
219         // Save the properties
220         //
221         this.pdpProperties = newProperties;
222         //
223         // Save in our map
224         //
225         if (this.mapLoadedPolicies.remove(toscaPolicy) == null) {
226             LOGGER.error("Failed to remove toscaPolicy {} from internal map size {}", toscaPolicy.getMetadata(),
227                     this.mapLoadedPolicies.size());
228         }
229         //
230         // Not sure if any of the errors above warrant returning false
231         //
232         return true;
233     }
234
235     @Override
236     public Pair<DecisionResponse, Response> makeDecision(DecisionRequest request,
237             Map<String, String[]> requestQueryParams) {
238         //
239         // Convert to a XacmlRequest
240         //
241         Request xacmlRequest;
242         try {
243             xacmlRequest = this.getTranslator().convertRequest(request);
244         } catch (ToscaPolicyConversionException e) {
245             LOGGER.error("Failed to convert request", e);
246             var response = new DecisionResponse();
247             response.setStatus("error");
248             response.setMessage(e.getLocalizedMessage());
249             return Pair.of(response, null);
250         }
251         //
252         // Now get a decision
253         //
254         var xacmlResponse = this.xacmlDecision(xacmlRequest);
255         //
256         // Convert to a DecisionResponse
257         //
258         return Pair.of(this.getTranslator().convertResponse(xacmlResponse), xacmlResponse);
259     }
260
261     protected abstract ToscaPolicyTranslator getTranslator(String type);
262
263     protected ToscaPolicyTranslator getTranslator() {
264         return this.getTranslator("");
265     }
266
267     protected synchronized PDPEngine getEngine() {
268         return this.pdpEngine;
269     }
270
271     protected synchronized Properties getProperties() {
272         var newProperties = new Properties();
273         newProperties.putAll(pdpProperties);
274         return newProperties;
275     }
276
277     protected synchronized Path getDataPath() {
278         return pathForData;
279     }
280
281     /**
282      * Creates an instance of PDP engine given the Properties object.
283      */
284     protected synchronized void createEngine(Properties properties) {
285         //
286         // Now initialize the XACML PDP Engine
287         //
288         try {
289             var factory = getPdpEngineFactory();
290             PDPEngine engine = factory.newEngine(properties);
291             if (engine != null) {
292                 //
293                 // If there is a previous engine have it shutdown.
294                 //
295                 this.destroyEngine();
296                 //
297                 // Save it off
298                 //
299                 this.pdpEngine = engine;
300             }
301         } catch (FactoryException e) {
302             LOGGER.error("Failed to create XACML PDP Engine", e);
303         }
304     }
305
306     protected synchronized void destroyEngine() {
307         if (this.pdpEngine == null) {
308             return;
309         }
310         try {
311             this.pdpEngine.shutdown();
312         } catch (Exception e) {
313             LOGGER.warn("Exception thrown when destroying XACML PDP engine.", e);
314         }
315         this.pdpEngine = null;
316     }
317
318     /**
319      * Make a decision call.
320      *
321      * @param request Incoming request object
322      * @return Response object
323      */
324     protected synchronized Response xacmlDecision(Request request) {
325         //
326         // This is what we need to return
327         //
328         Response response = null;
329         //
330         // Track some timing
331         //
332         long timeStart = System.currentTimeMillis();
333         try {
334             response = this.pdpEngine.decide(request);
335         } catch (PDPException e) {
336             LOGGER.error("Xacml PDP Engine decide failed", e);
337         } finally {
338             //
339             // Track the end of timing
340             //
341             long timeEnd = System.currentTimeMillis();
342             LOGGER.info("Elapsed Time: {}ms", (timeEnd - timeStart));
343         }
344         return response;
345     }
346
347     // these may be overridden by junit tests
348
349     protected PDPEngineFactory getPdpEngineFactory() throws FactoryException {
350         return PDPEngineFactory.newInstance();
351     }
352 }