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