<?php
/**
* This file is part of Synapse.
*
* Copyright (C) 2020-2021 Daniel Ménard
*
* For copyright and license information, please view the
* LICENSE file that was distributed with this source code.
*/
declare(strict_types=1);
namespace App\Security;
use App\Entity\Company;
use App\Entity\Tender;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Authorization\Voter\Voter;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* TenderVoter.
*
* @author Daniel Ménard <daniel.menard.35@gmail.com>
*/
final class TenderVoter extends Voter
{
/**
* L'attribut à utiliser pour déterminer si l'utilisateur peut consulter un appel d'offres.
*/
public const VIEW = 'view';
/**
* L'attribut à utiliser pour déterminer si l'utilisateur peut modifier un appel d'offres.
*/
public const EDIT = 'edit';
/**
* L'attribut à utiliser pour déterminer si l'utilisateur peut voir le budget d'un appel d'offres.
*/
public const VIEW_BUDGET = 'view_budget';
/**
* Liste des attributs que l'on gère.
*/
private const ATTRIBUTES = [
self::VIEW => 'canView',
self::EDIT => 'canEdit',
self::VIEW_BUDGET => 'canViewBudget',
];
/**
* {@inheritdoc}
*/
final protected function supports(string $attribute, $subject): bool
{
// On ne s'occupe que des objets de type Tender et des attributs qu'on connaît
return ($subject instanceof Tender) && isset(self::ATTRIBUTES[$attribute]);
}
/**
* {@inheritdoc}
*/
final protected function voteOnAttribute(string $attribute, $tender, TokenInterface $token): bool
{
// L'utilisateur doit être connecté
$user = $token->getUser();
if (!$user instanceof UserInterface) {
return false;
}
// Un administrateur a tous les droits, quelle que soit la permission demandée
if (in_array('ROLE_ADMIN', (array) $user->getRoles())) {
return true; // mauvaise pratique mais isGranted ne fonctionne pas dans les tests
}
// Teste la permission demandée
$method = [$this, self::ATTRIBUTES[$attribute]];
return $method($tender, $user);
}
/**
* Teste si un utilisateur peut voir un appel d'offres.
*
* Retourne true si l'une des conditions suivantes est vraie :
* - l'utilisateur est le DDO ou le GROUPE du projet
* - l'utilisateur est le projet (accès de la forme AOxxx)
*/
final protected function canView(Tender $tender, UserInterface $user): bool
{
// L'utilisateur est le DDO ou le GROUPE du projet dont fait partie le lot
if ($user instanceof Company) {
return $user === $tender->getDdo() || $user->getId() === $tender->getGroupId();
}
// L'utilisateur est le projet dont fait partie le lot (accès de la forme AOxxx)
if ($user instanceof Tender) {
return $user === $tender;
}
// Type d'utilisateur inconnu
return false;
}
/**
* Teste si un utilisateur peut modifier un appel d'offres.
*
* Retourne true si l'une des conditions suivantes est vraie :
* - l'utilisateur est le DDO ou le GROUPE du projet
* - l'utilisateur est le projet (accès de la forme AOxxx)
*/
final protected function canEdit(Tender $tender, UserInterface $user): bool
{
return $this->canView($tender, $user);
}
/**
* Teste si un utilisateur peut voir le budget d'un appel d'offres.
*
* Retourne true si l'une des conditions suivantes est vraie :
* - l'utilisateur est le DDO ou le GROUPE du projet
* - l'utilisateur est le projet (accès de la forme AOxxx) ET la propriété budgetIsHidden
* du projet est à false.
*/
final protected function canViewBudget(Tender $tender, UserInterface $user): bool
{
if ($user instanceof Tender && $tender->getBudgetIsHidden()) {
return false;
}
return $this->canView($tender, $user);
}
}