sajad torkamani

In a nutshell

You can use Symfony Voter classes to implement access control checks for different operations on your entities.

1. Create Voter class for entity

You’ll want to create a dedicated Voter class for each entity that you want to implement access control checks for. Here’s an example for a Book entity:

<?php
// api/src/Security/Voter/BookVoter.php

namespace App\Security\Voter;

use App\Entity\Book;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;

class BookVoter extends Voter
{
    private $security = null;

    public function __construct(Security $security)
    {
        $this->security = $security;
    }

    protected function supports($attribute, $subject): bool
    {
        $supportsAttribute = in_array($attribute, ['BOOK_CREATE', 'BOOK_READ', 'BOOK_EDIT', 'BOOK_DELETE']);
        $supportsSubject = $subject instanceof Book;

        return $supportsAttribute && $supportsSubject;
    }

    /**
     * @param string $attribute
     * @param Book $subject
     * @param TokenInterface $token
     * @return bool
     */
    protected function voteOnAttribute($attribute, $subject, TokenInterface $token): bool
    {
        /** ... check if the user is anonymous ... **/

        switch ($attribute) {
            case 'BOOK_CREATE':
                if ( $this->security->isGranted(Role::ADMIN) ) { return true; }  // only admins can create books
                break;
            case 'BOOK_READ':
                /** ... other autorization rules ... **/
        }

        return false;
    }
}

1. Specify supports condition

Use supports method is to determine whether the current voter class should apply to a given operation.

In the above example, we want BookVoter to only apply if the operation is one of BOOK_CREATE, BOOK_READ, BOOK_EDIT, or BOOK_DELETE and the subject is an instance of Book.

2. Specify permissions checks

The voteOnAttribute method specifies the actual permission checks. You can use a switch statement to handle the different operations (e.g., BOOK_READ, BOOK_CREATE, etc).

2. Apply voter to API resources / operations

Once you’ve created your Voter class, you can apply it at the operation level:

<?php
// api/src/Entity/Book.php

namespace App\Entity;

use ApiPlatform\Core\Annotation\ApiResource;

#[ApiResource(
    attributes: ["security" => "is_granted('ROLE_USER')"],
    collectionOperations: [
        "get",
        "post" => [ "security_post_denormalize" => "is_granted('BOOK_CREATE', object)" ],
    ],
    itemOperations: [
        "get" => [ "security" => "is_granted('BOOK_READ', object)" ],
        "put" => [ "security" => "is_granted('BOOK_EDIT', object)" ],
        "delete" => [ "security" => "is_granted('BOOK_DELETE', object)" ],
    ],
)]
class Book
{
    // ...

Other notes

  • You cannot use Voters on collection GET operations. You must use extensions instead (also see this note).
  • Avoid using both the attributes: ["security" => ".."] and security_post_denormalize together. If you need to use security_post_denormalize, specify security only at the operation level.
  • If you use Voters on POST methods, you must use something like "post" = { "security_post_denormalize" = "is_granted('BOOK_CREATE', object)" } so that the voter receives both the $attribute and $subject.

Sources

Tagged: