Yesterday’s blog on “Using Username token in Authentication” I explained a standard way of authenting SOAP messages in Application layer (Message level Authentication). Anyway you can authenticate SOAP messages in transport level itself. For an example with HTTP Transport we can use the HTTP Basic Authentication for this purpose.

Setting up a client with Authentication Information

With WSF/PHP you can give the username, password and the authentication type as options for WSClient constructor.

	$client = new WSClient(array ("to" => "http://server/myendpoint",
		"httpAuthUsername" => "user",
		"httpAuthPassword" => "user_password",
		"httpAuthType" => "basic"));

Setting up the Server to Handle the Authentication

Since the Http authentication is handled by the transport level, you have to configure your authentication information in your web server itself. (e.g. Apache or IIS).

If you are using Apache, please use this guide to configure your allowed list to access the server.

Username token is a simple token sent inside SOAP message header element with username and password information.  It is used to authenticate SOAP messages in a standardized way.

Sending Username Token

To send username token with WSF/PHP you can use the generic API designed to implement WS-Security scenarios.

  • First you need to declare the security policy saying you are using username token. You can do this either with a policy which is complaint with WS-Security Policy standards or using an associative array. Here we use the second approach which is more PHP-Friendly.
    array("useUsernameToken" => TRUE)
  • With a WSSecurityToken instance we are giving our user parameters. In this case it is username, password and the password type.
    $security_token = new WSSecurityToken(array("user" => "my_username",
                                                    "password" => "my_password",
                                                    "passwordType" => "Digest"));
  • And create the WSClient object with policy and the security token object you just created + with “useWSA” on.  This is to enable the addressing headers in the request message which guide the server to identify the service and the operation.

Here is the complete code for the client.

    // Set up security options
    $security_options = array("useUsernameToken" => TRUE );
    $policy = new WSPolicy(array("security" => $security_options));
    $security_token = new WSSecurityToken(array("user" => "my_username",
                                                "password" => "my_password",
                                                "passwordType" => "Digest"));

    // Create client with options
    $client = new WSClient(array("useWSA" => TRUE,
                                 "policy" => $policy,
                                 "securityToken" => $security_token));

    // Send request and capture response
    $resMessage = $client->request($reqMessage);

Handling Username Token at Server Side
The same options (“policy” and “securityToken”) you gave to WSClient, can be given to WSService object as well. But hard coding values for “username” and “password” in SecurityToken is not much useful at the server side. Because it authenticate only one user. So in order to maintain multiple accounts, you have to have a callback function in php.

// callback function
function my_passwd_callback_function($username)
{
    // logic to return the password for the username
    return $password
}

// setting it to the security token
$sec_token = new WSSecurityToken(array("passwordCallback" => "my_passwd_callback_function",
                                       "passwordType" => "Digest"));

Here in the function you return the password for the username from a database and that information will be used to authenticate the request message.

In PHP you have several ways of sending binary data. It can be primarily categorized in to non-optimized method (send as base64 binary) and optimized method (send as MTOM or SWA). Here I m talking about how to send binaries in above mentioned methods starting from a WSDL.

WSDL

Think you have a WSDL with the following XML Schema.

            <!-- Here is my submitPerson method->

            <xs:element name="submitPerson">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="name" type="xs:string"/>
                        <xs:element name="age" type="xs:int"/>
                        <xs:element name="photo" type="xs:base64Binary"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>

The submit Person method submit the name,age and a photo which is a type of base64Binary.

Generated Class

After generating php class for this piece of code using wsdl2php you will have  the following class.

class submitPerson {

    /**
     * @var string
     */
    public $name;

    /**
     * @var int
     */
    public $age;

    // You need to set only one from the following two vars

    /**
     * @var Plain Binary
     */
    public $photo;

    /**
     * @var base64Binary
     */
    public $photo_encoded;

}

So it is very easy to fill the class with your own data. Note that you only need to fill one of the ‘photo’ or ‘photo_encoded’ fields. If you have binary already converted to base64 then you can use the ‘photo_encoded’ field where as if you only have the row binary just use the ‘photo’ field. Here are my values for this particular example.

