Logaholic.de

Avatar

queer as code!

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.

Bookmark and Share

How to fail ant builds with xbuild

While setting up a new mono (C# with xbuild) project with ant as build tool I discovered that xbuild always exits with return code 0.

Exit code 0 should always stand for “process finished, everything went fine” – ant consequently concludes that every xbuild run is a success… which is not always true.

Since I need that information to control wether to perform further steps in the project (auto-deployment to test system) or not, I am now scanning the xbuild output for “Build FAILED.” like this in my ant build.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project name="foobar" default="build" basedir=".">
    <target name="xbuild">
        <exec executable="xbuild" failonerror="true" dir="${basedir}" vmlauncher="false" outputproperty="xbuild.output" />
        <echo message="${xbuild.output}" />
    </target>

    <target name="failbuild-on-error">
        <fail message="Build Failed">
            <condition>
                <contains string="${xbuild.output}" substring="Build FAILED." />
            </condition>
        </fail>
    </target>

    <target name="build" depends="xbuild,failbuild-on-error"/>
</project>
Bookmark and Share

Zend_MVC, Controller Plugins and Annotations

While it is common practice in many enterprise grade environments to use annotations to not only document but also influence code, I am missing some of this goodness in Zend Framework MVC (1.x) applications.

Recently I had the idea to influence Controller Actions with annotations but discarded it with thoughts like “In PHP I will have to use reflection and some black magic to get this working which will have insane performance hits for my applications”.
… until I set everything up to see that it costs just 1-2ms in average per request without any form of caching.

As example this is code I use to set the used layout script via @layout annotation:

Before:

public function fooAction()
{
    $this->_helpers->layout->setLayout('bar');
    //...
}

After:

/**
 * @layout bar
 */
public function fooAction()
{
    //...
}

Abstract Class (reflection = php5.3) to get the action’s docblock

This needs more work to reflect the configurable module/controller directory and prefixDefaultModule Zend_MVC settings – this may not work for you out of the box.

/**
 * @author Karsten Deubert <karsten@deubert.net>
 */
abstract class My_Controller_Plugin_Annotation_Abstract extends Zend_Controller_Plugin_Abstract
{
    /**
     * @return string
     */
    protected function _getDocblockForControllerAction()
    {
        /** @var $request Zend_Controller_Request_Http */
        $request = $this->getRequest();

        $frontController = Zend_Controller_Front::getInstance();

        /** @var $dispatcher Zend_Controller_Dispatcher_Standard */
        $dispatcher = $frontController->getDispatcher();

        $controllerName =
            ucfirst($dispatcher->formatModuleName($request->getModuleName()))
            .'_'
            .$dispatcher->formatControllerName($request->getControllerName());

        $controllerPath =
            $frontController->getModuleDirectory($request->getModuleName())
            .DIRECTORY_SEPARATOR
            .$frontController->getModuleControllerDirectoryName()
            .DIRECTORY_SEPARATOR
            .$dispatcher->formatControllerName($request->getControllerName())
            .'.php';

        require_once $controllerPath;

        $actionName = $dispatcher->formatActionName($request->getActionName());

        $reflectionClass = new ReflectionClass($controllerName);
        if (!$reflectionClass->hasMethod($actionName))
        {
            return '';
        }

        $reflectionMethod = $reflectionClass->getMethod($actionName);

        $docBlock = $reflectionMethod->getDocComment();
        return $docBlock;
    }
}

Controller Plugin code:

/**
 * @author Karsten Deubert <karsten@deubert.net>
 */
class My_Controller_Plugin_Annotation_Layout extends My_Controller_Plugin_Annotation_Abstract
{
    /**
     * @param Zend_Controller_Request_Abstract $request
     * @return void
     */
    public function preDispatch(Zend_Controller_Request_Abstract $request)
    {
        /** @var $request Zend_Controller_Request_Http */
        if (!$request instanceof Zend_Controller_Request_Http)
        {
            return;
        }

        $docBlock = $this->_getDocblockForControllerAction();

        $pattern = '#@layout (\S+)#iu';

        $matches = array();
        $matchesCount = preg_match($pattern, $docBlock, $matches);

        if (!$matchesCount)
        {
            return;
        }

        $layout = Zend_Layout::getMvcInstance();

        if ($matches[1] === 'disabled')
        {
            $layout->disableLayout();
        }
        else
        {
            $layout->setLayout($matches[1]);
        }
    }
}

I have several Annotation Controller Plugins in place for very different purposes and… love them!

Bookmark and Share

PHP 5.3.1 and SoapHeader on windows getting ignored

Today I spent a decent amount of time to realise that PHP 5.3.1 and its SoapServer doesn’t handle provided SoapHeaders as it should. If you provide a SoapHeader named ‘authenticate’ the corresponding method ‘authenticate’ should get executed before any other method – but seems to be ignored with the specified version (on windows using XAMPP).

After upgrading to PHP 5.3.5 (as in the latest XAMPP package) everything works as expected.

Bookmark and Share

You are what you read

279038_1921708527789_1394340376_31755266_5688486_o

Who are you?

Bookmark and Share

New SSD: OCZ Vertex 2 SSD 120GB

My first SSD, the OCZ Apex 120GB, lasted for about 18 months. As replacement I bought another OCZ drive, this time from the Vertex series: OCZ Vertex 2 120GB. I read about the new SandForce 1200 Controller in most modern SSDs and that the Vertex drive uses firmware which is equivalent to the SandForce 1500 controller. The Agility series has the “normal” SandForce 1200 controller and lacks some writing speed in some areas.

Detailed review and comparison to other drives, in german: hardwareluxx.de

The bad minimum read spead (~1mb/s) my old ssd had is gone, as you can see in this HDTach screenshot:

read

Bookmark and Share

Thank you Windows Home Server

About a year ago I bought a 3TB “NAS” running Windows Home Server: Acer Aspire EasyStore H341.

After a 15 minute setup per machine in my household everything gets automated daily, full backups.

Two days ago my ssd crashed with “SMART status BAD – backup and replace drive”, unable to ignore/boot, windows 7 repair tools didn’t even recognize the drive…

Then:

- Drive to the next store with the ssd replacement i wanted: 15 minutes

- New SSD: 200€

- Booting from the Home Server recovery CD, selecting which partitions to recover: 10 minutes

- All ssd partitions restored: 30 minutes (Gbit lan, yay)

- Reboot, working system (only 6h changes lost, luckily I was mostly sorting my mails)

Windows Home Server makes even complete drive failures a non-event, I really love this fine piece of working, reliable soft- and hardware!

Bookmark and Share

How to install cruisecontrol/phpUnderControl in Debian Lenny

Some time ago, I posted a small tutorial how to install phpundercontrol in a fresh Debian Etch machine. As Debian Lenny is now out for some time I’d like to update this small tutorial for its changes.

The good thing: You don’t need to use backports anymore – the sun-java6-bin/sun-java6-jre packages are available in the lenny non-free tree.

Therefore, the first thing to do is check your /etc/apt/sources.list if it checks for non-free packages (does not per default).

deb http://ftp.debian.org/debian/ lenny main non-free
deb-src http://ftp.debian.org/debian/ lenny main non-free

The rest stays mostly the same:

# update apt to include non-free packages
apt-get update

# install packets (java and subversion)
apt-get install sun-java6-bin sun-java6-jre
apt-get install subversion

# add symlink from /usr/bin/java to /bin/java for the cruisecontrol start script
ln -s /usr/bin/java /bin/java

# install apache (mainly for phpmyadmin) and php5 + cli
apt-get install apache2-mpm-prefork
apt-get install php5 php5-cli php5-dev
apt-get install php-pear make

# install xdebug (needed for phpunit)
pecl install xdebug
echo "zend_extension=/usr/lib/php5/20060613+lfs/xdebug.so" >> /etc/php5/cli/php.ini

# install phpunit and phpundercontrol via pear
pear upgrade --force pear
pear channel-discover pear.phpunit.de
pear channel-discover components.ez.no
pear install phpunit/phpunit
pear install --alldeps channel://components.ez.no/Graph
pear install --alldeps channel://pear.phpunit.de/phpundercontrol-0.5.0

# get and extract cruisecontrol
apt-get install unzip wget
cd ~
wget http://freefr.dl.sourceforge.net/sourceforge/cruisecontrol/cruisecontrol-bin-2.8.3.zip
unzip cruisecontrol-bin-2.8.3.zip -d /opt
cd /opt
ln -s cruisecontrol-bin-2.8.3 cruisecontrol

# run phpundercontrol modifications against cruisecontrol
phpuc install /opt/cruisecontrol

# first testrun
cd /opt/cruisecontrol
./cruisecontrol.sh
Bookmark and Share

Reading browser history with css and javascript

This small script demonstrates how to check if a user has visited a particular url with his browser using css and javascript. Simple trick, small script, but could be dangerous – and you don’t see a damn thing.

The only protection against this of which i know is NoScript or just turning off JavaScript in your browser…

Bookmark and Share

Rotating dom elements with css3 and jscript/jquery animation

If you want to rotate dom elements, webkit offers you

-webkit-transform: rotate(0deg);

and firefox offers you

-moz-transform: rotate(0deg);

I hacked together a small demo rotating dom elements with css including some animation with jscript/jquery.

Bookmark and Share

Next,