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