Axis2C, WS-Security & Perl

I've been wanting to build a Perl client to consume an Axis2C web service protected by Rampart/C for a while now.  So over the past couple days I took a few hours to figure out how.  First I needed a username/password protected service to test with.  I decided to use the "math" sample service that comes with the axis2c download.  I got axis2c, along with a bunch of other related stuff from WSO2's <a href="http://wso2.com/products/web-services-framework/c/" >Web Services Framework for C</a>.  For the most part, this just packages up a lot of Apache open source software like Axis2C and Rampart/C into one tidy tar file.  Anyway, the math sample service doesn't come configured with rampart security right out of the gate, so the first thing I had to do was modify the services.xml file in $WSFC_HOME/services/math to add it.  Here's what my file looked like after adding the entries for Rampart:

<service name="math">
 <parameter name="ServiceClass" locked="xsd:false">math</parameter>
 <description>
   This is a testing service, named 'math' to test multiple operations in the same service
 </description>
 <module ref="rampart"/>

 <wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
   <wsp:ExactlyOne>
     <wsp:All>
       <sp:AsymmetricBinding xmlns:sp="
http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">
         <wsp:Policy>
           <sp:InitiatorToken>
             <wsp:Policy>
               <sp:X509Token sp:IncludeToken="
http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Never">
                 <wsp:Policy>
                   <sp:WssX509V3Token10/>
                 </wsp:Policy>
               </sp:X509Token>
             </wsp:Policy>
           </sp:InitiatorToken>
           <sp:RecipientToken>
             <wsp:Policy>
               <sp:X509Token sp:IncludeToken="
http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Never">
                 <wsp:Policy>
                   <sp:WssX509V3Token10/>
                 </wsp:Policy>
               </sp:X509Token>
             </wsp:Policy>
           </sp:RecipientToken>
           <sp:Layout>
             <wsp:Policy>
               <sp:Strict/>
             </wsp:Policy>
           </sp:Layout>
         </wsp:Policy>
       </sp:AsymmetricBinding>
       <sp:SignedSupportingTokens xmlns:sp="
http://schemas.xmlsoap.org/ws/2005/07/securitypolicy">                
         <wsp:Policy>
           <sp:UsernameToken sp:IncludeToken="
http://schemas.xmlsoap.org/ws/2005/07/securitypolicy/IncludeToken/Once"/>
         </wsp:Policy>                
       </sp:SignedSupportingTokens>
       <rampc:RampartConfig xmlns:rampc="
http://ws.apache.org/rampart/c/policy">
         <rampc:PasswordCallbackClass>
           /PATH/TO/CALLBACK_LIBRARY.SO
         </rampc:PasswordCallbackClass>
       </rampc:RampartConfig>
     </wsp:All>
   </wsp:ExactlyOne>
 </wsp:Policy>

 <operation name="add">
   <!--messageReceiver class="axis2_receivers" /-->
 </operation>
 <operation name="sub">
   <!--messageReceiver class="axis2_receivers" /-->
 </operation>
 <operation name="mul">
   <!--messageReceiver class="axis2_receivers" /-->
 </operation>
 <operation name="div">
   <!--messageReceiver class="axis2_receivers" /-->
 </operation>
</service>

For the value of this tag:

  <rampc:PasswordCallbackClass>
    /PATH/TO/CALLBACK_LIBRARY.SO
  </rampc:PasswordCallbackClass>

You just need to replace it with a callback library that will return the password for the username sent in the request.  In the WSO2 bundle a sample callback library can be found in rampartc/samples/callback.  That's what I used for this test.  More info for the rampart configuration of an axis2c service can be found here.

With that now configured I can move on to my Perl client.  I wanted to see the requests and responses that were being sent - figured it would be helpful to troubleshoot - so I used tcpmon.  tcpmon also comes bundled in the WSO2 package and once built and installed you can find it in $WSFC_HOME/bin/tools/tcpmon.

First thing I just wanted to see what would happen if I called the protected service with no credentials in my soap header.  Here's my simple Perl client with no security stuff added:

#!/usr/bin/perl -w

use SOAP::Lite;

my $service_call = SOAP::Lite
-> uri('http://ws.apache.org/axis2/services/math')
-> proxy('http://localhost:9099/axis2/services/math/add');

my $param1Element = SOAP::Data->name('param1')->value('1');

my $param2Element = SOAP::Data->name('param2')->value('2');

my $response = $service_call->add(
$param1Element
, $param2Element
);

print "The result is " . $response->dataof('//result')->value . "\n";

Pretty simple.  With this setup here's what my request looks like:

    <soap:Body>
        <param1 xsi:type="xsd:int">1</param1>
        <param2 xsi:type="xsd:int">2</param2>
      </add>
    </soap:Body>
  </soap:Envelope>

And here's the response:
    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Header></soapenv:Header>
    <soapenv:Body>
      <soapenv:Fault>
        <faultcode>soapenv:Client</faultcode>
        <faultstring>Array list index out of bounds</faultstring>
      </soapenv:Fault>
    </soapenv:Body>
  </soapenv:Envelope>

Honestly I'm not too sure what that error message means.  I see it fairly often with this version of WSO2 (1.3) and I think it's probably a bug (maybe a subject for another post).  An excerpt from my HTTP server log is a little more helpful:

