Search code examples
phpenvironmentsimplesamlphpdev-to-production

Instantiate which PHP class based on dev or production environment


I've integrated SimpleSAMLphp with my application, however it only works on a production environment as there is no connection to the IdP servers elsewhere. How can I continue working on the development environment on things that require authentication?

I've written a wrapper class that exposes the necessary methods to the SimpleSAML_Auth_Simple class. The relevant code is as follows:

Pages requiring authentication

<?php

// (assume autoloading)
$saml = new SAMLWrapper('name-of-sp');
$saml->requireAuthentication('https://[::1]/app/saml-controller.php?callback=1');
$userAttributes = $saml->getAttributes();

// rest of application code below...

Wrapper class

class SAMLWrapper extends IAuthentication
{
    private $as;

    public function __construct($sp) {
        require_once('/var/simplesamlphp/lib/_autoload.php');
        // THIS PATH DOES NOT EXIST ON DEV

        $this->as = new \SimpleSAML_Auth_Simple($sp);
    }

    public function requireAuthentication($callback) {
        $this->as->requireAuth(array('ReturnTo' => $callback));
    }

    public function getAttributes() {
        return $this->as->getAttributes();
    }
}

Dummy wrapper class

I've considered writing a dummy wrapper like this:

class DummySAML extends IAuthentication
{
    private $attrs;

    public function __construct(array $attrs) {
        $this->attrs = $attrs;
    }

    public function requireAuthentication() {
        return;
    }

    public function getAttributes() {
        return $this->attrs;
    }
}

However this means I have to switch between the SAMLWrapper and DummySAML class on all the pages requiring authentication:

if (getenv('SLIM_MODE') === 'DEV') {
    // instantiate DummySAML with test attributes
} else {
    // instantiate SAMLWrapper with service provider name
}

Is there an easier and better way of doing this?


Solution

  • One option would be to move the env-based switching inside a single wrapper class. One immediately obvious downside is that your test attributes would need to either be hard-coded inside the class, or always passed to the constructor even in production. Otherwise you wouldn't be able to support both scenarios with a single constructor.

    In my own apps, I would probably obtain the authentication wrapper from a dependency injection container, registering a factory that checks the environment and returns an instance of the appropriate class (real or dummy). If you're not already using DI, migrating could be a real pain, but you could always create a one-off static factory that handles the instantiation of the appropriate wrapper to reduce the amount of boilerplate at the top of each file.