Thursday, September 18, 2008

Debugging PHP Web Services

One of the difficulties of doing distributed computing, is the difficulty of debugging when things are not going well. It is no exception for Web Services. Since messages are exchanged between services, clients, we have to have some mechanisms  to capture messages, view the soap messages exchanged, some known error messages etc.

Capturing Messages

One of the most well known technique in debugging web services calls is to  some tcp message capture tool to capture the messages exchanged. There are few such good tools for this task.

1. TCPMON tool from apache

             This is a very simple and easy to use tool which has very wide adoption and is quite popular among developers. You can download this tool from apache tcpmon site. 

2. SOAP UI

            This is another popular tool for debugging and developing SOAP based applications. This tool is able to generate request SOAP message for a given WSDL where you fill out the values and send a valid soap request and response. This is a very useful tool to get started with Web Services. You can download soap UI from soapui.org

3.   Ethereal

         This is another tool that is quite useful is capturing and analyzing messages. http://thud.ethereal.com/

Using debugging functions from framework

you can you framework provided functions for debugging. For example WSF/PHP provides two functions getLastRequest() and getLastResponse() on the WSClient class which returns the exchanged request and response messages. Also it provides functions to obtains the returned HTTP headers. Using these, you can also do some debugging.

Logging

Logging is another useful techniques to idenfity possible problems. You can look as the wsf_php_client.log and wsf_php_server.log files and get an idea of what went wrong. If you are not able to trace what's wrong using the logs , you can post your problem with the appropriate log messages to the forum  and get support.

Using Known Error Messages

Another useful technique is to go through the known set of error messages to locate what went wrong. In WSF/PHP, here are some of the error messages you can get.

Monday, September 15, 2008

Implementing Rest Service and Client with WSF/PHP

WSO2 WSF/PHP 2.0.0 has support for HTTP verbs GET,POST,DELETE and PUT in its REST support alone with the ability to support custom URL mappings. Lets have a look at the WSF/PHP REST API and how to implement a simple client and service.

Usually, a REST system is build around CRUD ( Create, Read, Update, Delete ) model.

An HTTP POST Request is a creation of a resource. Similarly a GET request is reading, PUT request is updating and DELETE request is deleting.

You can find details of a design of a complex REST system here.

Implementing a PHP Web Service that can expose its operations as REST operations.

For exposing operations as REST operations, WSService constructor provides the option RESTMapping to specify each operation with its corresponding HTTP method and Custom URL that is mapped to the operation.

Lets consider an example.  Consider we have an operations createUserAccount, updateUserAccount, deleteUserAccount, and readUserAccount as the operations of a simple service called ManageUserAccounts.php

Then out operations map would be like

   1: $operations = array("createUserAccount" => "createAccount",


   2:                "updateUserAccount"=>"updateAccount",


   3:                 "deleteUserAccount"=>"deleteAccount",


   4:             "readUserAccount"=>"getAccount");




Here, the actual php functions implementing the business logic are  createAccount, updateAccount, deleteAccount and getAccount.



Now we can map these operations to an HTTP method and a custom URL, so that we can execute these same operations without having to use SOAP. 





   1: $restmap = array ("createUserAccount"=> array("HTTPMethod"=>"POST","RESTLocation"=> "users"),


   2:           "updateUserAccount"=> array("HTTPMethod"=>"PUT","RESTLocation"=> "users"),


   3:           "deleteUserAccount"=> array("HTTPMethod"=>"DELETE","RESTLocation"=> "users"),


   4:           "readUserAccount"=> array("HTTPMethod"=>"GET","RESTLocation"=> "users"));




Note how HTTPMethod is used to specify the HTTP Method that will be used to invoke each operation. For example, we will be using POST method to  invoke "createUserAccount" operation. 



RESTLocation option is used to specify a portion of a URL which will makeup the final URL of the operation. For example, if our service URL  is "http://localhost/samples/ManageUserAcconts.php" then, since we have set "users" as our RESTLocation, the final URL that will make the endpoint would be "http://localhost/samples/ManageUserAcconts.php/users". Note how I have used the same RESTLocation for all four operations. WSF/PHP is able to identify the correct operation using the HTTPMethod when the URL is the same.



Implementing the client



WSClient class which is the Client API in WSF/PHP API enables you to make use of  HTTP verbs POST,GET,DELETE and PUT when making a REST style operation invocation. 



