Aller au contenu principal

Contrôle d'accès personnalisé

Le contrôle d'accès dans le fichier de configuration security.yaml peut nous permettre de restreindre des sections entières à un rôle particulier, une méthode HTTP, ou tout un tas d'autres options.

Mais si on veut un contrôle d'accès plus fin, au niveau d'un contrôleur par exemple, la configuration ne suffira plus.

Dans un contrôleur​

Dans un contrôleur, on dispose de méthodes fournies par l'AbstractController.

denyAccessUnlessGranted​

denyAccessUnlessGranted($attribute, $subject = null, string $message = 'Access Denied.') permet de générer automatiquement une erreur 403 Forbidden si l'utilisateur connecté n'a pas le droit d'accéder à la ressource (le subject) pour effectuer une action (l'attribute).

Rôle

On peut aussi passer un rôle dans $attribute :

$this->denyAccessUnlessGranted('ROLE_ADMIN');

isGranted​

isGranted($attribute, $subject = null), quant à elle, renvoie un booléen pour indiquer si l'utilisateur a le droit d'accéder à la ressource pour effectuer une action. On n'aura pas une 403 générée automatiquement, seulement un booléen en retour.

On peut également l'utiliser avec un rôle, pour vérifier si l'utilisateur a bien le rôle indiqué.

getRoles()

Il est recommandé de ne pas utiliser directement la méthode getRoles() sur un utilisateur pour vérifier ses rôles.

À la place, il faut utiliser denyAccessUnlessGranted ou isGranted

Récupérer l'utilisateur​

Depuis un contrôleur​

Si on veut récupérer l'utilisateur depuis un contrôleur, on pourra utiliser la méthode utilitaire $this->getUser(). Le type de retour indiqué est bien sûr UserInterface|null.

Depuis un service​

Si on ne se trouve pas dans un contrôleur, on pourra injecter directement le type Security :

class MyService
{
public function __construct(
private Security $security
) {
}

public function myMethod()
{
$user = $this->security->getUser();
}
}
Security

Le service Security présente la méthode isGranted pour vérifier si un utilisateur a un rôle ou bien peut effectuer une action. Encore une fois, il n'est pas nécessaire de récupérer l'utilisateur et ensuite vérifier ses rôles avec getRoles() : on utilisera directement isGranted sur le service Security

Utiliser un Voter​

Pour implémenter un contrôle d'accès plus fin, on peut également écrire un Voter personnalisé.

Par exemple, mettons que des articles peuvent être édités ou supprimés seulement si l'utilisateur connecté en est l'auteur, ou bien qu'il est administrateur.

Depuis un contrôleur, on peut vérifier si l'utilisateur a le droit de modifier l'article :

$this->denyAccessUnlessGranted('ARTICLE_EDIT', $article);

La logique de vérification peut quant à elle être écrite dans une classe héritant de la classe abstraite Voter :

class ArticleVoter extends Voter
{
public const EDIT = 'ARTICLE_EDIT';
public const DELETE = 'ARTICLE_DELETE';

public function __construct(
private AccessDecisionManager $accessDecisionManager
) {
}

protected function supports(string $attribute, mixed $subject): bool
{
return in_array($attribute, [self::EDIT, self::DELETE])
&& $subject instanceof Article;
}

protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
// if the user is anonymous, do not grant access
if (!$user instanceof UserInterface || !$subject instanceof Article) {
return false;
}

if ($this->accessDecisionManager->decide($token, ['ROLE_ADMIN']) || $subject->getAuthor() === $user) {
return true;
}

return false;
}
}
astuce

Utilisez l'AccessDecisionManager pour vérifier les rôles au sein d'un Voter (documentation)

Maker

On peut créer un voter à l'aide du maker : php bin/console make:voter

À partir du moment ou notre voter hériter de la classe abstraite Voter, Symfony va détecter automatiquement notre service et l'enregistrer automatiquement pour la vérification d'un contrôle d'accès.

Ce voter sera donc appelé automatiquement lorsqu'on appellera denyAccessUnlessGranted ou isGranted avec un attribut et un sujet.

Dans un premier temps, Symfony interrogera chaque voter enregistré en appelant leur méthode supports. Cette méthode retourne un booléen pour indiquer si elle est capable de fournir une décision pour l'attribut et le sujet.

Si supports renvoie true, alors Symfony appelle automatiquement la méthode voteOnAttribute du voter.