vendor/shopware/core/Framework/Api/Acl/Resource/AclPermissionValidator.php line 27

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\Api\Acl\Resource;
  3. use Shopware\Core\Framework\Api\Context\AdminApiSource;
  4. use Shopware\Core\Framework\Context;
  5. use Shopware\Core\Framework\DataAbstractionLayer\EntityTranslationDefinition;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\WriteCommand;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PreWriteValidationEvent;
  8. use Shopware\Core\Framework\Uuid\Uuid;
  9. use Shopware\Core\Framework\Validation\WriteConstraintViolationException;
  10. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  11. use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  12. use Symfony\Component\Validator\ConstraintViolation;
  13. use Symfony\Component\Validator\ConstraintViolationInterface;
  14. use Symfony\Component\Validator\ConstraintViolationList;
  15. class AclPermissionValidator implements EventSubscriberInterface
  16. {
  17.     public const VIOLATION_NO_PERMISSION 'no_permission_violation';
  18.     public static function getSubscribedEvents()
  19.     {
  20.         return [PreWriteValidationEvent::class => 'preValidate'];
  21.     }
  22.     public function preValidate(PreWriteValidationEvent $event): void
  23.     {
  24.         if ($event->getContext()->getScope() === Context::SYSTEM_SCOPE) {
  25.             return;
  26.         }
  27.         $commands $event->getCommands();
  28.         $source $event->getContext()->getSource();
  29.         if (!$source instanceof AdminApiSource || $source->isAdmin()) {
  30.             return;
  31.         }
  32.         $violationList = new ConstraintViolationList();
  33.         foreach ($commands as $command) {
  34.             $resource $command->getDefinition()->getEntityName();
  35.             $privilege $command->getPrivilege();
  36.             if (is_subclass_of($command->getDefinition(), EntityTranslationDefinition::class)) {
  37.                 $resource $command->getDefinition()->getParentDefinition()->getEntityName();
  38.                 if ($privilege !== AclResourceDefinition::PRIVILEGE_DELETE) {
  39.                     $privilege $this->getPrivilegeForParentWriteOperation($command$commands);
  40.                 }
  41.             }
  42.             if (!$source->isAllowed($resource$privilege)) {
  43.                 $this->violates($privilege$resource$command$violationList);
  44.             }
  45.         }
  46.         $this->tryToThrow($violationList);
  47.     }
  48.     private function tryToThrow(ConstraintViolationList $violations): void
  49.     {
  50.         if ($violations->count() > 0) {
  51.             $violationException = new WriteConstraintViolationException($violations);
  52.             throw new AccessDeniedHttpException('You don\'t have all necessary permissions.'$violationException);
  53.         }
  54.     }
  55.     private function buildViolation(
  56.         string $messageTemplate,
  57.         array $parameters,
  58.         $root null,
  59.         ?string $propertyPath null,
  60.         $invalidValue null,
  61.         ?string $code null
  62.     ): ConstraintViolationInterface {
  63.         return new ConstraintViolation(
  64.             str_replace(array_keys($parameters), array_values($parameters), $messageTemplate),
  65.             $messageTemplate,
  66.             $parameters,
  67.             $root,
  68.             $propertyPath,
  69.             $invalidValue,
  70.             null,
  71.             $code
  72.         );
  73.     }
  74.     private function violates(
  75.         string $privilege,
  76.         string $resource,
  77.         WriteCommand $command,
  78.         ConstraintViolationList $violationList
  79.     ): void {
  80.         $violationList->add(
  81.             $this->buildViolation(
  82.                 'No permissions to %privilege% "%resource%".',
  83.                 ['%privilege%' => $privilege'%resource%' => $resource],
  84.                 null,
  85.                 '/' $command->getDefinition()->getEntityName(),
  86.                 null,
  87.                 self::VIOLATION_NO_PERMISSION
  88.             )
  89.         );
  90.     }
  91.     /**
  92.      * @param WriteCommand[] $commands
  93.      */
  94.     private function getPrivilegeForParentWriteOperation(WriteCommand $command, array $commands): string
  95.     {
  96.         $pathSuffix '/translations/' Uuid::fromBytesToHex($command->getPrimaryKey()['language_id']);
  97.         $parentCommandPath str_replace($pathSuffix''$command->getPath());
  98.         $parentCommand $this->findCommandByPath($parentCommandPath$commands);
  99.         // writes to translation need privilege from parent command
  100.         // if we update e.g. a product and add translations for a new language
  101.         // the writeCommand on the translation would be an insert
  102.         if ($parentCommand) {
  103.             return $parentCommand->getPrivilege();
  104.         }
  105.         // if we don't have a parentCommand it must be a update,
  106.         // because the parentEntity must already exist
  107.         return AclResourceDefinition::PRIVILEGE_UPDATE;
  108.     }
  109.     /**
  110.      * @param WriteCommand[] $commands
  111.      */
  112.     private function findCommandByPath(string $commandPath, array $commands): ?WriteCommand
  113.     {
  114.         foreach ($commands as $command) {
  115.             if ($command->getPath() === $commandPath) {
  116.                 return $command;
  117.             }
  118.         }
  119.         return null;
  120.     }
  121. }