$person = new submitPerson();
$person->name = "xxxx yyy";
$person->age = 35;
$person->photo = file_get_contents("/photo/xxxyyy");

Sending Binary as MTOM

Here is the code you need to send the above structure as a MTOM message.

$client = new WSClient(array("useMTOM" => TRUE)); // anyway useMTOM is default to TRUE for WSClient
$proxy = $client->getProxy();

$response = $proxy->submitPerson($person);

Sending Binary as Base64 Encoded string

You only need to change one option. That is setting “useMTOM” to FALSE will send the binary as Base64.

$client = new WSClient(array("useMTOM" => FALSE));
$proxy = $client->getProxy();

$response = $proxy->submitPerson($person);

SWA (SOAP With Attachments)
You can send the binary data as SWA by setting the “useMTOM” option to “SWA”. SWA is also a binary optimized method of sending attachments, but unlike with MTOM you can’t integrate security or reliability with this approach.

September 21st, 2008WSDL2PHP 2 Minutes Introduction

WSDL2PHP makes the development of web service providers and consumers quick and easy. I wrote a 2 minutes guide on developing web services providers sometimes ago. So lets concentrate on developing web service consumers here.

Where is WSDL2PHP?

WSDL2PHP script is included in the WSF/PHP packs. You can find the wsdl2php.php script inside the ‘scripts’ directory of any source or binary package. Or you can use the online wsd2php tool hosted in WSF/PHP web services DEMO Site.

How to Run the Script?

Here is the command

/scripts/wsdl2php.php mywsdl.wsdl > myclient.php

The Code is Generated. How Can I add My Code There?

It is simple. Just search for the comment “//TODO”.
Check for an example here.

You have to write custom code for 2 occasions per operation.

  1. To Provide Input Parameters
  2. To Handle output parameters.

An Example?

Here is the code snippet corresponding to the simpleAdd request for our demo WSDL.

    $input = new simpleAdd();
    //TODO: fill in the class fields of $input to match your business logic

    // call the operation
    $response = $proxy->simpleAdd($input);
    //TODO: Implement business logic to consume $response, which is of type simpleAddResponse

Here is how after I filled my logic in place of TODO comments.

    $input = new simpleAdd();
    //DONE: fill in the class fields of $input to match your business logic

    //-------my code----
    $input->param0 = 2;
    $input->param1 = 3;
    //------------------

    // call the operation
    $response = $proxy->simpleAdd($input);
    //DONE: Implement business logic to consume $response, which is of type simpleAddResponse

    //--------my code-----
    echo $response->return;
    //--------------------

WSF/PHP allows you to develop web services with both Contract First (Starting from WSDL) and Code First Approaches. From these two, Contract First approach is the most famous and most recomended way of developing a Webservice. There you need to have a WSDL to start with. I will take the http://labs.wso2.org/wsf/php/example.xml as our WSDL.

Generate The Service Code

When you download the WSF/PHP pack there is a script called ‘wsdl2php.php’ in the script directory. Open a command line and run it with the following options. Or you can use the online wsdl2php tool

php wsdl2php.php http://labs.wso2.org/wsf/php/example.xml -s

Complete The Business Logic

Complete the TODO section of the generated file. Apparently what is left to do is filling the business logic for each function corresponding to the service operations in your WSDL. You are given the hint to the input and output parameters. Here is how I fill the logic for the simpleAdd function.

function simpleAdd($input) {
    // TODO: fill in the business logic
    // NOTE: $input is of type simpleAdd
    // NOTE: should return an object of type simpleAddResponse

    $res = new simpleAddResponse();

    $res->return = $input->param0 + $input->param1;

    return $res;
}

Advanced Types? – Read Generated Hints for Class variables

So how about filling the matrix add logic. Can be it be really complex?. No, what you need to observe is there the class member variables contains not only simple types but also another Class type. There is a comment on each member variables to hint its type. So the logic for the matrix addition is simple as this,