First you need to know the REST endpoint where the resource is located. Consider we are invoking the "readUserAccount'' function defined in above service. Then our endpoint URL is  "http://localhost/samples/ManageUserAcconts.php/users" and HTTPMethod is GET. You can do this request using WSClient as well as using the Web Browser.





   1:     $client = new WSClient( array("to" => "http://localhost/samples/ManageUserAccounts.php/users",


   2:                                   "useSOAP" => FALSE,


   3:                                   "HTTPMethod" => "GET"));


   4:  


   5:     /** Request Payload String is the XML String send as argument to the operation, 


   6:         In this case, it will be send in query string */


   7:                 


   8:     $responseMessage = $client->request($requestPayloadString);




Similarly you can invoke other operations as well.  I will discuss more details in another blog post.

Thursday, July 31, 2008

New features to be released with WSF/PHP

Since the 1.3.2 release of WSF/PHP, a lot of new functionality has been added to the extension. In addition to that many bugs have been corrected. Following are some of the new features that will be released with the next release.

Improved MTOM/SwA attachment support

One of the limitations of WSF/PHP attachment support was that the service was not handling the http chunked messages. Also there was a limitation on the size of the attachment that can be received by the service.

We have recently fixed this issue. Now both the client and service is able to send binary attachments as chunked. Also with this addition, a service can receive an attachment of any size.

Another limitation we had was that, it was necessary to read the binary file to memory and then set it with the attachments array in order to send an attachment. Now if the file if a large file, this would require a large amount of memory. Now you can set provide the filename and enable caching in order to send the attachment in a memory efficient way.

PKCS12 Key Store support.

This was one of the essential features for implementing WS-Security. Since a access restricted service would want to provide its functionality to a limited number of previously specified clients, it should have the ability to keep track of the clients public certificates in addition to its private in in a single file. This functionality has been added to WSF/PHP.

Improved REST Support

It is quite handy to have the ability to expose you service operations as a REST operation in addition to having it as a soap operation. Now WSF/PHP allows you to map you operations to URL and a HTTP Method so that you can expose the operation as a REST Style operation as well.

PHP Data Services Solution

This is one of the most interesting solutions we have implemented using WSF/PHP. This provides a easy to use framework for exposing the data in you database tables as Web Services. This is compatible with WSO2 WSAS Data Service solution as well. This comes with the WSDL Generation, A database abstraction layer which allows you to use multiple databases and many more features.

WS-Secure Conversation support.

WSF/PHP is now able to support WS-Secure conversation. WSF/PHP allows you can configure a single service to act as a security token service (STS) in addition to providing the secured operations.

Operation Level Policy support

Now you can configure WS Security polices at Operation level with WSF/PHP.

MTOM Attachment Support for Contract First Web Services

This was one of the things that users have been asking for a while. Because of the simplicity of use, Contract first method is preferred by most users. Now WSF/PHP has the support to send and receive attachments in this mode of operation as well.

Sunday, July 13, 2008

How to implement replay detection with WSF/PHP

There are many ways in which a secure messaging system can be attacked. Replay is one such technique. Consider the following scenario. A malicious user who is capturing the encrypted messages exchanged between the client and a service might not be able to know what the exchanged message contains. But he may still be able to do some damage by replaying the messages, if the service is not able to detect whether a received message happen to be a previously received message or not. 

Now a web service secured using WSF/PHP has that capability with its API. Ideally scenario for a secure web service is that, first it should detect replay attack and send appropriate fault message.

Implementing this with WSF/PHP only requires you to implement a function with the specified function signature and pass the name of the function as an argument to the associative array of WSSecurityToken arguments array as "replayDetectionCallback"=>"<function name">.

Lets look at a simple example on how this happens. Each message exchanged between the secured web service , and a client has a unique message id. Sometimes it can contain a timestamp as well. So the replay detection function signature is defined as  bool function_name(string message_id, string time_created [,mixed args]). Here the args is an optional value which is needed in case you want to pass your own set of arguments to the reply detection function.

Now what the replay detection function should do maintain a list of message id+time create values, and check against this list where the newly received values are already in the records. If should the function should return false. If the newly received values are not in the list already there, then add them and return true. Now if the function returned false, WSF/PHP will send a SOAP Fault containing appropriate fault data to the client.Otherwise the request will be handled properly.

The list of records should be persistent. One of the easiest ways to do it is to write to a database. Otherwise you can use a file to maintain this data. In addition to that, you can implement additional features like how long the records should be kept ect.

Now lets look at a simple code example of a replay detection function.

 function array_contains($array_of_string, $str) {
    foreach ($array_of_string as $value) {
        if(strcmp($value, $str) == 0) {
            return TRUE;           
        }
    }
    return FALSE;
}

