Can't specify header in SOAP call using Savon - ruby-on-rails

When calling to an API for retrieving labels for post parcels I get frustrated with how hard it is to set up a SOAP call with ruby.
I know that the following works from a sandbox environment:
this is the SHOULD BE call
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode</Action>
<Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:Username>devc_!R4xc8p9</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">098fd559930983af31ef6630a0bb0c1974156561</wsse:Password>
</wsse:UsernameToken>
</Security>
</s:Header>
<s:Body>
<GenerateBarcode xmlns:d6p1="http://postnl.nl/cif/domain/BarcodeWebService/" xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://postnl.nl/cif/services/BarcodeWebService/">
<d6p1:Message>
<d6p1:MessageID>5</d6p1:MessageID>
<d6p1:MessageTimeStamp>28-06-2017 14:15:41</d6p1:MessageTimeStamp>
</d6p1:Message>
<d6p1:Customer>
<d6p1:CustomerCode>DEVC</d6p1:CustomerCode>
<d6p1:CustomerNumber>11223344</d6p1:CustomerNumber>
</d6p1:Customer>
<d6p1:Barcode>
<d6p1:Type>3S</d6p1:Type>
<d6p1:Range>DEVC</d6p1:Range>
<d6p1:Serie>1000000-2000000</d6p1:Serie>
</d6p1:Barcode>
</GenerateBarcode>
</s:Body>
</s:Envelope>
EDIT
As sugested I used build_request to inspect how my request looks. It actually looks like this:
<?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://tempuri.org/" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
<env:Header>
<Username>devc_!R4xc8p9</Username
<Password>098fd559930983af31ef6630a0bb0c1974156561</Password>
</env:Header>
<env:Body><wsdl:GenerateBarcode><CustomerCode>DEVC</CustomerCode>
<CustomerNumber>11223344</CustomerNumber>
<Type>3S</Type>
<Range>DEVC</Range>
<Serie>1000000-2000000</Serie>
</wsdl:GenerateBarcode>
</env:Body>
</env:Envelope>
Comparing that with the xml above, it becomes clear that stuff is off. How to handle nesting?
This is when I build it myself like:
#client = Savon.client(
:convert_request_keys_to => :camelcase,
:raise_errors => false,
:pretty_print_xml => true,
:wsdl => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/?wsdl',
:headers => {
'Username' => 'devc_!R4xc8p9',
'Password' => '098fd559930983af31ef6630a0bb0c1974156561'
})
#response = #client.call(:generate_barcode) do
soap_header "Username" => 'devc_!R4xc8p9',
"Password" => '098fd559930983af31ef6630a0bb0c1974156561'
message customer_code: 'DEVC',
customer_number: 11223344,
type: '3S',
range: 'DEVC',
serie: '1000000-2000000'
end
I know my connection works, but also get back:
{:fault=>{:faultcode=>"s:CIF Framework Message Interceptor",
:faultstring=>"Check CIFException in the detail section", :detail=>
{:cif_exception=>{:errors=>{:exception_data=>{:description=>nil,
:error_msg=>"Username/password is not specified.",
:error_number=>"3"}},
:#xmlns=>"http://postnl.nl/cif/services/common/",
:"#xmlns:i"=>"http://www.w3.org/2001/XMLSchema-instance"}}}}
So already in my header stuff goes wrong. Username/password is not specified. I've tried several combinations and I don't get why.
Update
#client = Savon.client(
:env_namespace => :s,
# :namespace_identifier => :none, # :none doesn't work... gets interpreted literary. Out-commeting gives wsdl:GenerateBarcode
:convert_request_keys_to => :camelcase,
:raise_errors => false,
:pretty_print_xml => true,
:endpoint => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/BarcodeWebService.svc',
:wsdl => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/?wsdl')
soap_header = {
"Security" => {
"UsernameToken" => {
"Username" => 'devc_!R4xc8p9',
"Password" => '098fd559930983af31ef6630a0bb0c1974156561'
}
}
}
message = {
"d6p1:Customer" => {
"d6p1:CustomerCode" => 'DEVC',
"d6p1:CustomerNumber" => 11223344
},
"d6p1:Barcode" => {
"d6p1:Type" => '3S',
"d6p1:Range" => 'DEVC',
"d6p1:Serie" => '1000000-2000000'
}
}
#request = #client.build_request(:generate_barcode) do
soap_header soap_header
message message
end
#response = #client.call(:generate_barcode) do
soap_header soap_header
message message
Which builds the following request:
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:s="http://tempuri.org/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Security>
<UsernameToken>
<Username>devc_!R4xc8p9</Username
<Password>098fd559930983af31ef6630a0bb0c1974156561</Password>
</UsernameToken>
</Security>
</s:Header>
<s:Body>
<wsdl:GenerateBarcode>
<d6p1:Customer>
<d6p1:CustomerCode>DEVC</d6p1:CustomerCode
<d6p1:CustomerNumber>11223344</d6p1:CustomerNumber>
</d6p1:Customer>
<d6p1:Barcode>
<d6p1:Type>3S</d6p1:Type>
<d6p1:Range>DEVC</d6p1:Range>
<d6p1:Serie>1000000-2000000</d6p1:Serie>
</d6p1:Barcode></s:GenerateBarcode>
</s:Body>
For as of yet, I get errors adding the wsse: namespace straight in the soap_header hash.
EDIT 2
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:wsdl="http://tempuri.org/"
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode
</Action>
<Security xmlns="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<UsernameToken>
<Username>devc_!R4xc8p9</Username>
<Password>xxxxxxxx</Password>
</UsernameToken>
</Security>
</s:Header>
<s:Body>
<wsdl:GenerateBarcode>
<d6p1:Customer>
<d6p1:CustomerCode>DEVC</d6p1:CustomerCode>
<d6p1:CustomerNumber>11223344</d6p1:CustomerNumber>
</d6p1:Customer>
<d6p1:Barcode>
<d6p1:Type>3S</d6p1:Type>
<d6p1:Range>DEVC</d6p1:Range>
<d6p1:Serie>1000000-2000000</d6p1:Serie>
</d6p1:Barcode>
</wsdl:GenerateBarcode>
</s:Body>
</s:Envelope>
Comparing this to my should be call it seems that my <s:envelope ...> tag has too much information.
I'll make a follow up question of this probably.
EDIT 3
I restarted at Complex SOAP call in Ruby on Rails using Savon gets weird around the envelope and main operation

