Course: Symfony 3 Security: Beautiful Authentication, Powerful Authorization Tutorial
I do use access controls to lock down big sections, but, mostly, I handle authorization inside my controllers.
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.
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.
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.
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!