<?php
declare(strict_types=1);
namespace ERP\SecurityBundle\EventListener;
use ApiPlatform\Core\Exception\ResourceClassNotFoundException;
use ApiPlatform\Core\Metadata\Resource\Factory\ResourceMetadataFactoryInterface;
use ApiPlatform\Core\Security\ExpressionLanguage;
use ApiPlatform\Core\Security\ResourceAccessChecker;
use ApiPlatform\Core\Security\ResourceAccessCheckerInterface;
use ApiPlatform\Core\Util\RequestAttributesExtractor;
use ERP\SecurityBundle\Services\RoleHierarchyService;
use Symfony\Component\HttpKernel\Event\RequestEvent;
use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface;
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Role\RoleHierarchyInterface;
/**
* Denies access to the current resource if the logged user doesn't have sufficient permissions.
*/
final class DenyAccessListener
{
private $resourceMetadataFactory;
private $resourceAccessChecker;
/**
* @var RoleHierarchyService
*/
private $roleHierarchyService;
public function __construct(
ResourceMetadataFactoryInterface $resourceMetadataFactory,
RoleHierarchyService $roleHierarchyService,
/* ResourceAccessCheckerInterface */ $resourceAccessCheckerOrExpressionLanguage = null,
AuthenticationTrustResolverInterface $authenticationTrustResolver = null,
RoleHierarchyInterface $roleHierarchy = null,
TokenStorageInterface $tokenStorage = null,
AuthorizationCheckerInterface $authorizationChecker = null)
{
$this->resourceMetadataFactory = $resourceMetadataFactory;
$this->roleHierarchyService = $roleHierarchyService;
if ($resourceAccessCheckerOrExpressionLanguage instanceof ResourceAccessCheckerInterface) {
$this->resourceAccessChecker = $resourceAccessCheckerOrExpressionLanguage;
return;
}
$this->resourceAccessChecker = new ResourceAccessChecker($resourceAccessCheckerOrExpressionLanguage, $authenticationTrustResolver, $roleHierarchy, $tokenStorage, $authorizationChecker);
@trigger_error(sprintf('Passing an instance of "%s" or null as second argument of "%s" is deprecated since API Platform 2.2 and will not be possible anymore in API Platform 3. Pass an instance of "%s" and no extra argument instead.', ExpressionLanguage::class, self::class, ResourceAccessCheckerInterface::class), E_USER_DEPRECATED);
}
/**
* Sets the applicable format to the HttpFoundation Request.
*
* @throws ResourceClassNotFoundException
*/
public function onKernelRequest(RequestEvent $event): void
{
$request = $event->getRequest();
if (!$attributes = RequestAttributesExtractor::extractAttributes($request)) {
return;
}
$resourceMetadata = $this->resourceMetadataFactory->create($attributes['resource_class']);
$metaAccess = $resourceMetadata->getOperationAttribute($attributes, 'access_control', null, true);
if ('/api/' !== substr($request->getPathInfo(), 0, 5)) {
// return;
}
if (null === $metaAccess) {
try {
$attribute = $this->roleHierarchyService->getOperationAccessAttribute($attributes['resource_class']);
$isGranted = "is_granted('{$attribute}')";
} catch (\InvalidArgumentException $exception) {
$isGranted = null;
}
if (null === $isGranted) {
return;
}
$extraVariables = $request->attributes->all();
$extraVariables['object'] = $request->attributes->get('data');
$extraVariables['request'] = $request;
if (isset($extraVariables['_route_params']) && isset($extraVariables['_route_params']['_api_extra_access_control'])) {
switch ($extraVariables['_route_params']['_api_extra_access_control']) {
case 'IS_AUTHENTICATED_ANONYMOUSLY':
return;
}
}
$access = $this->resourceAccessChecker->isGranted($attributes['resource_class'], $isGranted, $extraVariables);
if (!$access) {
throw new AccessDeniedException((string) $resourceMetadata->getOperationAttribute($attributes, 'access_control_message', 'Access Denied.', true));
}
}
}
}