The Message hash, should be nested in the same way as the call expects it something like
#client = Savon.client(
:convert_request_keys_to => :camelcase,
:raise_errors => false,
:pretty_print_xml => true,
:wsdl => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/?wsdl',
:headers => {
'Username' => 'devc_!R4xc8p9',
'Password' => '098fd559930983af31ef6630a0bb0c1974156561'
})
#response = #client.call(:generate_barcode) do
soap_header "Security" => {
"UsernameToken" => {
"Username" => 'devc_!R4xc8p9',
"Password" => '098fd559930983af31ef6630a0bb0c1974156561'
}
}
message "GenerateBarcode" => {
"Message" => {
"MessageID" => 5, #might be different
"MessageTime" => Time.now, #might be different depending on use case
},
"Customer" => {
"CustomerCode" => 'DEVC',
"CustomerNumber" => 11223344
},
"Barcode" => {
"Type" => '3S',
"Range" => 'DEVC',
"Serie" => '1000000-2000000'
}
}
end
EDIT
Referencing this page it looks like you can add the action tag and elements like this
'Action' => "http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode",
:attributes! => {
"s:mustUnderstand" => "1",
"xmlns" => "http://schemas.microsoft.com/ws/2005/05/addressing/none"
}
Referencing this page it looks like you can set the namespaces directly "wsse:UsernameToken" instead of "UsernameToken"
which should result in the following
#client = Savon.client(
:convert_request_keys_to => :camelcase,
:raise_errors => false,
:pretty_print_xml => true,
:wsdl => 'https://testservice.postnl.com/CIF_SB/BarcodeWebService/1_1/?wsdl',
:headers => {
'Username' => 'devc_!R4xc8p9',
'Password' => '098fd559930983af31ef6630a0bb0c1974156561'
})
#response = #client.call(:generate_barcode) do
soap_header "Security" => {
"wsse:UsernameToken" => {
"wsse:Username" => 'devc_!R4xc8p9',
"wsse:Password" => '098fd559930983af31ef6630a0bb0c1974156561'
}
"Action" => "http://postnl.nl/cif/services/BarcodeWebService/IBarcodeWebService/GenerateBarcode",
:attributes! => {
"s:mustUnderstand" => "1",
"xmlns" => "http://schemas.microsoft.com/ws/2005/05/addressing/none"
}
}
message "GenerateBarcode" => {
"Message" => {
"MessageID" => 5, #might be different
"MessageTime" => Time.now, #might be different depending on use case
},
"Customer" => {
"CustomerCode" => 'DEVC',
"CustomerNumber" => 11223344
},
"Barcode" => {
"Type" => '3S',
"Range" => 'DEVC',
"Serie" => '1000000-2000000'
}
}
end

