Can't get Savon to form the right XML - ruby

I've used SOAP before but many, many years ago and try to stay clear if I can help it.
Short story...
Using "Soap Client" OS X app I can get the response I wish.
If I use client.request :foo_web_serivce, :language_count, :body => { :foo_app_id => "...", :session_id => "..." } then I get a Status 200 but my results are -1 as I believe the app_id is not recognised.
If I try to replicate the Soap Client call (<LanguageCount>) rather than Savon's call (<foo_web_service:LanguageCount>) then I get an Status 500.
Please advise and thanks in advance.
Long Story...
Using SOAP Client I can see that the correct request XML is:
User-Agent: Mac OS X; WebServicesCore (357)
Content-Type: text/xml
Host: redacted
Soapaction: http://tempuri.org/redacted/LanguageCount
<?xml version="1.0" encoding="utf-8"?>
<SOAP-ENV:Envelope
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Body>
<LanguageCount xmlns="http://tempuri.org/">
<fooAppID xsi:type="xsd:string">12345678-1234-1234-1234-123456789123</fooAppID>
<sessionID xsi:type="xsd:string">12345678-1234-1234-1234-123456789123</sessionID>
</LanguageCount>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
However, when I try to do it in the console like so:
ruby-1.8.7-p302 > client = Savon::Client.new do |wsdl, http, wsse|
ruby-1.8.7-p302 > wsdl.document = "http://FooService.cloudapp.net/FooService.svc?wsdl"
ruby-1.8.7-p302 ?> end
=> #<Savon::Client:0x1055e2dc8 #http=#<HTTPI::Request:0x1055e2bc0>, #wsse=#<Savon::WSSE:0x1055e2b98>, #wsdl=#<Savon::WSDL::Document:0x1055e2c10 #request=#<HTTPI::Request:0x1055e2bc0>, #document="http://FooService.cloudapp.net/FooService.svc?wsdl">>
ruby-1.8.7-p302 >
ruby-1.8.7-p302 > client.request :language_count, :body => {:Foo_app_id => "12345678-1234-1234-1234-123456789123", :session_id => "12345678-1234-1234-1234-123456789123"}
Retrieving WSDL from: http://FooService.cloudapp.net/FooService.svc?wsdl
HTTPI executes HTTP GET using the net_http adapter
SOAP request: http://Fooservice.cloudapp.net/FooService.svc
SOAPAction: "LanguageCount", Content-Type: text/xml;charset=UTF-8
<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:wsdl="http://tempuri.org/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Body><LanguageCount><FooAppId>12345678-1234-1234-1234-123456789123</FooAppId><sessionId>12345678-1234-1234-1234-123456789123</sessionId></LanguageCount></env:Body></env:Envelope>
HTTPI executes HTTP POST using the net_http adapter
SOAP response (status 500):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><s:Fault><faultcode xmlns:a="http://schemas.microsoft.com/ws/2005/05/addressing/none">a:ActionNotSupported</faultcode><faultstring xml:lang="en-US">The message with Action 'LanguageCount' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).</faultstring></s:Fault></s:Body></s:Envelope>
Savon::SOAP::Fault: (a:ActionNotSupported) The message with Action 'LanguageCount' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None).
But when I use the namespace of foo_web_service, I get:
ruby-1.8.7-p302 > client = Savon::Client.new do |wsdl, http, wsse| ruby-1.8.7-p302 > wsdl.document = "http://FooService.cloudapp.net/FooService.svc?wsdl" ruby-1.8.7-p302 ?> end =>
#<Savon::Client:0x105882498 #http=#<HTTPI::Request:0x105882358>, #wsse=#<Savon::WSSE:0x105882330>, #wsdl=#<Savon::WSDL::Document:0x105882380 #request=#<HTTPI::Request:0x105882358>, #document="http://FooService.cloudapp.net/FooService.svc?wsdl">> ruby-1.8.7-p302 > ruby-1.8.7-p302 > client.request :Foo_web_service, :language_count, :body => {:Foo_app_id
=> "12345678-1234-1234-1234-123456789123", :session_id => "12345678-1234-1234-1234-123456789123"} Retrieving WSDL from: http://FooService.cloudapp.net/FooService.svc?wsdl HTTPI executes HTTP GET using the net_http adapter SOAP request: http://Fooservice.cloudapp.net/FooService.svc SOAPAction: "http://tempuri.org/FooService/LanguageCount", Content-Type: text/xml;charset=UTF-8 <?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:Foo_web_service="http://tempuri.org/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Body><Foo_web_service:LanguageCount><FooAppId>12345678-1234-1234-1234-123456789123</FooAppId><sessionId>12345678-1234-1234-1234-123456789123</sessionId></Foo_web_service:LanguageCount></env:Body></env:Envelope> HTTPI executes HTTP POST using the net_http adapter SOAP response (status 200): <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><LanguageCountResponse xmlns="http://tempuri.org/"><LanguageCountResult>-1</LanguageCountResult></LanguageCountResponse></s:Body></s:Envelope>
=> #<Savon::SOAP::Response:0x105a2cca8 #http=#<HTTPI::Response:0x105a28ce8 #body="<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body><LanguageCountResponse xmlns=\"http://tempuri.org/\"><LanguageCountResult>-1</LanguageCountResult></LanguageCountResponse></s:Body></s:Envelope>", #raw_body="<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\"><s:Body><LanguageCountResponse xmlns=\"http://tempuri.org/\"><LanguageCountResult>-1</LanguageCountResult></LanguageCountResponse></s:Body></s:Envelope>", #code=200, #headers={"x-powered-by"=>"ASP.NET", "x-aspnet-version"=>"4.0.30319", "content-type"=>"text/xml; charset=utf-8", "date"=>"Sat, 02 Jul 2011 23:40:56 GMT", "server"=>"Microsoft-IIS/7.0", "content-length"=>"214", "cache-control"=>"private"}>, #soap_fault=Savon::SOAP::Fault, #http_error=Savon::HTTP::Error> ruby-1.8.7-p302 >

