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