/*
* SoapUI, Copyright (C) 2004-2016 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.impl.wsdl.support.wsa;
import com.eviware.soapui.SoapUI;
import com.eviware.soapui.config.AnonymousTypeConfig;
import com.eviware.soapui.impl.wsdl.WsdlOperation;
import com.eviware.soapui.impl.wsdl.panels.teststeps.support.WsaAssertionConfiguration;
import com.eviware.soapui.impl.wsdl.submit.WsdlMessageExchange;
import com.eviware.soapui.impl.wsdl.support.soap.SoapUtils;
import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlUtils;
import com.eviware.soapui.model.testsuite.AssertionError;
import com.eviware.soapui.model.testsuite.AssertionException;
import com.eviware.soapui.support.StringUtils;
import com.eviware.soapui.support.xml.XmlUtils;
import org.apache.xmlbeans.XmlException;
import org.apache.xmlbeans.XmlObject;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* Validating class for WS Addressing implemented according to WSDL 1.1
* specification
*
* @author dragica.soldo
* @see {@link}http://www.w3.org/TR/2006/WD-ws-addr-wsdl-20060216/#WSDL11MEPS
*/
public class WsaValidator {
WsdlMessageExchange messageExchange;
Element header;
String wsaVersionNameSpace;
StringBuilder cumulativeErrorMsg;
WsaAssertionConfiguration wsaAssertionConfiguration;
public WsaValidator(WsdlMessageExchange messageExchange, WsaAssertionConfiguration wsaAssertionConfiguration) {
this.messageExchange = messageExchange;
this.wsaAssertionConfiguration = wsaAssertionConfiguration;
cumulativeErrorMsg = new StringBuilder();
}
public static String getWsaVersion(XmlObject contentObject, SoapVersion soapVersion) {
String wsaVns = null;
try {
// XmlObject xmlObject = XmlObject.Factory.parse( content );
XmlObject[] envS = contentObject.selectChildren(soapVersion.getEnvelopeQName());
Element envelope = (Element) envS[0].getDomNode();
Element hdr = (Element) SoapUtils.getHeaderElement(contentObject, soapVersion, true).getDomNode();
if (!hdr.hasChildNodes()) {
return null;
}
String wsaNameSpace = XmlUtils.findPrefixForNamespace(hdr, WsaUtils.WS_A_NAMESPACE_200508);
if (wsaNameSpace != null) {
wsaVns = WsaUtils.WS_A_NAMESPACE_200508;
} else {
wsaNameSpace = XmlUtils.findPrefixForNamespace(hdr, WsaUtils.WS_A_NAMESPACE_200408);
if (wsaNameSpace != null) {
wsaVns = WsaUtils.WS_A_NAMESPACE_200408;
} else {
wsaNameSpace = XmlUtils.findPrefixForNamespace(envelope, WsaUtils.WS_A_NAMESPACE_200508);
if (wsaNameSpace != null) {
wsaVns = WsaUtils.WS_A_NAMESPACE_200508;
} else {
wsaNameSpace = XmlUtils.findPrefixForNamespace(envelope, WsaUtils.WS_A_NAMESPACE_200408);
if (wsaNameSpace != null) {
wsaVns = WsaUtils.WS_A_NAMESPACE_200408;
} else {
return null;
}
}
}
}
} catch (XmlException e) {
SoapUI.logError(e);
}
return wsaVns;
}
private void validateWsAddressingCommon(String content) {
if (wsaAssertionConfiguration.isAssertTo()) {
Element toNode;
if (wsaVersionNameSpace != null) {
toNode = XmlUtils.getFirstChildElementNS(header, wsaVersionNameSpace, "To");
parseToNode(toNode);
} else {
toNode = XmlUtils.getFirstChildElementNS(header, WsaUtils.WS_A_NAMESPACE_200508, "To");
if (toNode == null) {
toNode = XmlUtils.getFirstChildElementNS(header, WsaUtils.WS_A_NAMESPACE_200408, "To");
}
parseToNode(toNode);
}
}
// if fault_to is specified check if anonymous allowed; faultTo is never
// asserted
Element faultToNode = XmlUtils.getFirstChildElementNS(header, wsaVersionNameSpace, "FaultTo");
if (faultToNode != null) {
Element addressNode = XmlUtils.getFirstChildElementNS(faultToNode, wsaVersionNameSpace, "Address");
if (addressNode != null) {
String faultToAddressValue = XmlUtils.getElementText(addressNode);
if (!StringUtils.isNullOrEmpty(faultToAddressValue)) {
// check for anonymous
if (AnonymousTypeConfig.PROHIBITED.toString().equals(messageExchange.getOperation().getAnonymous())
&& WsaUtils.isAnonymousAddress(faultToAddressValue, wsaVersionNameSpace)) {
cumulativeErrorMsg
.append("WS-A InvalidAddressingHeader FaultTo , Anonymous addresses are prohibited. ");
} else if (AnonymousTypeConfig.REQUIRED.toString().equals(
((WsdlMessageExchange) messageExchange).getOperation().getAnonymous())
&& !(WsaUtils.isAnonymousAddress(faultToAddressValue, wsaVersionNameSpace) || WsaUtils
.isNoneAddress(faultToAddressValue, wsaVersionNameSpace))) {
cumulativeErrorMsg
.append("WS-A InvalidAddressingHeader FaultTo , Anonymous addresses are required. ");
}
}
}
}
}
private void parseToNode(Element toNode) {
if (toNode == null) {
cumulativeErrorMsg.append("WS-A To property is not specified. ");
} else {
String toAddressValue = XmlUtils.getElementText(toNode);
if (StringUtils.isNullOrEmpty(toAddressValue)) {
cumulativeErrorMsg.append("WS-A To property is empty. ");
} else {
// check for anonymous - in case of mock response to=request.replyTo
if (AnonymousTypeConfig.PROHIBITED.toString().equals(messageExchange.getOperation().getAnonymous())
&& WsaUtils.isAnonymousAddress(toAddressValue, wsaVersionNameSpace)) {
cumulativeErrorMsg.append("WS-A InvalidAddressingHeader To , Anonymous addresses are prohibited. ");
}
}
}
}
public void validateWsAddressingRequest() throws AssertionException, XmlException {
String content = messageExchange.getRequestContent();
SoapVersion soapVersion = messageExchange.getOperation().getInterface().getSoapVersion();
// XmlObject xmlObject = XmlObject.Factory.parse( content );
XmlObject xmlObject = XmlUtils.createXmlObject(content);
header = (Element) SoapUtils.getHeaderElement(xmlObject, soapVersion, true).getDomNode();
wsaVersionNameSpace = getWsaVersion(xmlObject, soapVersion);
// not checking because of possibility of having wsaVersionNamespace
// specified inside property tag itself
// if (wsaVersionNameSpace == null)
// {
// throw new AssertionException( new AssertionError( "WS-A not enabled" )
// );
// }
WsdlOperation operation = messageExchange.getOperation();
if (wsaAssertionConfiguration.isAssertAction()) {
assertProperty("Wsa:Action", "Action");
}
validateWsAddressingCommon(content);
if (operation.isRequestResponse()) {
if (wsaAssertionConfiguration.isAssertMessageId()) {
// MessageId is Mandatory
assertProperty("Wsa:MessageId", "MessageId");
}
if (wsaAssertionConfiguration.isAssertReplyTo()) {
// ReplyTo is Mandatory
Element replyToNode;
String currentTagWsaNs;
if (!StringUtils.isNullOrEmpty(wsaVersionNameSpace)) {
replyToNode = XmlUtils.getFirstChildElementNS(header, wsaVersionNameSpace, "ReplyTo");
parseReplyToNode(replyToNode, wsaVersionNameSpace);
} else {
replyToNode = XmlUtils.getFirstChildElementNS(header, WsaUtils.WS_A_NAMESPACE_200508, "ReplyTo");
currentTagWsaNs = WsaUtils.WS_A_NAMESPACE_200508;
if (replyToNode == null) {
replyToNode = XmlUtils.getFirstChildElementNS(header, WsaUtils.WS_A_NAMESPACE_200408, "ReplyTo");
currentTagWsaNs = WsaUtils.WS_A_NAMESPACE_200408;
}
parseReplyToNode(replyToNode, currentTagWsaNs);
}
}
}
String cumulativeError = cumulativeErrorMsg.toString();
if (!StringUtils.isNullOrEmpty(cumulativeError)) {
throw new AssertionException(new AssertionError(cumulativeError));
}
}
private void parseReplyToNode(Element replyToNode, String wsaNsStr) {
if (replyToNode == null) {
cumulativeErrorMsg.append("WS-A ReplyTo property is not specified. ");
} else {
Element addressNode = XmlUtils.getFirstChildElementNS(replyToNode, wsaNsStr, "Address");
if (addressNode == null) {
cumulativeErrorMsg.append("WS-A ReplyTo Address property is not specified. ");
} else {
String replyToAddressValue = XmlUtils.getElementText(addressNode);
if (StringUtils.isNullOrEmpty(replyToAddressValue)) {
cumulativeErrorMsg.append("WS-A ReplyTo Address property is empty. ");
} else {
// check for anonymous
if (AnonymousTypeConfig.PROHIBITED.toString().equals(
((WsdlMessageExchange) messageExchange).getOperation().getAnonymous())
&& WsaUtils.isAnonymousAddress(replyToAddressValue, wsaNsStr)) {
cumulativeErrorMsg
.append("WS-A InvalidAddressingHeader ReplyTo , Anonymous addresses are prohibited. ");
} else if (AnonymousTypeConfig.REQUIRED.toString().equals(
((WsdlMessageExchange) messageExchange).getOperation().getAnonymous())
&& !(WsaUtils.isAnonymousAddress(replyToAddressValue, wsaNsStr) || WsaUtils.isNoneAddress(
replyToAddressValue, wsaNsStr))) {
cumulativeErrorMsg
.append("WS-A InvalidAddressingHeader ReplyTo , Anonymous addresses are required. ");
}
}
}
}
}
public void validateWsAddressingResponse() throws AssertionException, XmlException {
String content = messageExchange.getResponseContent();
SoapVersion soapVersion = messageExchange.getOperation().getInterface().getSoapVersion();
// XmlObject requestXmlObject = XmlObject.Factory.parse(
// messageExchange.getRequestContent() );
XmlObject requestXmlObject = XmlUtils.createXmlObject(messageExchange.getRequestContent());
// XmlObject xmlObject = XmlObject.Factory.parse( content );
XmlObject xmlObject = XmlUtils.createXmlObject(content);
header = (Element) SoapUtils.getHeaderElement(xmlObject, soapVersion, true).getDomNode();
wsaVersionNameSpace = getWsaVersion(xmlObject, soapVersion);
if (wsaAssertionConfiguration.isAssertAction()) {
String defaultWsdlAction = WsdlUtils.getDefaultWsaAction(messageExchange.getOperation(), true);
// Wsa:Action is assertion text, not property tag
assertProperty("Wsa:Action", "Action", defaultWsdlAction);
}
validateWsAddressingCommon(content);
if (wsaAssertionConfiguration.isAssertRelatesTo()) {
// RelatesTo is Mandatory
Element relatesToNode;
if (!StringUtils.isNullOrEmpty(wsaVersionNameSpace)) {
relatesToNode = XmlUtils.getFirstChildElementNS(header, wsaVersionNameSpace, "RelatesTo");
parseRelatesToNode(soapVersion, requestXmlObject, relatesToNode);
} else {
relatesToNode = XmlUtils.getFirstChildElementNS(header, WsaUtils.WS_A_NAMESPACE_200508, "RelatesTo");
if (relatesToNode == null) {
relatesToNode = XmlUtils.getFirstChildElementNS(header, WsaUtils.WS_A_NAMESPACE_200408, "RelatesTo");
}
parseRelatesToNode(soapVersion, requestXmlObject, relatesToNode);
}
}
// if fault_to is specified check if anonymous allowed
Element replyToNode = XmlUtils.getFirstChildElementNS(header, wsaVersionNameSpace, "ReplyTo");
if (replyToNode != null) {
Element addressNode = XmlUtils.getFirstChildElementNS(replyToNode, wsaVersionNameSpace, "Address");
if (addressNode != null) {
String replyToAddressValue = XmlUtils.getElementText(addressNode);
if (!StringUtils.isNullOrEmpty(replyToAddressValue)) {
// check for anonymous
if (AnonymousTypeConfig.PROHIBITED.toString().equals(
((WsdlMessageExchange) messageExchange).getOperation().getAnonymous())
&& WsaUtils.isAnonymousAddress(replyToAddressValue, wsaVersionNameSpace)) {
cumulativeErrorMsg
.append("WS-A InvalidAddressingHeader ReplyTo , Anonymous addresses are prohibited. ");
} else if (AnonymousTypeConfig.REQUIRED.toString().equals(
((WsdlMessageExchange) messageExchange).getOperation().getAnonymous())
&& !(WsaUtils.isAnonymousAddress(replyToAddressValue, wsaVersionNameSpace) || WsaUtils
.isNoneAddress(replyToAddressValue, wsaVersionNameSpace))) {
cumulativeErrorMsg
.append("WS-A InvalidAddressingHeader ReplyTo , Anonymous addresses are required. ");
}
}
}
}
if (wsaAssertionConfiguration.isAssertReplyToRefParams()) {
// check if request ReplyTo ReferenceParameters are included in
// response
NodeList requestReplyToRefProps = WsdlUtils.getRequestReplyToRefProps(messageExchange,
getWsaVersion(requestXmlObject, soapVersion));
for (int i = 0; i < requestReplyToRefProps.getLength(); i++) {
Node refProp = requestReplyToRefProps.item(i);
String refPropName = refProp.getNodeName();
NodeList existingResponseRefs = XmlUtils.getChildElementsByTagName(header, refPropName);
if (existingResponseRefs != null && existingResponseRefs.getLength() > 0) {
// TODO check if tag is well formed: wsa:IsReferenceParameter
continue;
} else {
cumulativeErrorMsg.append("Response does not have request ReferenceProperty " + refPropName + ". ");
}
}
}
if (wsaAssertionConfiguration.isAssertFaultToRefParams()) {
// check if request FaultTo ReferenceParameters are included in
// response
NodeList requestFaultToRefProps = WsdlUtils.getRequestFaultToRefProps(messageExchange,
getWsaVersion(requestXmlObject, soapVersion));
for (int i = 0; i < requestFaultToRefProps.getLength(); i++) {
Node refProp = requestFaultToRefProps.item(i);
String refPropName = refProp.getNodeName();
NodeList existingResponseRefs = XmlUtils.getChildElementsByTagName(header, refPropName);
if (existingResponseRefs != null && existingResponseRefs.getLength() > 0) {
continue;
} else {
cumulativeErrorMsg.append("Response does not have request ReferenceProperty " + refPropName + ". ");
}
}
}
String cumulativeError = cumulativeErrorMsg.toString();
if (!StringUtils.isNullOrEmpty(cumulativeError)) {
throw new AssertionException(new AssertionError(cumulativeError));
}
}
private void parseRelatesToNode(SoapVersion soapVersion, XmlObject requestXmlObject, Element relatesToNode) {
if (relatesToNode == null) {
cumulativeErrorMsg.append("WS-A RelatesTo property is not specified. ");
} else {
String relatesToValue = XmlUtils.getElementText(relatesToNode);
if (StringUtils.isNullOrEmpty(relatesToValue)) {
cumulativeErrorMsg.append("WS-A RelatesTo property is empty. ");
} else {
String requestMsgId = WsdlUtils.getRequestWsaMessageId(messageExchange,
getWsaVersion(requestXmlObject, soapVersion));
if (!relatesToValue.equals(requestMsgId)) {
cumulativeErrorMsg.append("WS-A RelatesTo property is not equal to request wsa:MessageId. ");
}
}
/*
* When absent, the implied value of this attribute is
* "http://www.w3.org/2005/08/addressing/reply". question is does it
* have to be present as 'reply' ???
*/
// String relationshipType =
// relatesToNode.getAttribute("RelationshipType");
// if (StringUtils.isNullOrEmpty(relationshipType))
// {
// relationshipType =
// relatesToNode.getAttributeNS(WsaUtils.WS_A_VERSION_200508,
// "RelationshipType");
// if (StringUtils.isNullOrEmpty(relationshipType))
// {
// relationshipType =
// relatesToNode.getAttributeNS(WsaUtils.WS_A_VERSION_200408,
// "RelationshipType");
// if (StringUtils.isNullOrEmpty(relationshipType))
// {
// cumulativeErrorMsg.append("WS-A RelationshipType is not specified. ");
// }
// }
// }
}
}
/*
* asserts specific property
*/
private void assertProperty(String propertyName, String wsaProperty, String expectedValue) {
Element propertyNode;
if (wsaVersionNameSpace != null) {
propertyNode = XmlUtils.getFirstChildElementNS(header, wsaVersionNameSpace, wsaProperty);
parsePropertyNode(propertyName, propertyNode, expectedValue);
} else {
propertyNode = XmlUtils.getFirstChildElementNS(header, WsaUtils.WS_A_NAMESPACE_200508, wsaProperty);
if (propertyNode == null) {
propertyNode = XmlUtils.getFirstChildElementNS(header, WsaUtils.WS_A_NAMESPACE_200408, wsaProperty);
}
parsePropertyNode(propertyName, propertyNode, expectedValue);
}
}
private void assertProperty(String propertyName, String wsaProperty) {
assertProperty(propertyName, wsaProperty, null);
}
private void parsePropertyNode(String propertyName, Element propertyNode, String expectedValue) {
if (propertyNode == null) {
cumulativeErrorMsg.append(propertyName + " property is not specified. ");
} else {
String actionValue = XmlUtils.getElementText(propertyNode);
if (StringUtils.isNullOrEmpty(actionValue)) {
cumulativeErrorMsg.append(propertyName + " property is empty. ");
} else if (!StringUtils.isNullOrEmpty(expectedValue)) {
if (!actionValue.equals(expectedValue)) {
cumulativeErrorMsg.append(propertyName + " expecting [" + expectedValue + "], actual value is ["
+ actionValue + "].");
}
}
}
}
}