function matrixAdd($input) {
    // TODO: fill in the business logic
    // NOTE: $input is of type matrixAdd
    // NOTE: should return an object of type matrixAddResponse

    $matrix0 = $input->param0;
    $matrix1 = $input->param1;

    $matrix2 = new Matrix();
    $matrix2->rows = array();

    if(count($matrix0->rows) == count($matrix1->rows)) { // considering only happy path

        for($i = 0; $i < count($matrix0->rows); $i ++ ) {

            $row0 = $matrix0->rows[$i];
            $row1 = $matrix1->rows[$i];

            $row2 = new MatrixRow();
            $matrix2->rows[] = $row2;
            $row2->columns = array();

            if(count($row0->columns) == count($row1->columns)) {

                for($j = 0; $j< count($row0->columns); $j ++ ) {

                    $col0 = $row0->columns[$j];
                    $col1 = $row1->columns[$j];

                    $col2 = $col0 + $col1;

                    $row2->columns[] = $col2;
                }
            }
        }
    }

    $res = new matrixAddResponse();
    $res->return = $matrix2;
    return $res;
}

We use the term DataServices for exposing a Database as a WebService. PHP DataServices library make this task easy by providing a very simple API to the developers.

The API is designed to let the service developer to provide the database configurations, input message format, output message format and the SQL query information. So reading and parsing the input SOAP message, building the response SOAP message from the queried data, serving the WSDL and providing the Database independent layer (with the help of PDO extensions) are done by the Dataserivce library.

Configurations

You give your database configurations in this format.

// database configuraitons
$config = array(
      "db" => "mysql", //your sql
      "username" => DB_USERNAME, //your username
      "password" => DB_PASSWORD, //your password
      "dbname" => "ds", //your database name
      "dbhost" => "localhost"); //your database host

Input Format

You have to declare the names and the types of the content of your input message. This is needed to generate the WSDL and invoke the database query with parameters.

// input format array(param_name => SQL_TYPE)
$inputFormat = array("customerNumber" => "INT");

Output Format

You can customize your response SOAP message by providing the names of the wrapper elements  for both the message and the rows.

// output format, plese check the API from http://wso2.org/wiki/display/wsfphp/API+for+Data+Services+Revised
$outputFormat = array("resultElement" => "Orders", //the name of the wrapper element of the message
                      "rowElement" => "Order", //the name of the wrapper element of each row
                      "elements" => array( "order-number" => "OrderNumber",
                                           "order-date" => "OrderDate",
                                           "status" => "status"));

Create the Query

The query in the PHP dataserivces term is not just the SQL query that you are familiar in accessing a database, It contains the input and output formats in addition to the actual SQL query to invoke.

// sql statment to execute
$sql="select o.OrderNumber, o.OrderDate, o.status from Customers c, Orders o where c.customerNumber=o.customerNumber and c.customerNumber=?";

// operations are consist of inputFormat (optional), outputFormat(required), sql(sql), input_mapping(optional)
$operations = array("customerOrders" =>array("inputFormat" => $inputFormat, "outputFormat" => $outputFormat, "sql" => $sql));

DataService object

Finally you create the DataService object with the provided configurations and the query information.

require_once("wso2/DataServices/DataService.php");

$my_data_service = new DataService(array("config" => $config,"operations" => $operations));

$my_data_service->reply();

Whenever you deploy a service script in WSF/PHP you can access its WSDL by adding “?wsdl” or “?wsdl2″ (for WSDL version 2.0) to the service endpoint. Anyway you may have noticed that for some services it will just list the operations with xsd:anyType as the input and output message schemes.

For an example for an operation like this,

<?php

function getInvoice($order_no) {
   //something that return an invoice for this order 
}

$operations = array("getInvoice" => "getInvoice");

$svr = new WSService(array("operations" => $operations));

$svr->reply();
?>

This will generate you a nice WSDL but with a schema similar to this,

<xsd:element name="getInvoice">
    <xsd:complexType>
       <xsd:sequence>
           <xsd:element name="order_no" type="xsd:anyType"/>
       </xsd:sequence>
    </xsd:complexType>
</xsd:element>

This schema is not enough for get an understanding about the request, response message formats, because it uses xsd:anyType to represent a general message. In order to get more specific schema, you need to provide more details to the WSDL generator. Here is how it is done using annotations.