[Mon Nov 22 05:05:04 2010] [error] rampart_in_handler.c(106) [rampart][rampart_in_handler] SOAP header cannot be found.

Now we're getting somewhere.  Rampart expected some security info in the SOAP header and didn't find any.  Let's fix that.  Here's the new version of my perl script with the code added to build my security header.

#!/usr/bin/perl -w

use SOAP::Lite;

my $service_call = SOAP::Lite
-> uri('http://ws.apache.org/axis2/services/math')
-> proxy('http://localhost:9099/axis2/services/math/add');

my $param1Element = SOAP::Data->name('param1')->value('1');

my $param2Element = SOAP::Data->name('param2')->value('2');

my $username = 'alice';
my $password = 'password';

my $security=SOAP::Header->name(
"wsse:Security"
)->attr(
{'soap:mustUnderstand'=>1,'xmlns:wsse'=>'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'}
);


my $userToken =SOAP::Header->name(
"wsse:UsernameToken" => \SOAP::Header->value(
SOAP::Header->name(
'wsse:Username'
)->value(
$username
)->type(
''
)
, SOAP::Header->name(
'wsse:Password'
)->value(
$password
)->type(
''
)->attr(
{'Type'=>'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText'}
)
)
)->attr({'xmlns:wsu'=>'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'});

my $response = $service_call->add(
$param1Element
, $param2Element
, $security->value(
\$userToken
)
);

print "The result is " . $response->dataof('//result')->value . "\n";

I certainly cannot take credit for this code.  I needed help and found it here.  The stuff in red is new.  It just manually builds the security tags to put in the SOAP header.  I like it that it's so easy to build tags and add them with SOAP::Lite, but it seems like there would be some built-in functions to do the WS-Security stuff.  Perhaps there is and I haven't found it yet.  Nevertheless, it does the trick.  With the new code added here's the request:

  <soap:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soap:mustUnderstand="1">
        <wsse:Username>alice</wsse:Username>
      </wsse:UsernameToken>
    </wsse:Security>
  </soap:Header>
  <soap:Body>
      <param1 xsi:type="xsd:int">1</param1>
      <param2 xsi:type="xsd:int">2</param2>
    </add>
  </soap:Body>
</soap:Envelope>

And here's the response.  Apparently 1+2 does equal 3.

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Header>
    <wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" soapenv:mustUnderstand="1">
    </wsse:Security>
  </soapenv:Header>
  <soapenv:Body>
    <ns1:result xmlns:ns1="http://axis2/test/namespace1">3</ns1:result>
  </soapenv:Body>
</soapenv:Envelope>

SOAP::Lite on Slicehost's Ubuntu Hardy

I'm wanting to start doing some experimenting with Perl client applications for web services written using axis2c.  Never actually written a Perl script that consumes a web service, but it appears with SOAP::Lite it shouldn't be too hard.  This module doesn't appear to be installed with the standard Slicehost Ubuntu build, but Perl 5.8.8 is already on there.  I tried the CPAN command from SOAP::Lite's website, but this didn't seem to work for me.  So, I downloaded the tar file for SOAP::Lite.  When I tried to build it, I noticed I was missing the required XML::Parser module.  

We are about to install SOAP::Lite and for your convenience will provide
you with list of modules and prerequisites, so you'll be able to choose
only modules you need for your configuration.

XMLRPC::Lite, UDDI::Lite, and XML::Parser::Lite are included by default.
Installed transports can be used for both SOAP::Lite and XMLRPC::Lite.

Press <enter> to see the detailed list.  

Feature                       Prerequisites                Install?
----------------------------- ---------------------------- --------
Core Package                  [*] Scalar::Util             always  
                              [*] URI                              
                              [*] constant                         
                              [*] Test::More                       
                              [*] MIME::Base64                     
                              [*] Class::Inspector                 
                              [ ] XML::Parser (v2.23)              
                              [*] Task::Weaken                     

After downloading it from here and building per the README instructions, I ran the make commands for SOAP::Lite again and I'm good to go.  Yay.

root@ezraxu:/usr/lib/perl/5.8.8# perldoc -l SOAP::Lite
/usr/local/share/perl/5.8.8/SOAP/Lite.pm

Setting Timezone in Ubuntu Hardy

I've been using server space on slicehost for quite some time to host some websites for family members with businesses and as a place for me to tinker. Even though it's been well over a year since I've had it, I'm ashamed to say I never bothered to set the time. Today I decided to fix it, but really didn't know how since my timezone appeared to be incorrect. First, I needed to figure out what version of ubuntu was running, so I used this command:

 
root@ezraxu:/home/george# lsb_release -a 
No LSB modules are available. 
Distributor ID:	Ubuntu 
Description:	Ubuntu 8.04.1 
Release:	8.04 
Codename:	hardy 

After a bit of searching, appears "tzconfig" is the common way to do this, but is not an option on this particular version of Ubuntu.  This forum led me to the command "dpkg-reconfigure tzdata".  This is an interactive application that allows you to set the timezone you're in (by the way, CST is GMT-6 - had to look that up too).  After setting the timezone I used "date" to set my time.  The format to use for setting the time using "date" isn't very straightforward.  It tells you how using the --help option, but I guess I just didn't understand it at first.  To set the time to '11/14/2010 2:40 PM' for example, I ran "date 111414202010.00".  Also, you'd probably figure this out anyway, but both of these commands require root access.