Jasper Kennis
Jasper Kennis

Reputation: 3062

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!

Upvotes: 1

Views: 951

Answers (2)

Aliaksei Kliuchnikau
Aliaksei Kliuchnikau

Reputation: 13719

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!

Upvotes: 2

Steffen Roller
Steffen Roller

Reputation: 3494

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

Upvotes: 0

Related Questions