Related

I'm trying to send leads to an external API, but have CORS policy blocking me. Any insights?

Trying to send leads to an external API. here is my Post-request:
Here is my error code in the console:
Access to XMLHttpRequest at 'https://www.trackbox.guru/forms/' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
code = RestClient.post('https://platform.esh.ltd/user/login', {
headers: {
'x-trackbox-username' => 'pass',
'x-trackbox-password' => 'user',
'x-api-key' => 'xx',
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Headers' => "*",
'Access-Control-Allow-Credentials'=> 'true'
},
body: {
'ai' => "xx",
'ci' => "660",
'gi' => "25",
'userip' => request.remote_ip,
'firstname' => reader_params[:first_name],
'lastname' => reader_params[:last_name],
'email' => reader_params[:email],
'password' => reader_params[:password],
'phone' => reader_params[:phone_number],
'prefix' => reader_params[:phone_code]
}
})
puts code
puts ('here')
redirect_to redirect_url
I also have a script tag in my header that allows the api to collect more data:
<script>gvars={'gi': 25, 'ci': 656, 'wl': 17, 'rd': 4, 'ap': 0,
'ae': 0, 'lg': 'en', 'ai':xx};</script> <script
src='https://cdn.trackbox.guru/trackbot.js?v=4.64'></script>
<div id='gaff'></div>
So the signature for post appears to be RestClient.post(url, payload, headers={}) README so move the headers outside of the outer curly braces {} and place it at the end.
Also the "body" wrapper seems like it is not required (looking at the API docs).
Give this a try instead.
code = RestClient.post('https://platform.esh.ltd/user/login', {
'ai' => "xx",
'ci' => "660",
'gi' => "25",
'userip' => request.remote_ip,
'firstname' => reader_params[:first_name],
'lastname' => reader_params[:last_name],
'email' => reader_params[:email],
'password' => reader_params[:password],
'phone' => reader_params[:phone_number],
'prefix' => reader_params[:phone_code]
},headers: {
'x-trackbox-username' => 'pass',
'x-trackbox-password' => 'user',
'x-api-key' => 'xx',
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Headers' => "*",
'Access-Control-Allow-Credentials'=> 'true'
})

savon multiple namespace in body

