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