Как задавать права доступа в Symfony используя базу данных (Voters used database)

С тех пор, как вся конфигурация Symfony кешируется с контейнером для лучшей производительности , мы по очевидным причинам не должны использовать базу данных что бы где-то «напечатать» новую конфигурацию, нам нужно что-то умнее.

Использование Symfony Voters

Symfony использует Voters что бы определить доступ к URL и другим ресурсам. Множество voters которые входят в комплект поставки вместе с Symfony security  которые используют конфигурацию в security.access_control что бы определить нужно ли давать права на запрос или нет.

То, что мы хотим сделать — это voter, который имеет доступ к базе данных. То, как создавать Voter описано в документации к Symfony

use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\VoterInterface;

class MyDynamicAccessVoter implements VoterInterface
{
    private $em;
    
    public function __construct(EntityMangerInterface $em)
    {
        $this->em = $em;
    }
    
    public function vote(TokenInterface $token, $subject, array $attributes)
    {
        if (!$subject instanceof Request) {
            return self::ACCESS_ABSTAIN;
        }

        $uri = $subject->getUri();
        $roles = $token->getRoles();
        
        // TODO реализуйте логику тут.
        //$this->em->getRepository(...)
        
        if (/* Доступ есть */) {
            return self::ACCESS_GRANTED;
        }
        
        if (/* Нет доступа */) {
            return self::ACCESS_DENIED;
        }
        
        // Vote "abstain" если мы не смогли определить
        return self::ACCESS_ABSTAIN;
    }
}

Этот класс должен быть зарегистрирован в контейнере сервисов. Если вы используете Symfony 4 со стандартным services.yaml он будет зарегистрирован автоматически. Если вам нужно зарегистрировать его вручную, не забудьте добавить тег security.voter

services:
    App\Voter\MyDynamicAccessVoter:
        arguments: ['@doctrine.orm.entity_manager']
        tags:
          - 'security.voter'

Также нужно сказать Symfony что вы используете access_control:

security:
    access_control:
         - { path: ^/admin }

MyDynamicAccessVoter будет исполняться для каждого URL который начинается с “/admin”. Вы конечно можете изменить путь на  “/” что бы он исполнялся для всех запросов:

security:
    access_control:
         - { path: ^/ }
Хотя это решение не очевидно, оно показывает насколько гибок и мощен компонент Symfony Security.
Эта статья является переводом

Добавить комментарий