I am new to Savon2
and passing wsdl url to the Savon.Client, and getting proper response in header, but I am not getting how to pass multiple namespace to the body:
Savon.client(wsdl: 'https://someUrl.com/abcd?wsdl', ssl_verify_mode: :none, pretty_print_xml: true, log: true, env_namespace: :soapenv, namespace_identifier: nil) do
wsse_auth("api_demo_account", "somePassword", :digest)
end
client.call(:operation_name, message: {
"api01Req" => [{"shipCode" => 'ABC', "shipAmount" => 1123,
"countryCode" => "USA"}
]},
'attributes' => { "xmlns" => "http://someUrl.com" })
and it is generating code for body like this:
<soapenv:Body>
<operationName xmlns="http://someUrl.com">
<xmlns:api01Req>
<shipCode>ABC</shipCode>
<shipAmount>1123</shipAmount>
<countryCode>USA</countryCode>
</xmlns:api01Req>
</operationName>
</soapenv:Body>
But I need a request like this:
<soapenv:Body>
<operationName xmlns="http://someUrl.com">
<api01Req>
<ns1:shipCode xmlns:ns1="http://someOtherUrl.com">ABC
</ns1:shipCode>
<ns2:shipAmount xmlns:ns2="http://someOtherUrl.com">1123
</ns2:shipAmount>
<ns3:countryCode xmlns:ns3="http://someOtherUrl.com">USA
</ns3:countryCode>
</api01Req>
</getTotalPostage>
</soapenv:Body>
like here it is generating ns1, ns2, ns3, etc along with xmlns:ns1, xmlns:ns2, etc.. different URL's, how should I modify the code to get like above request.

Why request sending error Savon::SOAPFault: (ns1:SecurityError)

I am getting this error from the SOAP actions integration.
Savon::SOAPFault: (ns1:SecurityError) A security error was encountered when verifying the message
my Savon client look like this:
client = Savon.client(wsdl: "https://212.154.167.194:9443/esf-web/ws/api1/SessionService?wsdl",
ssl_verify_mode: :none,
env_namespace: :soapenv,
pretty_print_xml: true,
namespace_identifier: :nam,
env_namespace: :soapenv,
namespaces: { "xmlns:nam" => "namespace.esf" },
:raise_errors => true,
log: true,
#loglevel: :debug,
pretty_print_xml: true)
message = {
"createSessionRequest" =>
{
"tin" => "placeholder",
"x509Certificate" => "placeholder"
}
}
and generated responce
response = client.call(:create_session, message: {:tin => 'XXXX', :x509Certificate => 'xxxxxx'})
The XML code should be here:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:esf="esf">
<soapenv:Header>
<wsse:Security soapenv:mustUnderstand="1" 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">
<wsse:UsernameToken wsu:Id="UsernameToken-664678CEF9FFC67AD214168421472821">
<wsse:Username>123456789011</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
</wsse:UsernameToken>
</wsse:Security></soapenv:Header>
<soapenv:Body>
<esf:createSessionRequest>
<tin>?</tin>
<!--Optional:-->
<projectCode>?</projectCode>
<x509Certificate>?</x509Certificate>
</esf:createSessionRequest>
</soapenv:Body>
</soapenv:Envelope>
Please help
In the xml you have the following username and password
<wsse:Username>123456789011</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">password</wsse:Password>
Where do you set those username and password?
Read the guide so that you can follow their steps to configure Savon:
response = client.call(:authenticate, message: { username: "luke", password: "secret" })

Savon SOAP Request not working

