2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
20 package org.onap.policy.pdp.rest.api.services;
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;
32 import java.util.Properties;
33 import java.util.UUID;
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;
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;
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;
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;
82 public Collection<PDPResponse> generateRequest(String jsonString, UUID requestID, boolean unique, boolean decide) throws PolicyException{
85 Collection<PDPResponse> results = null;
86 Response response = null;
87 // Create Request. We need XACML API here.
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;
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);
107 if (response != null) {
108 results = checkResponse(response);
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);
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();
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);
136 pdpResponse.setDecision(PolicyDecision.DENY);
139 pdpResponse.setDecision(PolicyDecision.DENY);
141 for(Advice advice: result.getAssociatedAdvice()){
142 for(AttributeAssignment attribute: advice.getAttributeAssignments()){
143 pdpResponse.setDetails(attribute.getAttributeValue().getValue().toString());
147 combinedResult.add(pdpResponse);
148 return combinedResult;
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;
155 // check for Decision for decision based calls.
156 PDPResponse pdpResponse = new PDPResponse();
157 pdpResponse.setDecision(PolicyDecision.PERMIT);
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));
163 pdpResponse.setDetails("Decision Permit. OK!");
165 combinedResult.add(pdpResponse);
166 return combinedResult;
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())) {
187 } else if (attribute.getDataTypeId().stringValue().endsWith("anyURI")) {
190 configURL = attribute.getAttributeValue().getValue().toString();
191 pdpConfigLocation = configURL.replace("$URL", XACMLProperties.getProperty(XACMLRestProperties.PROP_PDP_WEBAPPS));
193 if (!("PDP".equalsIgnoreCase(attribute.getIssuer()))) {
194 throw new PolicyException(XACMLErrorConstants.ERROR_DATA_ISSUE + "Error having multiple URI in the Policy");
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())){
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;
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());
218 configAttributes.put(attribute.getAttributeId().stringValue()
219 .replaceFirst("(matching).", ""),attribute.getAttributeValue().getValue().toString());
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());
232 if (!configAttributes.isEmpty()) {
233 match.setConfigAttributes(configAttributes);
235 if ((config == 1) && (uri == 1)) {
236 // If there is a configuration.
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);
246 pdpResponse.setPolicyName(policyName);
247 pdpResponse.setPolicyVersion(policyVersion);
248 pdpResponse.setMatchingConditions(matchingConditions);
249 pdpResponse.setResponseAttributes(responseAttributes);
251 combinedResult.add(pdpResponse);
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);
267 return combinedResult;
270 uniqueResult.put(priority, pdpResponse);
273 uniqueResult.put(priority, pdpResponse);
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);
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());
297 pdpResponse.setActionAdvised(actionAdvised);
298 pdpResponse.setPolicyResponseStatus(PolicyResponseStatus.ACTION_ADVISED);
299 pdpResponse.setPolicyResponseMessage("Action has been Advised ");
300 combinedResult.add(pdpResponse);
306 // Select Unique policy.
307 int minNum = DEFAULT_PRIORITY;
308 for(int num: uniqueResult.keySet()){
313 combinedResult.add(uniqueResult.get(minNum));
318 return combinedResult;
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();
338 private PDPResponse configCall(String pdpConfigLocation) throws PDPException, IOException{
339 PDPResponse pdpResponse = new PDPResponse();
340 if(pdpConfigLocation.contains("/")){
341 pdpConfigLocation = pdpConfigLocation.replace("/", File.separator);
343 InputStream inputStream = null;
345 inputStream = new FileInputStream(new File(pdpConfigLocation));
347 if (pdpConfigLocation.endsWith("json")) {
348 pdpResponse.setType(PolicyType.JSON);
349 JsonReader jsonReader = Json.createReader(inputStream);
350 pdpResponse.setConfig(jsonReader.readObject().toString());
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;
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);
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));
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);
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.");
394 LOGGER.info("config Retrieved " + pdpConfigLocation);
395 pdpResponse.setStatus("Config Retrieved! ",
396 PolicyResponseStatus.NO_ACTION_REQUIRED,
397 PolicyConfigStatus.CONFIG_RETRIEVED);
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);
404 } catch (FileNotFoundException e) {
405 LOGGER.error(XACMLErrorConstants.ERROR_DATA_ISSUE + e);
406 throw new PDPException(XACMLErrorConstants.ERROR_DATA_ISSUE + "Error in ConfigURL", e);
408 if(inputStream != null){
414 private Response callPDP(Request request, UUID requestID){
415 Response response = null;
417 if (requestID == null) {
418 requestID = UUID.randomUUID();
419 LOGGER.debug("No request ID provided, sending generated ID: " + requestID.toString());
421 LOGGER.debug("Using provided request ID: " + requestID.toString());
423 PDPEngine pdpEngine = XACMLPdpServlet.getPDPEngine();
424 if (pdpEngine == null) {
425 String message = "PDPEngine not loaded.";
426 LOGGER.error(XACMLErrorConstants.ERROR_PROCESS_FLOW + message);
429 // call the PDPEngine to decide and give the response on the Request.
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);