vendor/shopware/core/Framework/Api/Acl/AclWriteValidator.php line 43

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\Api\Acl;
  3. use Shopware\Core\Framework\Api\Acl\Event\CommandAclValidationEvent;
  4. use Shopware\Core\Framework\Api\Acl\Role\AclRoleDefinition;
  5. use Shopware\Core\Framework\Api\Context\AdminApiSource;
  6. use Shopware\Core\Framework\Api\Context\AdminSalesChannelApiSource;
  7. use Shopware\Core\Framework\Api\Exception\MissingPrivilegeException;
  8. use Shopware\Core\Framework\Context;
  9. use Shopware\Core\Framework\DataAbstractionLayer\EntityTranslationDefinition;
  10. use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\WriteCommand;
  11. use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PreWriteValidationEvent;
  12. use Shopware\Core\Framework\Log\Package;
  13. use Shopware\Core\Framework\Uuid\Uuid;
  14. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  15. use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
  16. /**
  17.  * @deprecated tag:v6.5.0 - reason:becomes-internal - EventSubscribers will become internal in v6.5.0
  18.  */
  19. #[Package('system-settings')]
  20. class AclWriteValidator implements EventSubscriberInterface
  21. {
  22.     private EventDispatcherInterface $eventDispatcher;
  23.     /**
  24.      * @internal
  25.      */
  26.     public function __construct(EventDispatcherInterface $eventDispatcher)
  27.     {
  28.         $this->eventDispatcher $eventDispatcher;
  29.     }
  30.     /**
  31.      * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
  32.      */
  33.     public static function getSubscribedEvents()
  34.     {
  35.         return [PreWriteValidationEvent::class => 'preValidate'];
  36.     }
  37.     public function preValidate(PreWriteValidationEvent $event): void
  38.     {
  39.         $context $event->getContext();
  40.         $source $event->getContext()->getSource();
  41.         if ($source instanceof AdminSalesChannelApiSource) {
  42.             $context $source->getOriginalContext();
  43.             $source $context->getSource();
  44.         }
  45.         if ($context->getScope() === Context::SYSTEM_SCOPE || !$source instanceof AdminApiSource || $source->isAdmin()) {
  46.             return;
  47.         }
  48.         $commands $event->getCommands();
  49.         $missingPrivileges = [];
  50.         foreach ($commands as $command) {
  51.             $resource $command->getDefinition()->getEntityName();
  52.             $privilege $command->getPrivilege();
  53.             if ($privilege === null) {
  54.                 continue;
  55.             }
  56.             if (is_subclass_of($command->getDefinition(), EntityTranslationDefinition::class)) {
  57.                 $resource $command->getDefinition()->getParentDefinition()->getEntityName();
  58.                 if ($privilege !== AclRoleDefinition::PRIVILEGE_DELETE) {
  59.                     $privilege $this->getPrivilegeForParentWriteOperation($command$commands);
  60.                 }
  61.             }
  62.             if (!$source->isAllowed($resource ':' $privilege)) {
  63.                 $missingPrivileges[] = $resource ':' $privilege;
  64.             }
  65.             $event = new CommandAclValidationEvent($missingPrivileges$source$command);
  66.             $this->eventDispatcher->dispatch($event);
  67.             $missingPrivileges $event->getMissingPrivileges();
  68.         }
  69.         $this->tryToThrow($missingPrivileges);
  70.     }
  71.     /**
  72.      * @param list<string> $missingPrivileges
  73.      */
  74.     private function tryToThrow(array $missingPrivileges): void
  75.     {
  76.         if (!empty($missingPrivileges)) {
  77.             throw new MissingPrivilegeException($missingPrivileges);
  78.         }
  79.     }
  80.     /**
  81.      * @param WriteCommand[] $commands
  82.      */
  83.     private function getPrivilegeForParentWriteOperation(WriteCommand $command, array $commands): string
  84.     {
  85.         $pathSuffix '/translations/' Uuid::fromBytesToHex($command->getPrimaryKey()['language_id']);
  86.         $parentCommandPath str_replace($pathSuffix''$command->getPath());
  87.         $parentCommand $this->findCommandByPath($parentCommandPath$commands);
  88.         // writes to translation need privilege from parent command
  89.         // if we update e.g. a product and add translations for a new language
  90.         // the writeCommand on the translation would be an insert
  91.         if ($parentCommand) {
  92.             return (string) $parentCommand->getPrivilege();
  93.         }
  94.         // if we don't have a parentCommand it must be a update,
  95.         // because the parentEntity must already exist
  96.         return AclRoleDefinition::PRIVILEGE_UPDATE;
  97.     }
  98.     /**
  99.      * @param WriteCommand[] $commands
  100.      */
  101.     private function findCommandByPath(string $commandPath, array $commands): ?WriteCommand
  102.     {
  103.         foreach ($commands as $command) {
  104.             if ($command->getPath() === $commandPath) {
  105.                 return $command;
  106.             }
  107.         }
  108.         return null;
  109.     }
  110. }