I'm using Savon gem in my Rails app and I need to submit my form fields to Equifax.
Here's the Equifax XML:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:v3="http://eid.equifax.com/soap/schema/canada/v3">
<soapenv:Header/>
<soapenv:Body>
<v3:InitialRequest>
<v3:Identity>
<v3:Name>
<v3:FirstName>Michael</v3:FirstName>
<v3:LastName>Smith</v3:LastName>
</v3:Name>
<!--1 to 3 repetitions:-->
<v3:Address timeAtAddress="72" addressType="current">
<v3:HybridAddress>
<!--1 to 6 repetitions:-->
<v3:AddressLine>1028 Summerville</v3:AddressLine>
<v3:City>Vancouver</v3:City>
<v3:Province>BC</v3:Province>
<v3:PostalCode>V7B0A8</v3:PostalCode>
</v3:HybridAddress>
</v3:Address>
<!--Optional:-->
<v3:DateOfBirth>
<v3:Day>26</v3:Day>
<v3:Month>08</v3:Month>
<v3:Year>1984</v3:Year>
</v3:DateOfBirth>
<v3:PhoneNumber phoneType="current">
<v3:PhoneNumber>6045556666</v3:PhoneNumber>
</v3:PhoneNumber>
</v3:Identity>
<v3:ProcessingOptions>
<v3:Language>English</v3:Language>
</v3:ProcessingOptions>
</v3:InitialRequest>
</soapenv:Body>
</soapenv:Envelope>
This is my Ruby code making the SOAP Request.
<%= client.call(:start_transaction, message: { "v3:InitialRequest" => { "v3:Identity" => { "v3:Name" => { "v3:FirstName" => "michael", "v3:LastName" => "Smith"}, "v3:Address" => {"v3:AddressLine" => "233 Cambie St", "v3:City" => "Vancouve", "v3:Province" => "BC", "v3:PostalCode" => "V6B0E8"}, "v3:DateOfBirth" => {"v3:Day" => "26", "v3:Month" => "08", "v3:Year" => "1984"}, "v3:PhoneNumber" => {"v3:PhoneNumber" => "6048885555"}}}, attributes: { "v3:Address" => { "timeAtAddress" => "72", "addressType" => "current"}}) %>
The error message that is returned: (soap:Client) Error reading XMLStreamReader.
Can someone point me in the right direction?
Please try the below way by sending one string of that xml
xml_str = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:v3="http://eid.equifax.com/soap/schema/canada/v3"><soapenv:Header/><soapenv:Body><v3:InitialRequest><v3:Identity><v3:Name><v3:FirstName>Michael</v3:FirstName><v3:LastName>Smith</v3:LastName></v3:Name><!--1 to 3 repetitions:--><v3:Address timeAtAddress="72" addressType="current"><v3:HybridAddress><v3:AddressLine>1028 Summerville</v3:AddressLine><v3:City>Vancouver</v3:City><v3:Province>BC</v3:Province><v3:PostalCode>V7B0A8</v3:PostalCode></v3:HybridAddress></v3:Address><v3:DateOfBirth><v3:Day>26</v3:Day><v3:Month>08</v3:Month><v3:Year>1984</v3:Year></v3:DateOfBirth><v3:PhoneNumber phoneType="current"><v3:PhoneNumber>6045556666</v3:PhoneNumber></v3:PhoneNumber></v3:Identity><v3:ProcessingOptions><v3:Language>English</v3:Language></v3:ProcessingOptions></v3:InitialRequest></soapenv:Body></soapenv:Envelope>'
client.call(:start_transaction, xml: xml_str)
Remember you can use here document to made the xml string but the string should not consis "\n

how to pass Security header in savon 2.7 with Rails 4

my require header is
<SOAP-ENV:Header xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:Security SOAP-ENV:mustUnderstand="1">
<wsse:UsernameToken>
<wsse:Username>123</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">1234</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</SOAP-ENV:Header>
this is my message :
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:urn="urn:dictionary:com.test.webservices">
<soapenv:Body>
<urn:getPIN/>
</soapenv:Body>
</soapenv:Envelope>
my code
soap_header = {
"wsse:Security" => {
"#soapenv:mustUnderstand" => 1,
"wsse:UsernameToken" =>{
"Username" => "123",
"Password" => "1234",
"digest" => false
}
}
}
client = Savon.client(wsdl: #wsdl, log: true,:ssl_verify_mode => :none,:soap_header=> soap_header)
response = client.call :getPIN, xml: #message
how to pass header in Savon 2.7 we not able to access services please help us.
Try this please (found on Savon 2 documentation):
Savon.client(wsse_auth: ["123", "1234"])
The soap endpoint I was contacting was very picky and I had this same problem. (wsse_auth did not work)
I solved this by modifying your answer slightly.
soap_header = {
"wsse:Security" => {
"#soapenv:mustUnderstand" => 1,
"#xmlns:soap" => "http://schemas.xmlsoap.org/wsdl/soap/",
"#xmlns:wsse" => "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd",
"wsse:UsernameToken" =>{
"wsse:Username" => "123",
"wsse:Password" => "12345",
}
}
}
The problem was the missing soapenv:mustUnderstand="1"

Resources