<?php
namespace App\Security\Api\Authorization\Voter;
use App\Entity\Api\CoreModule\User;
use App\Helper\Api\Constants\Entity\Properties\Entity as PropertyNamesEntity;
use App\Helper\Api\Entity\RequestDataContainer;
use App\Security\Api\Authorization\VoterAttribute\AbstractVoterAttribute;
use App\Validator\Api\Data\CoreModule\ModuleDataValidator;
use LogicException;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Validator\Exception\ValidationFailedException;
/**
* API active module voter.
*
* @package API
* @internal
*/
class ActiveModuleVoter extends AbstractApiBaseVoter
{
/**
* Validator interface.
*
* @var ModuleDataValidator
*/
protected ModuleDataValidator $validator;
/**
* Constructor.
*
* @param ModuleDataValidator $validator
*/
public function __construct(ModuleDataValidator $validator)
{
$this->validator = $validator;
}
/**
* Determines if the attribute and subject are supported by this voter.
*
* @param string $attribute An attribute.
* @param mixed $subject The subject to secure, e.g. an object the user wants to access or any other PHP type.
*
* @return bool
*/
protected function supports(string $attribute, $subject): bool
{
// requires data container
if (!$subject instanceof RequestDataContainer) {
return false;
}
// votes then on every API call when attribute format is valid
return AbstractVoterAttribute::isValidAttribute($attribute);
}
/**
* Perform a single access check operation on a given attribute, subject and token.
* It is safe to assume that $attribute and $subject already passed the "supports()" method check.
*
* @param string $attribute An attribute.
* @param mixed $subject The subject to secure, e.g. an object the user wants to access or any other PHP type.
* @param TokenInterface $token The token interface.
* @return bool
*/
protected function voteOnAttribute(string $attribute, mixed $subject, TokenInterface $token): bool
{
$user = $token->getUser();
if (!$user instanceof User) {
// the user must be logged in, otherwise deny access
return false;
}
if (!$this->supports($attribute, $subject)) {
throw new LogicException('This code should not be reached!');
}
if (!$subject instanceof RequestDataContainer) {
throw new LogicException('This voter requires a RequestDataContainer object passed as subject!');
}
try {
$this->validator->validateIsModuleActive([
PropertyNamesEntity::ID => AbstractVoterAttribute::getModuleIdFromAttribute($attribute),
]);
} catch (ValidationFailedException $exception) {
// get message of first violation and issue an access violation when module is inactive
$violations = $exception->getViolations();
$subject->setException(new AccessDeniedHttpException($violations->get(0)->getMessage(), $exception));
return false;
}
return true;
}
}