Denying Access in a Controller

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

I do use access controls to lock down big sections, but, mostly, I handle authorization inside my controllers.

Deny Access (the long way)!

Let's play around: comment out the access_control:

... lines 1 - 2
security:
... lines 4 - 33
access_control:
# - { path: ^/admin, roles: ROLE_ADMIN }

And open up GenusAdminController. To check if the current user has a role, you'll always use one service: the authorization checker. It looks like this: if (!$this->get('security.authorization_checker')->isGranted('ROLE_ADMIN'). So, if we do not have ROLE_ADMIN, then throw $this->createAccessDeniedException():

... lines 1 - 13
class GenusAdminController extends Controller
{
... lines 16 - 18
public function indexAction()
{
if (!$this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')) {
throw $this->createAccessDeniedException('GET OUT!');
}
... lines 24 - 31
}
... lines 33 - 84
}

That message is just for us developers.

Head back and refresh. Access denied!

So what's the magic behind that createAccessDeniedException() method? Find out: hold Command and click to open it. Ah, it literally just throws a special exception called AccessDeniedException:

... lines 1 - 21
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
... lines 23 - 38
abstract class Controller implements ContainerAwareInterface
{
... lines 41 - 257
/**
* Returns an AccessDeniedException.
*
* This will result in a 403 response code. Usage example:
*
* throw $this->createAccessDeniedException('Unable to access this page!');
*
* @param string $message A message
* @param \Exception|null $previous The previous exception
*
* @return AccessDeniedException
*/
protected function createAccessDeniedException($message = 'Access Denied.', \Exception $previous = null)
{
return new AccessDeniedException($message, $previous);
}
... lines 274 - 396
}

It turns out - no matter where you are - if you need to deny access for any reason, just throw this exception. Symfony handles everything else.

Deny Access (the short way)!

Simple, but that was too much work. So, you'll probably just do this instead: $this->denyAccessUnlessGranted('ROLE_ADMIN'):

... lines 1 - 13
class GenusAdminController extends Controller
{
... lines 16 - 18
public function indexAction()
{
$this->denyAccessUnlessGranted('ROLE_ADMIN');
... lines 22 - 29
}
... lines 31 - 82
}

Much better: that does the same thing as before.

Denying Access with Annotations

And, I have another idea! If you love annotations, you can use those to deny access. Above the controller, add @Security() then type a little expression: is_granted('ROLE_ADMIN'):

... lines 1 - 7
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
... lines 9 - 14
class GenusAdminController extends Controller
{
/**
* @Route("/genus", name="admin_genus_list")
* @Security("is_granted('ROLE_ADMIN')")
*/
public function indexAction()
{
... lines 23 - 29
}
... lines 31 - 82
}

This has the exact same effect - it just shows us a different message.

Locking down an Entire Controller

But no matter how easy we make it, what we really want to do is lock down this entire controller. Right now, we could still go to /admin/genus/new and have access. We could repeat the security check in every controller... or we could do something cooler.

Add the annotation above the class itself:

... lines 1 - 7
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Security;
... lines 9 - 11
/**
* @Security("is_granted('ROLE_ADMIN')")
* @Route("/admin")
*/
class GenusAdminController extends Controller
{
... lines 18 - 82
}

As soon as you do that, all of these endpoints are locked down.

Sweet!

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}