[POLICY-73] replace openecomp for policy-engine
[policy/engine.git] / ONAP-PDP-REST / src / main / java / org / onap / policy / pdp / rest / api / services / PDPServices.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP-PDP-REST
4  * ================================================================================
5  * Copyright (C) 2017 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  * ============LICENSE_END=========================================================
19  */
20 package org.onap.policy.pdp.rest.api.services;
21
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.io.StringWriter;
27 import java.net.MalformedURLException;
28 import java.util.Collection;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.Map;
32 import java.util.Properties;
33 import java.util.UUID;
34
35 import javax.json.Json;
36 import javax.json.JsonReader;
37 import javax.xml.XMLConstants;
38 import javax.xml.parsers.DocumentBuilder;
39 import javax.xml.parsers.DocumentBuilderFactory;
40 import javax.xml.transform.Transformer;
41 import javax.xml.transform.TransformerFactory;
42 import javax.xml.transform.dom.DOMSource;
43 import javax.xml.transform.stream.StreamResult;
44
45 import org.apache.commons.io.IOUtils;
46 import org.onap.policy.api.PolicyConfigStatus;
47 import org.onap.policy.api.PolicyDecision;
48 import org.onap.policy.api.PolicyException;
49 import org.onap.policy.api.PolicyResponseStatus;
50 import org.onap.policy.api.PolicyType;
51 import org.onap.policy.common.logging.flexlogger.FlexLogger;
52 import org.onap.policy.common.logging.flexlogger.Logger;
53 import org.onap.policy.pdp.rest.XACMLPdpServlet;
54 import org.onap.policy.pdp.rest.api.models.PDPResponse;
55 import org.onap.policy.rest.XACMLRestProperties;
56 import org.onap.policy.std.Matches;
57 import org.onap.policy.xacml.api.XACMLErrorConstants;
58 import org.w3c.dom.Document;
59
60 import com.att.research.xacml.api.Advice;
61 import com.att.research.xacml.api.AttributeAssignment;
62 import com.att.research.xacml.api.Decision;
63 import com.att.research.xacml.api.Obligation;
64 import com.att.research.xacml.api.Request;
65 import com.att.research.xacml.api.Response;
66 import com.att.research.xacml.api.Result;
67 import com.att.research.xacml.api.pdp.PDPEngine;
68 import com.att.research.xacml.std.json.JSONRequest;
69 import com.att.research.xacml.std.json.JSONResponse;
70 import com.att.research.xacml.util.XACMLProperties;
71
72 public class PDPServices {
73     private static final Logger LOGGER = FlexLogger.getLogger(PDPServices.class.getName());
74     // Change the default Priority value here. 
75     private static final int DEFAULT_PRIORITY = 9999;
76     private boolean unique = false;
77     private Boolean decide = false;
78     private Request rainydayRequest = null;
79     
80     public Collection<PDPResponse> generateRequest(String jsonString, UUID requestID, boolean unique, boolean decide) throws PolicyException{
81         this.unique = unique;
82         this.decide = decide;
83         Collection<PDPResponse> results = null;
84         Response response = null;
85         // Create Request. We need XACML API here.
86         try {
87             Request request = JSONRequest.load(jsonString);
88             // Assign a rainy day treatment request to parse the decided treatment
89                 if (jsonString.contains("BB_ID")) {
90                         rainydayRequest = request;
91                 }
92             // Call the PDP
93             LOGGER.info("--- Generating Request: ---\n" + JSONRequest.toString(request));
94             response = callPDP(request, requestID);
95         } catch (Exception e) {
96             LOGGER.error(XACMLErrorConstants.ERROR_SCHEMA_INVALID + e);
97             PDPResponse pdpResponse = new PDPResponse();
98             results = new HashSet<>();
99             pdpResponse.setPolicyConfigMessage("Unable to Call PDP. Error with the URL");
100             pdpResponse.setPolicyConfigStatus(PolicyConfigStatus.CONFIG_NOT_FOUND);
101             pdpResponse.setPolicyResponseStatus(PolicyResponseStatus.NO_ACTION_REQUIRED);
102             results.add(pdpResponse);
103             throw new PolicyException(e);
104         }
105         if (response != null) {
106             results = checkResponse(response);
107         } else {
108             LOGGER.info("No Response Received from PDP");
109             PDPResponse pdpResponse = new PDPResponse();
110             results = new HashSet<>();
111             pdpResponse.setPolicyConfigMessage("No Response Received");
112             pdpResponse.setPolicyConfigStatus(PolicyConfigStatus.CONFIG_NOT_FOUND);
113             pdpResponse.setPolicyResponseStatus(PolicyResponseStatus.NO_ACTION_REQUIRED);
114             results.add(pdpResponse);
115         }
116         return results;
117     }
118
119     private Collection<PDPResponse> checkResponse(Response response) throws PolicyException{
120         String pdpConfigLocation = null;
121         Collection<PDPResponse> combinedResult = new HashSet<>();
122         int priority = DEFAULT_PRIORITY;
123         Map<Integer, PDPResponse> uniqueResult = new HashMap<>();
124         for (Result result : response.getResults()) {
125             if (!result.getDecision().equals(Decision.PERMIT)) {
126                 LOGGER.info("Decision not a Permit. "  + result.getDecision().toString());
127                 PDPResponse pdpResponse = new PDPResponse();
128                 if (decide) {
129                         String indeterminatePropValue = XACMLProperties.getProperty("decision.inStringdeterminate.response");
130                         if(result.getDecision().equals(Decision.INDETERMINATE)&& indeterminatePropValue != null){
131                                 if("PERMIT".equalsIgnoreCase(indeterminatePropValue)){
132                                         pdpResponse.setDecision(PolicyDecision.PERMIT);
133                                 }else{
134                                         pdpResponse.setDecision(PolicyDecision.DENY);
135                                 }
136                         }else{
137                                 pdpResponse.setDecision(PolicyDecision.DENY);
138                         }
139                     for(Advice advice: result.getAssociatedAdvice()){
140                         for(AttributeAssignment attribute: advice.getAttributeAssignments()){
141                             pdpResponse.setDetails(attribute.getAttributeValue().getValue().toString());
142                             break;
143                         }
144                     }
145                     combinedResult.add(pdpResponse);
146                     return combinedResult;
147                 }
148                 pdpResponse.setStatus(XACMLErrorConstants.ERROR_DATA_ISSUE + "Incorrect Params passed: Decision not a Permit.",PolicyResponseStatus.NO_ACTION_REQUIRED,PolicyConfigStatus.CONFIG_NOT_FOUND);
149                 combinedResult.add(pdpResponse);
150                 return combinedResult;
151             } else {
152                 if (decide) {
153                     // check for Decision for decision based calls.
154                     PDPResponse pdpResponse = new PDPResponse();
155                     pdpResponse.setDecision(PolicyDecision.PERMIT);
156                     
157                         //if this is a Rainy Day treatment decision we need to get the selected treatment
158                         if(rainydayRequest!=null){
159                                 pdpResponse.setDetails(getRainyDayTreatment(result));
160                         } else {
161                         pdpResponse.setDetails("Decision Permit. OK!");
162                         }
163                     combinedResult.add(pdpResponse);
164                     return combinedResult;
165                 }
166                 if (!result.getAssociatedAdvice().isEmpty()) {
167                     // Configurations should be in advice. 
168                     // Also PDP took actions could be here.
169                     for (Advice advice : result.getAssociatedAdvice()) {
170                         int config = 0, uri = 0;
171                         String configURL = null;
172                         String policyName = null;
173                         String policyVersion = null;
174                         Matches match = new Matches();
175                         Map<String, String> matchingConditions = new HashMap<>();
176                         Map<String, String> configAttributes = new HashMap<>();
177                         Map<String, String> responseAttributes = new HashMap<>();
178                         Map<String, String> actionTaken = new HashMap<>();
179                         PDPResponse pdpResponse = new PDPResponse();
180                         Map<String, String> adviseAttributes = new HashMap<>();
181                         for (AttributeAssignment attribute : advice.getAttributeAssignments()) {
182                             adviseAttributes.put(attribute.getAttributeId().stringValue(), attribute.getAttributeValue().getValue().toString());
183                             if ("CONFIGURATION".equalsIgnoreCase(attribute.getAttributeValue().getValue().toString())) {
184                                 config++;
185                             } else if (attribute.getDataTypeId().stringValue().endsWith("anyURI")) {
186                                 uri++;
187                                 if (uri == 1) {
188                                     configURL = attribute.getAttributeValue().getValue().toString();
189                                     pdpConfigLocation = configURL.replace("$URL", XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_WEBAPPS));
190                                 } else {
191                                     if (!("PDP".equalsIgnoreCase(attribute.getIssuer()))) {
192                                         throw new PolicyException(XACMLErrorConstants.ERROR_DATA_ISSUE + "Error having multiple URI in the Policy");
193                                     }
194                                 }
195                             } else if ("PolicyName".equalsIgnoreCase(attribute.getAttributeId().stringValue())) {
196                                 policyName = attribute.getAttributeValue().getValue().toString();
197                             } else if ("VersionNumber".equalsIgnoreCase(attribute.getAttributeId().stringValue())) {
198                                 policyVersion = attribute.getAttributeValue().getValue().toString();
199                             } else if ("Priority".equalsIgnoreCase(attribute.getAttributeId().stringValue())){
200                                 try{
201                                     priority = Integer.parseInt(attribute.getAttributeValue().getValue().toString());
202                                 } catch(Exception e){
203                                     LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE+ "Unable to Parse Integer for Priority. Setting to default value",e);
204                                     priority = DEFAULT_PRIORITY;
205                                 }
206                             } else if (attribute.getAttributeId().stringValue().startsWith("matching")) {
207                                 matchingConditions.put(attribute.getAttributeId().stringValue()
208                                         .replaceFirst("(matching).", ""),attribute.getAttributeValue().getValue().toString());
209                                 if ("ONAPName".equals(attribute.getAttributeId().stringValue()
210                                         .replaceFirst("(matching).", ""))) {
211                                     match.setOnapName(attribute.getAttributeValue().getValue().toString());
212                                 } else if ("ConfigName".equals(attribute.getAttributeId().stringValue()
213                                         .replaceFirst("(matching).", ""))) {
214                                     match.setConfigName(attribute.getAttributeValue().getValue().toString());
215                                 } else {
216                                     configAttributes.put(attribute.getAttributeId().stringValue()
217                                             .replaceFirst("(matching).", ""),attribute.getAttributeValue().getValue().toString());
218                                 }
219                             } else if (attribute.getAttributeId().stringValue().startsWith("key:")) {
220                                 responseAttributes.put(attribute.getAttributeId().stringValue().replaceFirst("(key).", ""),
221                                         attribute.getAttributeValue().getValue().toString());
222                             } else if (attribute.getAttributeId().stringValue().startsWith("controller:")) {
223                                 responseAttributes.put("$"+ attribute.getAttributeId().stringValue(),
224                                         attribute.getAttributeValue().getValue().toString());
225                             } else if (attribute.getAttributeId().stringValue().startsWith("dependencies:")) {
226                                 responseAttributes.put("$dependency$",
227                                         attribute.getAttributeValue().getValue().toString());
228                             }
229                         }
230                         if (!configAttributes.isEmpty()) {
231                             match.setConfigAttributes(configAttributes);
232                         }
233                         if ((config == 1) && (uri == 1)) {
234                             // If there is a configuration.
235                             try {
236                                 LOGGER.debug("Configuration Call to : " + configURL);
237                                 pdpResponse = configCall(pdpConfigLocation);
238                             } catch (Exception e) {
239                                 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW+ e);
240                                 pdpResponse.setStatus("Error in Calling the Configuration URL "+ e,
241                                                 PolicyResponseStatus.NO_ACTION_REQUIRED,
242                                                 PolicyConfigStatus.CONFIG_NOT_FOUND);
243                             }
244                             pdpResponse.setPolicyName(policyName);
245                             pdpResponse.setPolicyVersion(policyVersion);
246                             pdpResponse.setMatchingConditions(matchingConditions);
247                             pdpResponse.setResponseAttributes(responseAttributes);
248                             if(!unique){
249                                 combinedResult.add(pdpResponse);
250                             }else{
251                                 if(!uniqueResult.isEmpty()){
252                                     if(uniqueResult.containsKey(priority)){
253                                         // Not any more unique, check the matching conditions size
254                                         int oldSize = uniqueResult.get(priority).getMatchingConditions().size();
255                                         int newSize = matchingConditions.size();
256                                         if(oldSize < newSize){
257                                             uniqueResult.put(priority, pdpResponse);
258                                         }else if(oldSize == newSize){
259                                             pdpResponse = new PDPResponse();
260                                             pdpResponse.setStatus("Two/more Policies have Same Priority and matching conditions, Please correct your policies.",
261                                                     PolicyResponseStatus.NO_ACTION_REQUIRED,
262                                                     PolicyConfigStatus.CONFIG_NOT_FOUND);
263                                             combinedResult.add(pdpResponse);
264                                             unique = false;
265                                             return combinedResult;
266                                         }
267                                     }else{
268                                         uniqueResult.put(priority, pdpResponse);
269                                     }
270                                 }else{
271                                     uniqueResult.put(priority, pdpResponse);
272                                 }
273                             }
274                         } else {
275                             // Else it is Action Taken.
276                             LOGGER.info("Action Taken by PDP. ");
277                             actionTaken.putAll(adviseAttributes);
278                             pdpResponse.setActionTaken(actionTaken);
279                             pdpResponse.setPolicyResponseStatus(PolicyResponseStatus.ACTION_TAKEN);
280                             pdpResponse.setPolicyResponseMessage("Action Taken by the PDP");
281                             combinedResult.add(pdpResponse);
282                         }
283                     }
284                 }
285                 if (!result.getObligations().isEmpty()) {
286                     // Obligation actions
287                     // Action advised should be in obligations.
288                     for (Obligation obligation : result.getObligations()) {
289                         Map<String, String> actionAdvised = new HashMap<>();
290                         PDPResponse pdpResponse = new PDPResponse();
291                         for (AttributeAssignment attribute : obligation.getAttributeAssignments()) {
292                             actionAdvised.put(attribute.getAttributeId().stringValue(),
293                                     attribute.getAttributeValue().getValue().toString());
294                         }
295                         pdpResponse.setActionAdvised(actionAdvised);
296                         pdpResponse.setPolicyResponseStatus(PolicyResponseStatus.ACTION_ADVISED);
297                         pdpResponse.setPolicyResponseMessage("Action has been Advised ");
298                         combinedResult.add(pdpResponse);
299                     }
300                 }
301             }
302         }
303         if(unique){
304             // Select Unique policy. 
305             int minNum = DEFAULT_PRIORITY;
306             for(int num: uniqueResult.keySet()){
307                 if(num < minNum){
308                     minNum = num;
309                 }
310             }
311             combinedResult.add(uniqueResult.get(minNum));
312             // Turn off Unique
313             unique = false;
314         }
315         
316         return combinedResult;
317     }
318     
319     private String getRainyDayTreatment(Result result) {
320         String treatment = null;
321         if (rainydayRequest!=null&& !result.getAssociatedAdvice().isEmpty()) {
322                 // Get the desired treatment for requested errorCode from the Advice
323                 for (Advice advice : result.getAssociatedAdvice()) {
324                         Map<String, String> adviseAttributes = new HashMap<>();
325                         for (AttributeAssignment attribute : advice.getAttributeAssignments()) {
326                                 adviseAttributes.put(attribute.getAttributeId().stringValue(), attribute.getAttributeValue().getValue().toString());
327                                 if ("treatment".equalsIgnoreCase(attribute.getAttributeId().stringValue())){
328                                         treatment = attribute.getAttributeValue().getValue().toString();
329                                 }
330                         }   
331                 }
332         }
333         return treatment;
334     }
335
336     private PDPResponse configCall(String pdpConfigLocation) throws Exception{
337         PDPResponse pdpResponse = new PDPResponse();
338         if(pdpConfigLocation.contains("/")){
339             pdpConfigLocation = pdpConfigLocation.replace("/", File.separator);
340         }
341         InputStream inputStream = null;
342         try {
343             inputStream = new FileInputStream(new File(pdpConfigLocation));
344             try {
345                 if (pdpConfigLocation.endsWith("json")) {
346                     pdpResponse.setType(PolicyType.JSON);
347                     JsonReader jsonReader = Json.createReader(inputStream);
348                     pdpResponse.setConfig(jsonReader.readObject().toString());
349                     jsonReader.close();
350                 } else if (pdpConfigLocation.endsWith("xml")) {
351                     pdpResponse.setType(PolicyType.XML);
352                     DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
353                     dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
354                     dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
355                     DocumentBuilder db = null;
356                     try {
357                         db = dbf.newDocumentBuilder();
358                         Document document = db.parse(inputStream);
359                         DOMSource domSource = new DOMSource(document);
360                         StringWriter writer = new StringWriter();
361                         StreamResult result = new StreamResult(writer);
362                         TransformerFactory tf = TransformerFactory.newInstance();
363                         Transformer transformer;
364                         transformer = tf.newTransformer();
365                         transformer.transform(domSource, result);
366                         pdpResponse.setConfig(writer.toString());
367                     } catch (Exception e) {
368                         LOGGER.error(XACMLErrorConstants.ERROR_SCHEMA_INVALID+ e);
369                         throw new Exception(XACMLErrorConstants.ERROR_SCHEMA_INVALID+ "Unable to parse the XML config", e);
370                     }
371                 } else if (pdpConfigLocation.endsWith("properties")) {
372                     pdpResponse.setType(PolicyType.PROPERTIES);
373                     Properties configProp = new Properties();
374                     configProp.load(inputStream);
375                     Map<String, String> propVal = new HashMap<>();
376                     for(String name: configProp.stringPropertyNames()) {
377                         propVal.put(name, configProp.getProperty(name));
378                     }
379                     pdpResponse.setProperty(propVal);
380                 } else if (pdpConfigLocation.endsWith("txt")) {
381                     pdpResponse.setType(PolicyType.OTHER);
382                     String other = IOUtils.toString(inputStream);
383                     IOUtils.closeQuietly(inputStream);
384                     pdpResponse.setConfig(other);
385                 } else {
386                     LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Config Not Found");
387                     pdpResponse.setPolicyConfigStatus(PolicyConfigStatus.CONFIG_NOT_FOUND);
388                     pdpResponse.setPolicyConfigMessage("Illegal form of Configuration Type Found.");
389                     inputStream.close();
390                     return pdpResponse;
391                 }
392                 LOGGER.info("config Retrieved " + pdpConfigLocation);
393                 pdpResponse.setStatus("Config Retrieved! ",
394                         PolicyResponseStatus.NO_ACTION_REQUIRED,
395                         PolicyConfigStatus.CONFIG_RETRIEVED);
396                 return pdpResponse;
397             } catch (IOException e) {
398                 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + e);
399                 throw new Exception(XACMLErrorConstants.ERROR_PROCESS_FLOW +
400                         "Cannot open a connection to the configURL", e);
401             }
402         } catch (MalformedURLException e) {
403             LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + e);
404             throw new Exception(XACMLErrorConstants.ERROR_DATA_ISSUE + "Error in ConfigURL", e);
405         }finally{
406                 if(inputStream != null){
407                 inputStream.close();
408                 }
409         }
410     }
411
412     private Response callPDP(Request request,
413             UUID requestID) throws Exception{
414         Response response = null;
415         // Get the PDPEngine
416         if (requestID == null) {
417             requestID = UUID.randomUUID();
418             LOGGER.debug("No request ID provided, sending generated ID: " + requestID.toString());
419         } else {
420             LOGGER.debug("Using provided request ID: " + requestID.toString());
421         }
422         PDPEngine pdpEngine = XACMLPdpServlet.getPDPEngine();
423         if (pdpEngine == null) {
424             String message = "PDPEngine not loaded.";
425             LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + message);
426             return response;
427         }
428         // call the PDPEngine to decide and give the response on the Request.
429         try {
430             response = pdpEngine.decide(request);
431             LOGGER.info("Response from the PDP is: \n" + JSONResponse.toString(response));
432         } catch (Exception e) {
433             LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + e);
434             return null;
435         }
436         return response;
437     }
438
439 }