Archives for the month of: July, 2008

I recently needed to pass a WSSE authentication header with some SOAP messages sent from a Rails app to a government service. Ruby’s native SOAP library is soap4r. It’s featureful, but under-documented, so it took some fiddling to get WSSE to work.

WSSE is a pretty simple standard for sending a username and password to a service in a SOAP message’s header. The layout is:

<wsse:Security xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/07/secext" soap:mustUnderstand="0">
  <wsse:UsernameToken>
    <wsse:Username>Username</wsse:Username>
    <wsse:Password>Password</wsse:Password>
  </wsse:UsernameToken>
</wsse:Security>

 

Constructing the WSSE headers

Adding headers in soap4r requires constructing an object that responds to a method that will return the headers to be added. A helper class provided for this is SOAP::Header::SimpleHandler. SimpleHandler allows us to respond with a hash that is converted into XML.

SimpleHandler also expects subclasses to call its initialize method with an enclosing tag. We can use xsd4r (the XML parser/constructor used by soap4r) to create the tag and define its namespace:

XSD::QName.new(NAMESPACE, 'Security')

Our call back method is a hash containing the username and password to be sent:

{"UsernameToken" => {"Username" => USERNAME, "Password" => PASSWORD}}

The entire class looks like this:

require 'soap/header/simplehandler'

class WsseAuthHeader < SOAP::Header::SimpleHandler
  NAMESPACE = 'http://schemas.xmlsoap.org/ws/2002/07/secext'
  USERNAME  = 'username'
  PASSWORD  = 'password'

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

  def on_simple_outbound
    {"UsernameToken" => {"Username" => USERNAME, "Password" => PASSWORD}}
  end
end

 

Adding the WSSE headers

Adding the headers to a soap4r driver is quite easy (this has only been tested with a driver that has been auto-generated from a WSDL file).

require 'soap_driver.rb'
require 'wsse_authentication.rb'
d = Driver.new
...
d.headerhandler << WsseAuthHeader.new()

Postgres has support for 8 byte integers with bigint’s, and ActiveRecord natively supports reading & writing these values in Postgres (both on 32 and 64 bit machines, thanks to Fixnum and Bignum). Using them in AR migrations is actually pretty easy as well, but there’s some fudgery required to use 8 byte ints as an id if we don’t want to patch AR.

 

To create an 8 byte field:

create_table :example do |t|
  t.integer  :eight_byte_integer, :limit => 8
end 

 

To create a table with an 8 byte id without patching:

create_table :example do |t|
  ... fields
end

change_column :example, :id, :integer, :limit => 8

Note: id sequences are automatically created as bigint’s, so all we’re doing here is allowing the table’s id field to store all the values the sequence will create.

 

Alternatively, we can patch ActiveRecord to automatically make all primary keys bigserial’s. It’s better to patch a frozen version of Rails (in your vendor folder) than your installed gem.

file: vendor/rails/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -283,7 +283,7 @@

       def native_database_types #:nodoc:
         {
-          :primary_key => "serial primary key",
+          :primary_key => "bigserial primary key",
           :string      => { :name => "character varying", :limit => 255 },
           :text        => { :name => "text" },
           :integer     => { :name => "integer" },