function replay_detect_callback($msg_id, $time_created) {
    $max_duration = 5;
    if (stristr(PHP_OS, 'WIN')) {
        $replay_file = "replay.content";
    }else{
        $replay_file = "/tmp/replay.content";
    }
    $list_of_records = array();   
    clearstatcache();   
    if(file_exists($replay_file))
    {
        $length = filesize($replay_file);
        $fp_rf = fopen($replay_file, "r");
        if(flock($fp_rf, LOCK_SH)) {
            $content = fread($fp_rf, $length);
            flock($fp_rf, LOCK_UN);
            $tok_rec = strtok($content, '@');
            while($tok_rec) {
                $list_of_records[] = $tok_rec;
                $tok_rec = strtok('@');
            }
        } else {
            echo "Couldn't lock the ".$replay_file." for reading!";
        }
        fclose($fp_rf);
    }else {
        $fp_rf_w = fopen($replay_file, "w");
        if(flock($fp_rf_w, LOCK_EX)) {
            fwrite($fp_rf_w, $msg_id.$time_created.'@');
            flock($fp_rf_w, LOCK_UN);
        } else {
            echo "Couldn't lock the ".$replay_file." for writing!";
        }
        fclose($fp_rf_w);
        return TRUE;
    }

    if(array_contains($list_of_records, $msg_id.$time_created)) {
        return FALSE;
    } else {
        $elements = count($list_of_records);
        if($elements == $max_duration) {
            $new_rcd_list = array_splice($list_of_records, 1);
            $new_rcd_list[] = $msg_id.$time_created;
            $fp_rf_w = fopen($replay_file, "w");
            if(flock($fp_rf_w, LOCK_EX)) {
                foreach($new_rcd_list as $value) {
                    fwrite($fp_rf_w, $value.'@');
                }
                flock($fp_rf_w, LOCK_UN);
            } else {
                echo "Couldn't lock the file for writing!";
            }
            fclose($fp_rf_w);
        } else {
            $list_of_records[] = $msg_id.$time_created;
            $fp_rf_w = fopen($replay_file, "w");
            if(flock($fp_rf_w, LOCK_EX)) {
                foreach($list_of_records as $value) {
                    fwrite($fp_rf_w, $value.'@');
                }
                flock($fp_rf_w, LOCK_UN);
            } else {
                echo "Couldn't lock the file for writing!";
            }

            fclose($fp_rf_w);
        }
    }

    return TRUE;
}

This function is quite simple. it writes each record received to a file named replay.content while implementing the above described logic.  Now lets look at how to use this WSSecurityToken to set this function.


$security_token = new WSSecurityToken(

array("user" => "Raigama", 

"password" => "RaigamaPW",

"passwordType" => "Digest",

"replayDetectionCallback" => "replay_detect_callback", 
"enableReplayDetect" => TRUE));

Note how the callback function is specified. In addition to specifying the callback function, you need to enable Replay detection functionality by setting the option "enableReplayDetect"=>TRUE.

Thursday, July 10, 2008

PKCS12 Key Store support added

WSF/PHP security API now has the pkcs12 key store support. Previously when implementing a service or a client that uses WS-Security, Sometimes it is necessary to make the service limited to a number of pre approved clients. To allow this functionality, it is necessary to obtain the approved clients public  keys and store them in a key store file in addition to the private key used by the service. PKCS12 is the commonly used file format to store X.509 private keys and public key certificates protected by a password.

 

Following is the API for using a PKCS12 Key store file.

WSSecurityToken object accepts an options array in its constructor. We added a new option "PKCS12KeyStore" for specifying the key store file as a string. Following is an example service using a key store file.

<?php

function echoFunction($inMessage)

{

$returnMessage = new WSMessage($inMessage->str);

return $returnMessage;

}

$keystore = file_get_contents("../keys/bob_kstore.p12");

$operations = array("echoString" => "echoFunction");

$sec_array = array("encrypt" => TRUE,

                            "algorithmSuite" => "Basic256Rsa15",

                            "securityTokenReference" => "IssuerSerial");

$actions = array("http://php.axis2.org/samples/echoString" => "echoString");

$policy = new WSPolicy(array("security"=> $sec_array));

$sec_token = new WSSecurityToken(array("PKCS12KeyStore" => $keystore,

                                                                   "user"=>"b",

                                                                   "password"=>"b12345"));

$svr = new WSService(array("actions" => $actions,

                                             "operations" => $operations,

                                             "policy" => $policy,

                                            "securityToken" => $sec_token));

$svr->reply();

?>

Note how the PKCS12 key store file is obtained as an string using the file_get_contents function and specified using the option "PKCS12KeyStore" option.

Thursday, May 29, 2008

WSO2 WSF/PHP 1.3.1 Released

We did a bug fix release of 1.3.0 release as 1.3.1.

Tutorial on handling attachments with Axis2/C

I wrote a small know how tutorial on handling MTOM and SwA attachments with Axis2/C