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