2 * ============LICENSE_START=======================================================
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.policy.pdp.rest.api.services;
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;
33 import java.util.Properties;
34 import java.util.UUID;
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;
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;
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;
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;
83 public Collection<PDPResponse> generateRequest(String jsonString, UUID requestID, boolean unique, boolean decide) throws PolicyException{
86 Collection<PDPResponse> results = null;
87 Response response = null;
88 // Create Request. We need XACML API here.
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;
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);
108 if (response != null) {
109 results = checkResponse(response);
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);
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();
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);
137 pdpResponse.setDecision(PolicyDecision.DENY);
140 pdpResponse.setDecision(PolicyDecision.DENY);
142 for(Advice advice: result.getAssociatedAdvice()){
143 for(AttributeAssignment attribute: advice.getAttributeAssignments()){
144 pdpResponse.setDetails(attribute.getAttributeValue().getValue().toString());
148 combinedResult.add(pdpResponse);
149 return combinedResult;
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;
156 // check for Decision for decision based calls.
157 PDPResponse pdpResponse = new PDPResponse();
158 pdpResponse.setDecision(PolicyDecision.PERMIT);
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));
164 pdpResponse.setDetails("Decision Permit. OK!");
166 combinedResult.add(pdpResponse);
167 return combinedResult;
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())) {
188 } else if (attribute.getDataTypeId().stringValue().endsWith("anyURI")) {
191 configURL = attribute.getAttributeValue().getValue().toString();
192 pdpConfigLocation = configURL.replace("$URL", XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_WEBAPPS));
194 if (!("PDP".equalsIgnoreCase(attribute.getIssuer()))) {
195 throw new PolicyException(XACMLErrorConstants.ERROR_DATA_ISSUE + "Error having multiple URI in the Policy");
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())){
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;
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());
219 configAttributes.put(attribute.getAttributeId().stringValue()
220 .replaceFirst("(matching).", ""),attribute.getAttributeValue().getValue().toString());
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());
233 if (!configAttributes.isEmpty()) {
234 match.setConfigAttributes(configAttributes);
236 if ((config == 1) && (uri == 1)) {
237 // If there is a configuration.
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);
247 pdpResponse.setPolicyName(policyName);
248 pdpResponse.setPolicyVersion(policyVersion);
249 pdpResponse.setMatchingConditions(matchingConditions);
250 pdpResponse.setResponseAttributes(responseAttributes);
252 combinedResult.add(pdpResponse);
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);
268 return combinedResult;
271 uniqueResult.put(priority, pdpResponse);
274 uniqueResult.put(priority, pdpResponse);
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);
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());
298 pdpResponse.setActionAdvised(actionAdvised);
299 pdpResponse.setPolicyResponseStatus(PolicyResponseStatus.ACTION_ADVISED);
300 pdpResponse.setPolicyResponseMessage("Action has been Advised ");
301 combinedResult.add(pdpResponse);
307 // Select Unique policy.
308 int minNum = DEFAULT_PRIORITY;
309 for(int num: uniqueResult.keySet()){
314 combinedResult.add(uniqueResult.get(minNum));
319 return combinedResult;
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();
339 private PDPResponse configCall(String pdpConfigLocation) throws PDPException, IOException{
340 PDPResponse pdpResponse = new PDPResponse();
341 if(pdpConfigLocation.contains("/")){
342 pdpConfigLocation = pdpConfigLocation.replace("/", File.separator);
344 InputStream inputStream = null;
345 JsonReader jsonReader = null;
347 inputStream = new FileInputStream(new File(pdpConfigLocation));
349 if (pdpConfigLocation.endsWith("json")) {
350 pdpResponse.setType(PolicyType.JSON);
351 jsonReader = Json.createReader(inputStream);
352 pdpResponse.setConfig(jsonReader.readObject().toString());
354 } else if (pdpConfigLocation.endsWith("xml")) {
355 pdpResponse.setType(PolicyType.XML);
356 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
357 dbf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
358 dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
359 DocumentBuilder db = null;
361 db = dbf.newDocumentBuilder();
362 Document document = db.parse(inputStream);
363 DOMSource domSource = new DOMSource(document);
364 StringWriter writer = new StringWriter();
365 StreamResult result = new StreamResult(writer);
366 TransformerFactory tf = TransformerFactory.newInstance();
367 Transformer transformer;
368 transformer = tf.newTransformer();
369 transformer.transform(domSource, result);
370 pdpResponse.setConfig(writer.toString());
371 } catch (Exception e) {
372 LOGGER.error(XACMLErrorConstants.ERROR_SCHEMA_INVALID+ e);
373 throw new PDPException(XACMLErrorConstants.ERROR_SCHEMA_INVALID+ "Unable to parse the XML config", e);
375 } else if (pdpConfigLocation.endsWith("properties")) {
376 pdpResponse.setType(PolicyType.PROPERTIES);
377 Properties configProp = new Properties();
378 configProp.load(inputStream);
379 Map<String, String> propVal = new HashMap<>();
380 for(String name: configProp.stringPropertyNames()) {
381 propVal.put(name, configProp.getProperty(name));
383 pdpResponse.setProperty(propVal);
384 } else if (pdpConfigLocation.endsWith("txt")) {
385 pdpResponse.setType(PolicyType.OTHER);
386 String other = IOUtils.toString(inputStream);
387 IOUtils.closeQuietly(inputStream);
388 pdpResponse.setConfig(other);
390 LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + "Config Not Found");
391 pdpResponse.setPolicyConfigStatus(PolicyConfigStatus.CONFIG_NOT_FOUND);
392 pdpResponse.setPolicyConfigMessage("Illegal form of Configuration Type Found.");
396 LOGGER.info("config Retrieved " + pdpConfigLocation);
397 pdpResponse.setStatus("Config Retrieved! ",
398 PolicyResponseStatus.NO_ACTION_REQUIRED,
399 PolicyConfigStatus.CONFIG_RETRIEVED);
401 } catch (IOException | ParserConfigurationException e) {
402 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + e);
403 throw new PDPException(XACMLErrorConstants.ERROR_PROCESS_FLOW +
404 "Cannot open a connection to the configURL", e);
406 if(jsonReader != null) {
409 } catch (Exception e) {
410 LOGGER.error("Exception Occured while closing the JsonReader"+e);
414 } catch (FileNotFoundException e) {
415 LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + e);
416 throw new PDPException(XACMLErrorConstants.ERROR_DATA_ISSUE + "Error in ConfigURL", e);
418 if(inputStream != null){
424 private Response callPDP(Request request, UUID requestID){
425 Response response = null;
427 if (requestID == null) {
428 requestID = UUID.randomUUID();
429 LOGGER.debug("No request ID provided, sending generated ID: " + requestID.toString());
431 LOGGER.debug("Using provided request ID: " + requestID.toString());
433 PDPEngine pdpEngine = XACMLPdpServlet.getPDPEngine();
434 if (pdpEngine == null) {
435 String message = "PDPEngine not loaded.";
436 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + message);
439 // call the PDPEngine to decide and give the response on the Request.
441 response = pdpEngine.decide(request);
442 LOGGER.info("Response from the PDP is: \n" + JSONResponse.toString(response));
443 } catch (Exception e) {
444 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + e);