How to add transparent SoapHeader authentication as decorator to any existing service class
This should not be limited to it, but I’d like to show how I implemented this inside the Zend Framework and its Zend_Soap_* components.
SoapHeader authentication is a widely used method to secure access to soap Webservices where the credentials are in the request header.
Activating this type of authentication is for me down to a change from this:
$soap = new Zend_Soap_Server($uri.'&wsdl', $serverOptions);
$soap->setClass('My_Service_'.$serviceName);
to this:
$soap = new Zend_Soap_Server($uri.'&wsdl', $serverOptions);
$soap->setClass('My_Soap_Decorator_Secure', 'My_Service_'.$serviceName);
This is the basic decorator code:
/**
* This class decorates Soap service classes, provides and enforces authentication via soap header 'authenticate'
*
* @author Karsten Deubert <karsten@deubert.net>
*/
class My_Soap_Decorator_Secure
{
/**
* @var bool
*/
protected $_authenticationHeaderPresent = false;
/**
* @var mixed
*/
protected $_authenticatedUser = null;
/**
* @var mixed
*/
protected $_serviceClass = null;
public function __construct($class)
{
if (!class_exists($class))
{
throw new Exception('invalid class: '.$class);
}
$this->_serviceClass = new $class();
}
/**
* @param mixed $data
* @return void
*/
public function authenticate($data)
{
$this->_authenticationHeaderPresent = true;
// authentication code which checks if credentials are valid
$this->_authenticatedUser = $yourAuthenticatedUser;
}
public function __call($name, $arguments)
{
if (!$this->isAuthenticationHeaderPresent() || is_null($this->_authenticatedUser))
{
throw new Exception('authentication failed');
}
if (!is_callable(array($this->_serviceClass, $name)))
{
throw new Exception('invalid service class method');
}
return call_user_func_array(array($this->_serviceClass, $name), $arguments);
}
}
The usual soap request with authentication header should now look like this:
$authData = new stdClass();
$authData->user = 'foo';
$authData->secret = 'bar';
$authHeader = new SoapHeader($namespace, 'authenticate', $authData);
$soapClient = new SoapClient('http://foo.bar/asdf?wsdl',
array(
'cache_wsdl' => 0,
'soap_version' => SOAP_1_1
)
);
$soapClient->__setSoapHeaders(array($authHeader));
$soapClient->fooMethod();
With this header the soap server will first execute the authenticate method from the decorator, then (if successful) pass the method call via magic __call to the inner service class and its fooMethod() in this example.
Voila, transparent SoapHeader authentication separated from your service classes
What I haven’t researched fully yet is if there is a way to specify the authenticate header in the WSDL – every comment appreciated.



email me
follow me
