Frequent reader, first time question-asker.
I am working on interfacing Perl with a SOAP API provided by one of our vendors (LogRhythm), and seem to have hit a bit of a road-block.
I have been able to interface with the API, and have overcome the Authentication challenges I originally encountered. Now I am having an issue with Name Space tagging in the SOAP Envelope Body when I need to supply parameters to the SOAP call.
The API provided by the Vendor is built around .NET and WCF.
My prototype code currently looks like the following:
use strict;
no strict "refs";
use Data::Dumper;
use IO::Socket::SSL qw (SSL_VERIFY_NONE) ;
use SOAP::Lite;
use Tie::IxHash;
# Username and Password
my $sUID = "<API UserID>";
my $sPWD = "<API Password>";
# WSDL definition URI. Using a cached local copy using file:/... also works
my $LookupService_wsdl = 'https://melcapi01.soc.ipsec.net.au/LogRhythm.API/Services/LookupServiceBasicAuth.svc?singleWsdl';
my $lrns = 'http://www.logrhythm.com/webservices';
# Ensure that a consistent xmlns:soap value is used, without this we get inconsistent results.
$SOAP::Constants::PREFIX_ENV = 'SOAP-ENV';
# Don't validate SSL Certificate while testing
IO::Socket::SSL::set_defaults(SSL_verify_mode => "SSL_VERIFY_NONE");
# Construct the security header
my %authHash;
tie %authHash, "Tie::IxHash";
%authHash = (
Username => SOAP::Data->type( '' => $sUID )->prefix('wsse'),
Password => SOAP::Data->type( '' => $sPWD )->prefix('wsse'),
);
my $wsse = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";
my $securityHeader = SOAP::Header->new(
name => 'Security',
uri => $wsse,
prefix => 'wsse',
value => \SOAP::Data->new(
name => 'UsernameToken',
prefix => 'wsse',
value => \%authHash,
)
);
# Error handling
on_fault => sub { my($soap, $res) = @_;
die ref $res ? $res->faultstring : $soap->transport->status;
};
# Define the SOAP Instance
my $lrapi = SOAP::Lite
-> readable (1)
-> service($LookupService_wsdl)
-> on_action( sub {return $action});
# Set the default Namespace
$lrapi->default_ns($lrns);
# Actually get data from the LookupService
my $result;
# Build up the parameters
my @classificationType = ( SOAP::Data->new(name =>'classificationType', value => 2000));
$result = $lrapi->GetClassificationsByType($securityHeader);
print Dumper $result;
This results in the following SOAP Request:
SOAPAction: "http://www.logrhythm.com/webservices/LookupService/GetClassificationsByType"
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="http://www.logrhythm.com/webservices"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsa10="http://www.w3.org/2005/08/addressing"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy"
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>reporting</wsse:Username>
<wsse:Password>password</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<tns:GetClassificationsByType>
<classificationType xsi:type="xsd:int">2000</classificationType>
<classificationTypeSpecified xsi:type="xsd:boolean">true</classificationTypeSpecified>
</tns:GetClassificationsByType>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Which returns no results. I have done some digging and Request Doctoring using .NET WebService Studio, and have identified that the problem is caused by a missing xmlns identifier on the element in the SOAP Body.
The SOAP Request should look like the following:
SOAPAction: "http://www.logrhythm.com/webservices/LookupService/GetClassificationsByType"
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="http://www.logrhythm.com/webservices"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsa10="http://www.w3.org/2005/08/addressing"
xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata"
xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy"
xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<SOAP-ENV:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>reporting</wsse:Username>
<wsse:Password>password</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<tns:GetClassificationsByType xmlns="http://www.logrhythm.com/webservices">
<classificationType xsi:type="xsd:int">2000</classificationType>
<classificationTypeSpecified xsi:type="xsd:boolean">true</classificationTypeSpecified>
</tns:GetClassificationsByType>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Various support requests indicate that this should be resolved by manually setting the default_ns
on the SOAP::Lite object, however, this is not working for me.
I have gone digging through the source for SOAP::Lite (1.20) to see what is happening, and it seems that the {'_use_default_ns'}
property, which is set to 1
before making the SOAP method call, seems to get reset to 0
during processing of the call.
I have also tried to manually build the SOAP::Lite object with the intent of using the call()
method to get the desired results, however, I think I am running into issues with the number of name spaces that are auto-added as a result of the service()
mechanism I am currently using. If I want to use the call()
method it seems I cannot set up the object using service()
.
Does anyone have any other suggestions I can try as I am at a loss at this point?
Any assistance would be greatly appreciated.
Disclaimer: I am not a programmer/developer, I am a Senior Security Engineer/Consultant/Architect with some programming ability so I apologise in advance if my code is badly structured. This is also my first foray into fiddling with SOAP.