vendor/shopware/core/Framework/App/Subscriber/CustomFieldProtectionSubscriber.php line 46

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\App\Subscriber;
  3. use Doctrine\DBAL\Connection;
  4. use Shopware\Core\Framework\Api\Context\AdminApiSource;
  5. use Shopware\Core\Framework\Api\Context\SystemSource;
  6. use Shopware\Core\Framework\Context;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\InsertCommand;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Write\Command\WriteCommand;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PreWriteValidationEvent;
  10. use Shopware\Core\Framework\Log\Package;
  11. use Shopware\Core\Framework\Uuid\Uuid;
  12. use Shopware\Core\Framework\Validation\WriteConstraintViolationException;
  13. use Shopware\Core\System\CustomField\Aggregate\CustomFieldSet\CustomFieldSetDefinition;
  14. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  15. use Symfony\Component\Validator\ConstraintViolation;
  16. use Symfony\Component\Validator\ConstraintViolationInterface;
  17. use Symfony\Component\Validator\ConstraintViolationList;
  18. /**
  19.  * @internal only for use by the app-system, will be considered internal from v6.4.0 onward
  20.  */
  21. #[Package('core')]
  22. class CustomFieldProtectionSubscriber implements EventSubscriberInterface
  23. {
  24.     public const VIOLATION_NO_PERMISSION 'no_permission_violation';
  25.     private Connection $connection;
  26.     public function __construct(Connection $connection)
  27.     {
  28.         $this->connection $connection;
  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 [
  36.             PreWriteValidationEvent::class => 'checkWrite',
  37.         ];
  38.     }
  39.     public function checkWrite(PreWriteValidationEvent $event): void
  40.     {
  41.         $context $event->getContext();
  42.         if ($context->getSource() instanceof SystemSource || $context->getScope() === Context::SYSTEM_SCOPE) {
  43.             return;
  44.         }
  45.         $integrationId $this->getIntegrationId($context);
  46.         $violationList = new ConstraintViolationList();
  47.         foreach ($event->getCommands() as $command) {
  48.             if (
  49.                 !($command->getDefinition() instanceof CustomFieldSetDefinition)
  50.                 || $command instanceof InsertCommand
  51.             ) {
  52.                 continue;
  53.             }
  54.             $appIntegrationId $this->fetchIntegrationIdOfAssociatedApp($command);
  55.             if (!$appIntegrationId) {
  56.                 continue;
  57.             }
  58.             if ($integrationId !== $appIntegrationId) {
  59.                 $this->addViolation($violationList$command);
  60.             }
  61.         }
  62.         if ($violationList->count() > 0) {
  63.             $event->getExceptions()->add(new WriteConstraintViolationException($violationList));
  64.         }
  65.     }
  66.     private function getIntegrationId(Context $context): ?string
  67.     {
  68.         $source $context->getSource();
  69.         if (!($source instanceof AdminApiSource)) {
  70.             return null;
  71.         }
  72.         return $source->getIntegrationId();
  73.     }
  74.     private function fetchIntegrationIdOfAssociatedApp(WriteCommand $command): ?string
  75.     {
  76.         $id $command->getPrimaryKey()['id'];
  77.         $integrationId $this->connection->executeQuery('
  78.             SELECT `app`.`integration_id`
  79.             FROM `app`
  80.             INNER JOIN `custom_field_set` ON `custom_field_set`.`app_id` = `app`.`id`
  81.             WHERE `custom_field_set`.`id` = :customFieldSetId
  82.         ', ['customFieldSetId' => $id])->fetchOne();
  83.         if (!$integrationId) {
  84.             return null;
  85.         }
  86.         return Uuid::fromBytesToHex($integrationId);
  87.     }
  88.     private function addViolation(ConstraintViolationList $violationListWriteCommand $command): void
  89.     {
  90.         $violationList->add(
  91.             $this->buildViolation(
  92.                 'No permissions to %privilege%".',
  93.                 ['%privilege%' => 'write:custom_field_set'],
  94.                 '/' $command->getDefinition()->getEntityName(),
  95.                 self::VIOLATION_NO_PERMISSION
  96.             )
  97.         );
  98.     }
  99.     /**
  100.      * @param array<string, string> $parameters
  101.      */
  102.     private function buildViolation(
  103.         string $messageTemplate,
  104.         array $parameters,
  105.         ?string $propertyPath null,
  106.         ?string $code null
  107.     ): ConstraintViolationInterface {
  108.         return new ConstraintViolation(
  109.             str_replace(array_keys($parameters), array_values($parameters), $messageTemplate),
  110.             $messageTemplate,
  111.             $parameters,
  112.             null,
  113.             $propertyPath,
  114.             null,
  115.             null,
  116.             $code
  117.         );
  118.     }
  119. }