Ruby, Soap4R, WSSE Authentication

I’ve been doing some work for a client, where we have to interact with a web service front end for Oracle (which also means that everything reflects Oracle’s “delightful” shortened table names, like UsrAddrsPhn…). I didn’t find a lot out there to do this in Ruby, except this helpful post:

http://willcannings.wordpress.com/2008/07/06/wsse-authentication-in-ruby-soap4r/

However, it wasn’t quite enough for what we’re doing, as we have to deal with a nonce parameter as well.

Here’s the snippet of code I created:

require 'rubygems'
gem 'soap4r'
require 'base64'

require 'soap/rpc/driver'
require 'soap/wsdlDriver'

wsdl_url = ARGV.shift
proxy    = SOAP::WSDLDriverFactory.new(wsdl_url).create_rpc_driver

# run ruby with -d to see SOAP wiredumps.
proxy.wiredump_dev = STDERR if $DEBUG

require 'soap/header/simplehandler'
require 'soap/element'
require 'xsd/datatypes'

class WsseAuthHeader < SOAP::Header::Handler
  NAMESPACE= 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
  USERNAME  = 'Not really the username'
  PASSWORD  = 'Put your own password here'

  def initialize()
    super(XSD::QName.new(NAMESPACE, 'Security'))
  end

  def on_outbound
    security = SOAP::SOAPElement.new('wsse:Security')
    security.extraattr['xmlns:wsse'] =
      'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
    unt = SOAP::SOAPElement.new('wsse:UsernameToken')
    unt.extraattr['xmlns:wsu'] =
      'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'
    un = SOAP::SOAPElement.new('wsse:Username', USERNAME)
    unt.add(un)
    pw = SOAP::SOAPElement.new('wsse:Password', PASSWORD)
    pw.extraattr['Type'] =
      'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'
    unt.add(pw)
    # Create a nonce...
    nonce = SOAP::SOAPElement.new('wsse:Nonce', Base64.encode64((rand() * 1000000000).to_i.to_s))
    nonce.extraattr['EncodingType'] =
      'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary'
    unt.add(nonce)
    # The time format returned by XSDDateTime isn't quite right, so we
    # fiddle with it a bit.
    unt.add(SOAP::SOAPElement.new('wsu:Created',
                                  XSD::XSDDateTime.new(Time.now.utc - 30).to_s.gsub(/..*/, "") + "Z"))
    security.add(unt)
    return SOAP::SOAPHeaderItem.new(security, true)
  end
end

proxy.headerhandler << WsseAuthHeader.new()

... call proxy methods to interact with web service ...

The key thing is to dress up the outgoing header with all the right XML, including Nonce and Created parameters. This code was created by matching the XML generated by the Java code used to interact with this “JAX-WS” web service.

All in all, it’s a bit of a mess compared to a nice REST style web service. Hopefully the above code will be useful to others who are in a similar situation.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s