<?php
/**
 * @param int $orderNo Order Number
 * (maps to xsd:int)
 * @return string $invoice Invoice as a string
 * (maps to xsd:string)
 */
function getInvoice($order_no) {
   //something that return an invoice for this order 

}

$operations = array("getInvoice" => "getInvoice");
$opParams = array("getInvoice" => "MIXED"); // Have to declare parameters as MIXED

$svr = new WSService(array("operations" => $operations, "opParams" => $opParams));

$svr->reply();
?>

Now check the schema. It will gives you the information specific to your request, response messages.

<xsd:schema elementFormDefault="qualified"
    targetNamespace="http://www.wso2.org/php/xsd">
    <xsd:element name="getInvoice">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="orderNo" type="xsd:int"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
    <xsd:element name="getInvoiceResponse">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="invoice" type="xsd:string"/>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

So it is your chance to do more experiment with this feature. Check WSDL generation API for more snippets.

Building and Traversing XML are regular tasks for any Web Service developer. We may use DOM, AXIOM or even simple String manipulation functions to do that. Normally this takes a lot of time and effort. And if we are coding in ‘C’ it become more tedious as string manipulation in ‘C’ is not that straight forward.

What ADB does is it generates a set of ‘C’ functions specially for our XML Schema to build and traverse the XML.
Say you have the following XML Schema. (You need to have it inside a WSDL to generate the code).

        <xs:schema targetNamespace="http://dimuthu.org/adb/demo/2008/sept">
            <!-- demonstrating element-->
            <xs:element name="myDemo">
                <xs:complexType>
                    <xs:sequence>
                        <xs:element name="demo1" type="xs:int"/>
                        <xs:element name="demo2" type="xs:string"/>
                    </xs:sequence>
                </xs:complexType>
            </xs:element>
        <xs:schema/>

For this particular element there will be one adb type (adb_myDemo_t), one source (adb_myDemo.c) and one header (adb_myDemo.h) are generated. Don’t ever look at the source file (unless you want to hack it) just go through the header, it will have all the function you need to manipulate your ADB.

It will have following set of functions,

To create and free the object
  • adb_myDemo_create
  • adb_myDemo_free
Getters and Setters to manipulate Data
  • adb_myDemo_get_demo1
  • adb_myDemo_set_demo1
  • adb_myDemo_get_demo2
  • adb_myDemo_set_demo2
Build and Parse XML
  • adb_myDemo_serialize
  • adb_myDemo_deserialize

First we will check how to build a simple XML using an ADB object.

    adb_myDemo_t *mydemo = adb_myDemo_create(env);
    adb_myDemo_set_demo1(mydemo, env, 3); /* some arbitrary value */
    adb_myDemo_set_demo2(mydemo, env, "some arbitrary string");
    axiom_t *xml = adb_myDemo_serialize(mydemo, env, NULL, NULL, AXIS2_TRUE, NULL, NULL);

So now we have the AXIOM representation of the XML which we just build using ADB.

<ns0:myDemo xmlns:ns0="http://dimuthu.org/adb/demo/2008/sept">
    <ns0:demo1>3</ns0:demo1>
    <ns0:demo2>some arbitrary string</ns0:demo2>
</ns0:myDemo>

Anyway ADB is easier when you want to navigate through an XML. (you can read data randomly as you preferred). Here we starting from the AXIOM representation of our XML.

      adb_myDemo_t *mydemo= adb_myDemo_create( env);
      adb_myDemo_deserialize(mydemo, env, &xml, NULL, AXIS2_FALSE))
      int demo1 = adb_myDemo_get_demo1(mydemo, env);
      printf("My Demo1 is: %d\\n", demo1);
      axis2_char_t *demo2 = adb_myDemo_get_demo2(mydemo, env);
      printf("My Demo2 is: %s\\n", demo2);

I have printed the Data contained in the XML with the help of ADB objects.

In ordre to download the Codegen tool with ADB you have to download the Axis2/Java package.

If you want to learn more about ADB, Just go through more Axis2/C Codegen / ADB resources which I have blogged few weeks ago.


© 2007 Dimuthu’s Blog | iKon Wordpress Theme by Windows Vista Administration | Powered by Wordpress