diff --git a/best_practices/security.rst b/best_practices/security.rst index 207499aea07..6b25a7f54b5 100644 --- a/best_practices/security.rst +++ b/best_practices/security.rst @@ -264,37 +264,66 @@ the same ``getAuthorEmail`` logic you used above: namespace AppBundle\Security; - use Symfony\Component\Security\Core\Authorization\Voter\AbstractVoter; + use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; + use Symfony\Component\Security\Core\Authorization\AccessDecisionManagerInterface; + use Symfony\Component\Security\Core\Authorization\Voter\Voter; use Symfony\Component\Security\Core\User\UserInterface; + use AppBundle\Entity\Post; - // AbstractVoter class requires Symfony 2.6 or higher version - class PostVoter extends AbstractVoter + // Voter class requires Symfony 2.8 or higher version + class PostVoter extends Voter { const CREATE = 'create'; const EDIT = 'edit'; - protected function getSupportedAttributes() + /** + * @var AccessDecisionManagerInterface + */ + private $decisionManager; + + public function __construct(AccessDecisionManagerInterface $decisionManager) { - return array(self::CREATE, self::EDIT); + $this->decisionManager = $decisionManager; } - protected function getSupportedClasses() + protected function supports($attribute, $subject) { - return array('AppBundle\Entity\Post'); + if (!in_array($attribute, array(self::CREATE, self::EDIT))) { + return false; + } + + if (!$subject instanceof Post) { + return false; + } + + return true; } - protected function isGranted($attribute, $post, $user = null) + protected function voteOnAttribute($attribute, $subject, TokenInterface $token) { + $user = $token->getUser(); + /** @var Post */ + $post = $subject; // $subject must be a Post instance, thanks to the supports method + if (!$user instanceof UserInterface) { return false; } - if ($attribute === self::CREATE && in_array('ROLE_ADMIN', $user->getRoles(), true)) { - return true; - } - - if ($attribute === self::EDIT && $user->getEmail() === $post->getAuthorEmail()) { - return true; + switch ($attribute) { + case self::CREATE: + // if the user is an admin, allow them to create new posts + if ($this->decisionManager->decide($token, array('ROLE_ADMIN'))) { + return true; + } + + break; + case self::EDIT: + // if the user is the author of the post, allow them to edit the posts + if ($user->getEmail() === $post->getAuthorEmail()) { + return true; + } + + break; } return false; @@ -310,6 +339,7 @@ To enable the security voter in the application, define a new service: # ... post_voter: class: AppBundle\Security\PostVoter + arguments: ['@security.access.decision_manager'] public: false tags: - { name: security.voter } @@ -337,7 +367,7 @@ via the even easier shortcut in a controller: */ public function editAction($id) { - $post = // query for the post ... + $post = ...; // query for the post $this->denyAccessUnlessGranted('edit', $post);