Fixing XML parsers security bug
[so.git] / bpmn / so-bpmn-tasks / src / main / java / org / onap / so / bpmn / infrastructure / sdnc / tasks / SDNCRequestTasks.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP - SO
4  * ================================================================================
5  * Copyright (C) 2017 - 2018 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
21 package org.onap.so.bpmn.infrastructure.sdnc.tasks;
22
23 import java.io.StringReader;
24 import java.io.StringWriter;
25 import javax.xml.XMLConstants;
26 import javax.xml.parsers.DocumentBuilder;
27 import javax.xml.parsers.DocumentBuilderFactory;
28 import javax.xml.transform.Transformer;
29 import javax.xml.transform.TransformerFactory;
30 import javax.xml.transform.dom.DOMSource;
31 import javax.xml.transform.stream.StreamResult;
32 import javax.xml.xpath.XPath;
33 import javax.xml.xpath.XPathFactory;
34 import org.apache.commons.lang3.StringUtils;
35 import org.camunda.bpm.engine.delegate.DelegateExecution;
36 import org.onap.logging.filter.base.ONAPComponents;
37 import org.onap.so.bpmn.infrastructure.sdnc.exceptions.SDNCErrorResponseException;
38 import org.onap.so.client.exception.BadResponseException;
39 import org.onap.so.client.exception.ExceptionBuilder;
40 import org.onap.so.client.exception.MapperException;
41 import org.onap.so.client.sdnc.SDNCClient;
42 import org.onap.so.client.sdnc.beans.SDNCRequest;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
45 import org.springframework.beans.factory.annotation.Autowired;
46 import org.springframework.stereotype.Component;
47 import org.springframework.web.client.HttpClientErrorException;
48 import org.w3c.dom.Document;
49 import org.xml.sax.InputSource;
50 import com.jayway.jsonpath.JsonPath;
51 import com.jayway.jsonpath.PathNotFoundException;
52 import net.sf.saxon.lib.NamespaceConstant;
53 import net.sf.saxon.xpath.XPathFactoryImpl;
54
55 @Component
56 public class SDNCRequestTasks {
57
58     private static final String NET_SF_SAXON_XPATH_IMPL = "net.sf.saxon.xpath.XPathFactoryImpl";
59
60     private static final String XPATH_FACTORY_PROPERTY_NAME =
61             "javax.xml.xpath.XPathFactory:" + NamespaceConstant.OBJECT_MODEL_SAXON;
62
63     private static final Logger logger = LoggerFactory.getLogger(SDNCRequestTasks.class);
64
65     private static final String SDNC_REQUEST = "SDNCRequest";
66     private static final String MESSAGE = "_MESSAGE";
67     private static final String CORRELATOR = "_CORRELATOR";
68     protected static final String IS_CALLBACK_COMPLETED = "isCallbackCompleted";
69     protected static final String SDNC_SUCCESS = "200";
70
71     @Autowired
72     private ExceptionBuilder exceptionBuilder;
73
74     @Autowired
75     private SDNCClient sdncClient;
76
77     public void createCorrelationVariables(DelegateExecution execution) {
78         SDNCRequest request = (SDNCRequest) execution.getVariable(SDNC_REQUEST);
79         execution.setVariable(request.getCorrelationName() + CORRELATOR, request.getCorrelationValue());
80         execution.setVariable("sdncTimeout", request.getTimeOut());
81     }
82
83     public void callSDNC(DelegateExecution execution) {
84         SDNCRequest request = (SDNCRequest) execution.getVariable(SDNC_REQUEST);
85         try {
86             String response = sdncClient.post(request.getSDNCPayload(), request.getTopology());
87             String finalMessageIndicator = JsonPath.read(response, "$.output.ack-final-indicator");
88             execution.setVariable("isSDNCCompleted", convertIndicatorToBoolean(finalMessageIndicator));
89         } catch (PathNotFoundException e) {
90             logger.error("Error Parsing SDNC Response. Could not find read final ack indicator from JSON.", e);
91             exceptionBuilder.buildAndThrowWorkflowException(execution, 7000,
92                     "Recieved invalid response from SDNC, unable to read message content.", ONAPComponents.SO);
93         } catch (MapperException e) {
94             logger.error("Failed to map SDNC object to JSON prior to POST.", e);
95             exceptionBuilder.buildAndThrowWorkflowException(execution, 7000,
96                     "Failed to map SDNC object to JSON prior to POST.", ONAPComponents.SO);
97         } catch (BadResponseException e) {
98             logger.error("Did not receive a successful response from SDNC.", e);
99             exceptionBuilder.buildAndThrowWorkflowException(execution, 7000, e.getLocalizedMessage(),
100                     ONAPComponents.SDNC);
101         } catch (HttpClientErrorException e) {
102             logger.error("HttpClientErrorException: 404 Not Found, Failed to contact SDNC", e);
103             exceptionBuilder.buildAndThrowWorkflowException(execution, 7000, "SDNC cannot be contacted.",
104                     ONAPComponents.SO);
105         }
106     }
107
108     public void processCallback(DelegateExecution execution) {
109         try {
110             SDNCRequest request = (SDNCRequest) execution.getVariable(SDNC_REQUEST);
111             String asyncRequest = (String) execution.getVariable(request.getCorrelationName() + MESSAGE);
112
113             DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
114             dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
115             dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
116             dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
117             DocumentBuilder db = dbf.newDocumentBuilder();
118             Document doc = db.parse(new InputSource(new StringReader(asyncRequest)));
119
120             String finalMessageIndicator = getXmlElement(doc, "/input/ack-final-indicator");
121
122             boolean isCallbackCompleted = convertIndicatorToBoolean(finalMessageIndicator);
123             execution.setVariable(IS_CALLBACK_COMPLETED, isCallbackCompleted);
124             if (isCallbackCompleted) {
125                 String responseCode = getXmlElement(doc, "/input/response-code");
126                 if (!SDNC_SUCCESS.equalsIgnoreCase(responseCode)) {
127                     String responseMessage;
128                     try {
129                         responseMessage = getXmlElement(doc, "/input/response-message");
130                     } catch (Exception e) {
131                         responseMessage = "Unknown Error in SDNC";
132                     }
133                     throw new SDNCErrorResponseException(responseMessage);
134                 }
135             }
136         } catch (SDNCErrorResponseException e) {
137             logger.error("SDNC error response - " + e.getMessage());
138             exceptionBuilder.buildAndThrowWorkflowException(execution, 7000, e.getMessage(), ONAPComponents.SDNC);
139         } catch (Exception e) {
140             logger.error("Error processing SDNC callback", e);
141             exceptionBuilder.buildAndThrowWorkflowException(execution, 7000, "Error processing SDNC callback",
142                     ONAPComponents.SO);
143         }
144     }
145
146     public void handleTimeOutException(DelegateExecution execution) {
147         exceptionBuilder.buildAndThrowWorkflowException(execution, 7000,
148                 "Error timed out waiting on SDNC Async-Response", ONAPComponents.SO);
149     }
150
151     protected boolean convertIndicatorToBoolean(String finalMessageIndicator) {
152         return "Y".equals(finalMessageIndicator);
153     }
154
155     protected String getXmlElement(final Document doc, final String exp) throws Exception {
156         final TransformerFactory factory = TransformerFactory.newInstance();
157         factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, StringUtils.EMPTY);
158         factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_STYLESHEET, StringUtils.EMPTY);
159
160         final Transformer transformer = factory.newTransformer();
161         final StringWriter writer = new StringWriter();
162         transformer.transform(new DOMSource(doc), new StreamResult(writer));
163         logger.debug(writer.getBuffer().toString());
164
165         System.setProperty(XPATH_FACTORY_PROPERTY_NAME, NET_SF_SAXON_XPATH_IMPL);
166         final XPathFactory xPathFactory = XPathFactoryImpl.newInstance(NamespaceConstant.OBJECT_MODEL_SAXON);
167         final XPath xPath = xPathFactory.newXPath();
168         final String result = xPath.evaluate(exp, doc);
169         if (result == null || result.isEmpty()) {
170             throw new Exception("XPath Failed to find element expression: " + exp);
171         }
172         return result;
173     }
174 }