Policy-based Protection with Twig

When you start thinking about the different roles of authorization in your application, you'll probably find that they break down into the two main parts of your application: the backend and the frontend. Disregarding single-page applications, this "frontend" usually consists of the "view" layer in your MVC application or wherever you're echoing out your content. For many applications, this also means using some kind of templating framework, framework based application or not.

There are plenty of options when it comes to templating frameworks for the PHP ecosystem too. Some are built in to frameworks, like Blade or are more component-driven and framework independent like Plates. Also included in this last category is one of the most widely used templating packages available for PHP: Twig.

First, an Example

So you'll have an idea what the end result will be once you've finished this tutorial, I wanted to show what a Twig template with an authorization check might look like:

This is my content
and this

The Basics of Twig

For those of you readers not already familiar with Twig, it's a templating package from the creators of the Symfomy framework (and is included as the default templating system there). Twig takes the approach of using entire templates instead of one-off escaping only where it might be needed. So for example:

<?php
// In a PHP only template you might escape a value for the HTML context like
echo 'Value: '.htmlspecialchars($foo, ENT_QUOTES, 'UTF-8', true);

// With a "one-off" escaping library that could be hidden behind a function
echo 'Value: '.__escape($foo);

// But with Twig, you pass in an entire template and the data
$template = $twig->createTemplate('Value: {{ foo }}!');
echo $template->render(['foo' => $foo]);

?>

As you can see in the example above, it takes a little extra work to use Twig, but along with it comes plenty of other useful and powerful features. This is just one example using a string template. It can also automatically pull templates from a directory of your choosing making it even simpler to use and split views out from your core logic.

While the Twig templating language comes with lots of interesting and handy features, it doesn't internally support the concept of permissioning or authorization. Basically, it has no internal mechanism for judging if you should see a certain part of a template or not. Some developers say that this kind of evaluation should be handled by the controller to judge the show/hide of content to keep the logic versus output separation of concerns intact. However I have found times where adding this logic for several different show/hide sections in a template to the controller weighed it down and felt sort of "wrong" overall.

I knew there had to be a better way than just passing in a lot of boolean checks to the view. I needed something in my Twig templates to evaluate these checks for me in a more automatic, reusable way. Sounds like an authorization policy, right? I realized that my PropAuth library could be a good fit for the need and, fortunately, it worked out quite well.

What is PropAuth?

If you haven't already read the previous article about PropAuth, I'd suggest giving it a look. Here's the short version of it though: PropAuth is a library that checks properties on objects according to policies you've previously defined. This helps to prevent some of the issues that can happen with other ACL checks (like role-based access control) and makes the checks more reusable. It also allows for a more centralized place to define and look for these policies rather than scattering them through out the application.

Here's an example of a simple policy and object evaluation:

<?php
$user = new \stdClass();
$user->username = 'ccornutt';

$policy = Policy::instance()->hasUsername('ccornutt');
$result = Enforcer::instance()->evaluate($user, $policy);

echo 'Result: '.var_export($result, true);

?>

In this example we're doing a simple single policy evaluation, checking to see if the user's username value matches the expected ccornutt value. The Enforcer takes in these two pieces of information and performs the property checking for you, spitting out a boolean pass/fail result (in $result). There's a lot more complex things you can do with grouping policies into PolicySet collections and custom callback handling, but you can find more information on that in the other article.

Combining the Two

I've introduced two pieces of the puzzle that we'll need to get the authorization checks to work in our templates. Let's look at the third piece: the helper that pulls them together an lets you use the checks in your Twig templates.

<?php
$en = $this->container->get('enforcer');

$policySet = PolicySet::instance()
    ->add('is-admin', Policy::instance()->can(function($subject) {
        foreach ($subject->roles as $role) {
            if (strtolower($role->key) == 'admin') {
                return true;
            }
        }
        return false;
    }));

$en->setPolicySet($policySet);
//---------------------

$function = new Twig_SimpleFunction('allow', function($policyName) use ($container) {
    $user = $container->get('user');
    if ($user == null) {
        return false;
    }
    $en = $container->get('enforcer');
    return $en->allows($policyName, $user);
});
$view->getEnvironment()->addFunction($function);

?>

by Chris Cornutt

With over 12 years of PHP experiece and a focus on application security Chris is on a quest to bring his knowledge to the masses, making application security accessible to everyone. He also provides application security training and consulting services.