Remove duplicated code
[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-2020 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  * SPDX-License-Identifier: Apache-2.0
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.policy.pdp.xacml.application.common.std;
24
25 import com.att.research.xacml.api.Request;
26 import com.att.research.xacml.api.Response;
27 import com.att.research.xacml.api.pdp.PDPEngine;
28 import com.att.research.xacml.api.pdp.PDPEngineFactory;
29 import com.att.research.xacml.api.pdp.PDPException;
30 import com.att.research.xacml.util.FactoryException;
31 import java.io.IOException;
32 import java.nio.charset.StandardCharsets;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.util.ArrayList;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Properties;
41 import lombok.Getter;
42 import org.apache.commons.lang3.tuple.Pair;
43 import org.onap.policy.common.endpoints.parameters.RestServerParameters;
44 import org.onap.policy.models.decisions.concepts.DecisionRequest;
45 import org.onap.policy.models.decisions.concepts.DecisionResponse;
46 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
47 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
48 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyConversionException;
49 import org.onap.policy.pdp.xacml.application.common.ToscaPolicyTranslator;
50 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationException;
51 import org.onap.policy.pdp.xacml.application.common.XacmlApplicationServiceProvider;
52 import org.onap.policy.pdp.xacml.application.common.XacmlPolicyUtils;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55
56 public abstract class StdXacmlApplicationServiceProvider implements XacmlApplicationServiceProvider {
57
58     private static final Logger LOGGER = LoggerFactory.getLogger(StdXacmlApplicationServiceProvider.class);
59
60     protected String applicationName = "Please Override";
61     protected List<String> actions = Collections.emptyList();
62     protected List<ToscaPolicyTypeIdentifier> supportedPolicyTypes = new ArrayList<>();
63
64     private Path pathForData = null;
65     @Getter
66     private RestServerParameters policyApiParameters;
67     private Properties pdpProperties = null;
68     private PDPEngine pdpEngine = null;
69     private Map<ToscaPolicy, Path> mapLoadedPolicies = new HashMap<>();
70
71     public StdXacmlApplicationServiceProvider() {
72         super();
73     }
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, RestServerParameters 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<ToscaPolicyTypeIdentifier> supportedPolicyTypes() {
114         return supportedPolicyTypes;
115     }
116
117     @Override
118     public boolean canSupportPolicyType(ToscaPolicyTypeIdentifier 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             Properties newProperties = this.getProperties();
136             //
137             // Construct the filename
138             //
139             Path 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         Properties 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             DecisionResponse 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         Response 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         Properties 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             PDPEngineFactory 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 }