/*
* soapUI, copyright (C) 2004-2011 eviware.com
*
* soapUI is free software; you can redistribute it and/or modify it under the
* terms of version 2.1 of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details at gnu.org.
*/
package com.eviware.soapui.impl.wsdl.support.wsa;
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;
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;
/**
* 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 + "]." );
}
}
}
}
}