Long story short, I had to make 3 changes.
Changed response = client.request :language_count to
response = client.request "LanguageCount"
Added :xmlns => "http://tempuri.org/" to the end of the client.request call
Explicitly called the SOAPaction like: client.http.headers["SOAPAction"] = '"http://tempuri.org/FooService/LanguageCount"'
Hope this helps someone as I've been in SOAP hell for the weekend and hopefully I'll stop someone else being there.

Related

invalid SOAP request needs experienced eye

TLDR: the solution can be found here
I'm using savon to make requests against a SOAP service. I know... Gross.
Regardless, I'm having trouble making Savon behave. The SOAP provider has this validator, which takes the following inputs:
Web Service: ProductData
Version: 1.0.0
Operation: getProductSellable
Endpoint: https://psproductdata100-stg.pcna.online
When I use the validator, I enter this xml:
<GetProductSellableRequest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.promostandards.org/WSDL/ProductDataService/1.0.0/">
<wsVersion xmlns="http://www.promostandards.org/WSDL/ProductDataService/1.0.0/SharedObjects/">1.0.0</wsVersion>
</GetProductSellableRequest>
And I get this response body
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header />
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetProductSellableResponse xmlns="http://www.promostandards.org/WSDL/ProductDataService/1.0.0/">
<ErrorMessage xmlns="http://www.promostandards.org/WSDL/ProductDataService/1.0.0/SharedObjects/">
<code>110</code>
<description>Authentication Credentials Required</description>
</ErrorMessage>
</GetProductSellableResponse>
</s:Body>
</s:Envelope>
That response is valid because I did not provide my un/pw. If I do provide credentials, I get a full response. Below is a screenshot of that happening in my browser.
However, when I use Savon to make the same request
#!/usr/bin/env ruby
require 'savon'
require 'awesome_print'
require 'byebug'
require 'pry'
endpoint = 'https://psproductdata100-stg.pcna.online'
path = 'psProductData.svc?singleWsdl'
wsdl = "#{endpoint}/#{path}"
args = {
wsdl: wsdl,
log: true,
log_level: :debug,
pretty_print_xml: true,
element_form_default: :qualified
}
client = Savon.client(args) do
convert_request_keys_to :lower_camelcase
end
message = { ws_version: '1.0.0' }
response = client.call(:get_product_sellable) do
message(message)
end
ap response
The response does not come back as expected. The XML looks close to what was sent by the validator, but not exact.
Heres the request
D, [2018-04-26T18:01:00.471662 #89854] DEBUG -- : HTTPI /peer GET request to psproductdata100-stg.pcna.online (net_http)
I, [2018-04-26T18:01:00.979809 #89854] INFO -- : SOAP request: https://psproductdata100-stg.pcna.online/psProductData.svc
I, [2018-04-26T18:01:00.979886 #89854] INFO -- : SOAPAction: "getProductSellable", Content-Type: text/xml;charset=UTF-8, Content-Length: 501
D, [2018-04-26T18:01:00.980107 #89854] DEBUG -- : <?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://www.promostandards.org/WSDL/ProductDataService/1.0.0/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ins0="http://www.promostandards.org/WSDL/ProductDataService/1.0.0/SharedObjects/">
<env:Body>
<tns:GetProductSellableRequest>
<tns:wsVersion>1.0.0</tns:wsVersion>
</tns:GetProductSellableRequest>
</env:Body>
</env:Envelope>
And the response
D, [2018-04-26T18:01:00.980224 #89854] DEBUG -- : HTTPI /peer POST request to psproductdata100-stg.pcna.online (net_http)
I, [2018-04-26T18:01:01.650449 #89854] INFO -- : SOAP response (status 200)
D, [2018-04-26T18:01:01.650731 #89854] DEBUG -- : <?xml version="1.0"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<GetProductSellableResponse xmlns="http://www.promostandards.org/WSDL/ProductDataService/1.0.0/">
<ErrorMessage xmlns="http://www.promostandards.org/WSDL/ProductDataService/1.0.0/SharedObjects/">
<code>110</code>
<description>Version mismatch.</description>
</ErrorMessage>
</GetProductSellableResponse>
</s:Body>
</s:Envelope>
and the output from Savon
{
:get_product_sellable_response => {
:error_message => {
:code => "110",
:description => "Version mismatch.",
:#xmlns => "http://www.promostandards.org/WSDL/ProductDataService/1.0.0/SharedObjects/"
},
:#xmlns => "http://www.promostandards.org/WSDL/ProductDataService/1.0.0/"
},
:"#xmlns:xsi" => "http://www.w3.org/2001/XMLSchema-instance",
:"#xmlns:xsd" => "http://www.w3.org/2001/XMLSchema"
}
Thanks to the other answers on this thread, I found this post. I read up on how namespaces work, as well as linked XSD files. By combining my new knowledge with that post, I was a able to build out something like this:
require 'savon'
require 'awesome_print'
require 'byebug'
require 'pry'
endpoint = 'https://psproductdata100-stg.pcna.online'
path = 'psProductData.svc?singleWsdl'
wsdl = "#{endpoint}/#{path}"
args = {
wsdl: wsdl,
log: false,
log_level: :debug,
pretty_print_xml: true,
element_form_default: :qualified
}
client = Savon.client(args) do
convert_request_keys_to :lower_camelcase
namespaces 'xmlns:shar' => 'http://www.promostandards.org/WSDL/ProductDataService/1.0.0/SharedObjects/'
end
message = {
'shar:wsVersion' => '1.0.0',
'shar:id' => 'REDACTED',
'shar:password' => 'REDACTED'
}
response = client.call(:get_product_sellable) do
message(message)
end
ap response.body[:get_product_sellable_response][:product_sellable_array][:product_sellable][0..2]
Now i'm more fun to talk to at dinner parties.
Similar problem with namespaces ruby savon and wsdl namespacing. I believe you have the same problem.
Additional Note: Try to use SOAP UI to debug, the generated XML from the WSDL and see the difference. Also change the XML to the XML generated from the code and validate the XML in SOAP UI. It will show the errors if any in SOAP UI, will make debugging faster and errors more understandable.
I believe your issue is a namespace problem. The wsVersion is within the shared object namespace "ins0". It is not in the "tns" namespace. Try changing your request to look like the following:
<env:Body>
<tns:GetProductSellableRequest>
<ins0:wsVersion>1.0.0</ins0:wsVersion>
</tns:GetProductSellableRequest>
</env:Body>

consume custom binding oracle WFC web service with ruby / savon

I am trying to consume a wfc web service with a non basicthttp binding with ruby and savon.
The provider of the WS implemented the oracle/wss11_username_token_with_message_protection_service_policy (which is described here
http://www.oracle.com/technetwork/articles/soa/oracle-msft-interoperate-183511.html)
If I understand correctly this policy is expecting some sort of encryption (according to this http://blog.raastech.com/2012/08/owsm-security-errors.html) due to the non basic http binding
The WS described actually goes over ssl which may be the root of all evil due to me implementing it poorly.
Is even possible to consume a WFC WS with non basic http binding with savon?
My background is ruby and rails, yet I am new to WS (and savon) and ssl usage.
Now the provider of the WS does not give much of support. Actually their outing is that they only provide support for implementations in java or .net (is that even possible given that it’s a WS and thus platform independent?).
In an attempt to run the fairly simple code which is the following
require 'savon'
client = Savon.client do
wsdl "https://apps.ika.gr/hospitalisationWSS_TEST-hospitalisationWSS_TEST-context-root/MainWSClassPort?WSDL"
wsse_auth "ΥΥΥΥ", "ΧΧΧΧ" # ALTERED
wsse_timestamp true
pretty_print_xml true
log_level :debug
ssl_verify_mode :none
ssl_cert_file "files/ika.cer" # provided by WS provider
end
save_admission_hl7_msg = "MSH|^~\&|||||201310111112||ADT^A13^ADT_A13|2013000012113|P|2.6|||||||||66645678912345678945|^^^^^^^^^10000\nEVN|A13|201310101122|||nosiliapap\nPID\nPV1||I|||||||||||||||||2013000012111|||||||||||||||||||||||||||||||2013000012113\n"
response = client.call(:save_admission_hl7,
:attributes => { :xmlns => "http://bean.intracom.com/" },
:message => { :arg0 => { :#xmlns=>'', :content! =>{ :hl7ADT => save_admission_hl7_msg } } } )
end
puts response.body
Ι get the following output
D, [2013-12-04T08:21:20.445549 #3088] DEBUG -- : HTTPI GET request to apps.ika.gr (httpclient)
I, [2013-12-04T08:21:22.074643 #3088] INFO -- : SOAP request: https://apps.ika.gr/hospitalisationWSS_TEST-hospitalisationWSS_TEST-context-root/MainWSClassPort
I, [2013-12-04T08:21:22.074643 #3088] INFO -- : SOAPAction: "saveAdmissionHl7", Content-Type: text/xml;charset=UTF-8, Content-Length: 1363
D, [2013-12-04T08:21:22.075643 #3088] DEBUG -- : <?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tns="http://bean.intracom.com/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="UsernameToken-1">
<wsse:Username>YYYY</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">XXXX</wsse:Password>
</wsse:UsernameToken>
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Timestamp-2">
<wsu:Created>2013-12-04T06:21:22Z</wsu:Created>
<wsu:Expires>2013-12-04T06:22:22Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</env:Header>
<env:Body>
<tns:saveAdmissionHl7 xmlns="http://bean.intracom.com/">
<arg0 xmlns="">
<hl7ADT>MSH|^~&|||||201310111112||ADT^A13^ADT_A13|2013000012113|P|2.6|||||||||66645678912345678945|^^^^^^^^^10000
EVN|A13|201310101122|||nosiliapap
PID
PV1||I|||||||||||||||||2013000012111|||||||||||||||||||||||||||||||2013000012113
</hl7ADT>
</arg0>
</tns:saveAdmissionHl7>
</env:Body>
</env:Envelope>
D, [2013-12-04T08:21:22.076643 #3088] DEBUG -- : HTTPI POST request to apps.ika.gr (httpclient)
I, [2013-12-04T08:21:22.472665 #3088] INFO -- : SOAP response (status 500)
D, [2013-12-04T08:21:22.473665 #3088] DEBUG -- : <?xml version="1.0" encoding="UTF-8"?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:Fault xmlns:ns2="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns3="http://www.w3.org/2003/05/soap-envelope">
<faultcode xmlns:ns0="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">ns0:FailedCheck</faultcode>
<faultstring>FailedCheck : failure in security check</faultstring>
</ns2:Fault>
</S:Body>
</S:Envelope>
f:/rubystack/ruby/lib/ruby/gems/1.9.1/gems/savon-2.3.0/lib/savon/response.rb:85:in `raise_soap_and_http_errors!': (ns0:FailedCheck) FailedCheck : failure in security check (Savon::SOAPFault)
from f:/rubystack/ruby/lib/ruby/gems/1.9.1/gems/savon-2.3.0/lib/savon/response.rb:14:in `initialize'
from f:/rubystack/ruby/lib/ruby/gems/1.9.1/gems/savon-2.3.0/lib/savon/operation.rb:64:in `new'
from f:/rubystack/ruby/lib/ruby/gems/1.9.1/gems/savon-2.3.0/lib/savon/operation.rb:64:in `create_response'
from f:/rubystack/ruby/lib/ruby/gems/1.9.1/gems/savon-2.3.0/lib/savon/operation.rb:55:in `call'
from f:/rubystack/ruby/lib/ruby/gems/1.9.1/gems/savon-2.3.0/lib/savon/client.rb:36:in `call'
from eopyy.rb:82:in `<main>'
Now given my poor knowledge on WS's and SSL usage I cant figure out the error.
Is it the SSL? Should I use ssl_ca_cert_file instead of ssl_cert_file ? (obviously I tried that already). Do I need to use a key file also?
The provider actually provides a .jks file (next to the above "ika.cer" certificate) but also mentions that it should be used only within a java implementation (an not a .net). I already unpacked the jks file and attempted to use its contained certificates and files, unsuccesfully.
Any help, clues, tips, greatly appreciated.

API Call With Savon SOAP Client Results In ContractFilter mismatch at the EndpointDispatcher Error

I've run into an issue where the Savon Ruby Gem generates a SOAP API call that fails, however when I copy and paste the exact same XML message into SOAP-UI it succeeds.
I send this message:
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tem="http://tempuri.org/" xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:vis="http://schemas.datacontract.org/2004/07/Vision.SecureOriginCommand.ServiceContracts">
<soapenv:Body>
<tem:CameraConfiguration>
<tem:request>
<vis:ClientToken>5555</vis:ClientToken>
<vis:DeviceID>26219</vis:DeviceID>
<vis:Enabled>1</vis:Enabled>
<vis:Interval>60</vis:Interval>
</tem:request>
</tem:CameraConfiguration>
</soapenv:Body>
To this API (A remote web camera configuration):
https://oapqa.onasset.com/Services/SecureOriginCommand.svc?wsdl
But it fails with this message:
SOAP response (status 500):
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault><faultcode xmlns:a="http://schemas.microsoft.com/ws/2005/05/addressing/none">a:ActionNotSupported</faultcode>
<faultstring xml:lang="en-US">The message with Action 'oapSetSentryReportingIntervalRequest' cannot be processed at the receiver, due to a ContractFilter mismatch at the EndpointDispatcher. This may be because of either a contract mismatch (mismatched Actions between sender and receiver) or a binding/security mismatch between the sender and the receiver. Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None)</faultstring>
</s:Fault>
</s:Body>
My first thought was that I must have made a typo in the action name. But
no, when I try the exact same message in SOAP-UI I get the following
success:
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<CameraConfigurationResponse xmlns="http://tempuri.org/">
<CameraConfigurationResult xmlns:a="http://schemas.datacontract.org/2004/07/Vision.SecureOriginCommand.ServiceContracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<a:Error/>
<a:Result>true</a:Result>
</CameraConfigurationResult>
</CameraConfigurationResponse>
</s:Body>
</s:Envelope>
This leads me to believe that the issue is not being caused by the format
of my xml message but with the way I'm configuring my client. Here is the actual code:
Savon.configure do |config|
config.log = :debug
config.env_namespace = :soapenv
config.raise_errors = false
end
# TODO Enable ssl certficate verification
client = Savon::Client.new do
wsdl.document = TARGET_SO_WSDL
http.auth.ssl.verify_mode = :none
end
resp = client.request 'tem', 'CameraConfiguration' do
soap.namespaces['xmlns:vis'] = 'http://schemas.datacontract.org/2004/07/Vision.SecureOriginCommand.ServiceContracts'
soap.namespaces['xmlns:tem'] = 'http://tempuri.org/'
soap.body = {
'tem:request' => {
'vis:ClientToken' => ON_ASSET_API_KEY,
'vis:DeviceID' => webcam.gps_device.device_id,
'vis:Enabled' => 1,
'vis:Interval' => webcam.report_interval
}
}
end
I've spoken to the developer who maintains the API I'm trying to
access. I thought his response could provide a clue:
Binding on the RemoteSentryService was set to mexHttpBinding instead of mexHttpsBinding.
I don’t think this should give you a fault exception, because its working on .NET simulator client I have. And this endpoint is only used to generate the wsdl (MetaExchange Binding). But, given you are using a different client, I would still give it a shot.
I also regenerated the proxy from wsdl and updated my sample simulator and it looks good.
Is this issue a known issue with Savon and Microsoft SOAP
endpoints or HTTPS? Or is this issue something only I'm encountering?
Debugged it and noticed that Savon unfortunately doesn't send the correct SOAPAction HTTP header. FYI: After sending a SOAP request via soapUI, you can click on the "RAW" tab (vertically aligned on the request window) to investigate further.
Here's the complete example:
client = Savon::Client.new do
wsdl.document = TARGET_SO_WSDL
http.auth.ssl.verify_mode = :none
end
resp = client.request 'tem', 'CameraConfiguration' do
# Notice, that the SOAPAction needs to the wrapped in double quotes:
http.headers['SOAPAction'] = %("http://tempuri.org/ISecureOriginCommand/CameraConfiguration")
soap.namespaces['xmlns:vis'] = 'http://schemas.datacontract.org/2004/07/Vision.SecureOriginCommand.ServiceContracts'
soap.body = {
'tem:request' => {
'vis:ClientToken' => 5555,
'vis:DeviceID' => 26219,
'vis:Enabled' => 1,
'vis:Interval' => 60
}
}
end
Hope it works for you!

Savon: Adding an encoding to the body tag

How can I add an encoding attribute to the body tag using Savon?
Some background:
I am trying to use savon to connect to a SOAP resource. I can get the WSDL file and browse through the methods.
#client = Savon::Client.new("http://some.domain.com/v2messaging/service?WSDL")
when I try to use the login method
response = #client.request :service, :login do
soap.body = {
"String_1" => "username",
"String_2" => "password"
}
end
I get this error:
Failure/Error: response = #client.request :service, :login do Savon::SOAP::Fault: (env:Client) caught exception while handling request: unexpected encoding style: expected=http://schemas.xmlsoap.org/soap/encoding/, actual
The difference in the body tag. Here is the expected xml (found through SOAPUI application):
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:service="etapestryAPI/service">
<env:header/>
<env:body>
<service:login env:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<String_1>username</String_1>
<String_2>password</String_2>
</service:login>
</env:body>
</env:Envelope>
Savon sends:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:service="etapestryAPI/service" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ins0="http://java.sun.com/jax-rpc-ri/internal" xmlns:ins1="etapestryAPI/service">
<env:Body>
<service:login>
<String_1>username</String_1>
<String_2>password</String_2>
</service:login>
</env:Body>
</env:Envelope>
There are a few difference between these, but the error returned has to do with the env:encodingStyle attribute on the env:login tag. How can add this attribute?
I figured this one out. To add an attribute to the function tag (in this case login), you can pass in an additional parameter to the method:
response = #client.request :service, :login, "env:encodingStyle" => "http://schemas.xmlsoap.org/soap/encoding/" do
soap.body = {
"String_1" => "username",
"String_2" => "password"
}
end
This will probably now work without passing the block.

How to create urn:localhost-catalog SOAP header in Ruby?

I have this (working) example in php:
$usernameToken = new SoapHeaderUsernameToken( $password, $username );
$soapHeaders[] = new SoapHeader("urn:localhost-catalog", 'UsernameToken', $usernameToken);
$client = new SoapClient("my_client_url");
$client->__setSoapHeaders( $soapHeaders );
But I want a client in Ruby. I'm using the savon gem, which can do authentication for you. So far, I have the following method:
def my_soap_client
client = Savon::Client.new do
wsdl.document = "my_client_url"
end
client.wsse.credentials "usr", "pass" # Adding :digest doesn't seem to help
return client
end
# I make requests like this:
my_soap_client.request :wsdl, "getProductGroups", :parent_id => 1
I can successfully list available actions, but if I try to make a request, the result is always "(SOAP-ENV:Client) Missing parameter". Also, it doesn't matter if I pass correct credentials or fake ones. That is why I believe the authentication fails.
Hope someone can help!
Using Wireshark sniffer I intercepted SOAP request that your PHP code sends. It looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:examples:helloservice"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ns2="urn:localhost-catalog"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Header>
<ns2:UsernameToken>
<Password>pass</Password>
<Username>login</Username>
</ns2:UsernameToken>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:getProductGroups>
<parentId>1</parentId>
</ns1:getProductGroups>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Here you can see that this code adds ns2 namespace for UsernameToken header.
Then I intercepted SOAP request that sends your Ruby code:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wsdl="http://www.ecerami.com/wsdl/HelloService.wsdl"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken wsu:Id="UsernameToken-1"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<wsse:Username>usr</wsse:Username>
<wsse:Password
Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">
pass
</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</env:Header>
<env:Body>
<wsdl:getProductGroups>
<parentId>1</parentId>
</wsdl:getProductGroups>
</env:Body>
</env:Envelope>
You can see the difference - UsernameToken is wrapped into Security element and namespace for UsernameToken is "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
Your PHP code uses custom authorization and savon does not support it via wsse property. I looked into Savon source code, it uses Akami.wsse and in its code there is no possibility to set namespace. This means you need to manually construct headers. Here is the code in Ruby that generates SOAP request like you possibly need:
require 'savon'
client = Savon::Client.new do
wsdl.document = "test.wsdl"
end
client.request :wsdl, "sayHello" do
soap.namespaces["xmlns:ns2"] = "urn:localhost-catalog"
soap.header = { "ns2:UsernameToken" =>
{"Password" => "pass", "Username" => "login"}
}
soap.body = { :parent_id => 1 }
end
It generate the following SOAP request:
<?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wsdl="http://www.ecerami.com/wsdl/HelloService.wsdl"
xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns2="urn:localhost-catalog">
<env:Header>
<ns2:UsernameToken>
<Password>pass</Password>
<Username>login</Username>
</ns2:UsernameToken>
</env:Header>
<env:Body>
<wsdl:sayHello>
<parentId>1</parentId>
</wsdl:sayHello>
</env:Body>
</env:Envelope>
Hope this helps!
to set a header in Savon use the following expression:
client.request :wsdl, "getProductGroups" do
soap.header = { :header_key => "your header goes here",
:attributes! => { :attr => "your attribute" }
}
soap.body = { :parent_id => 1 }
end
perhaps that helps you.
-st

Resources