platform/src/Core/Framework/App/Subscriber/CustomFieldProtectionSubscriber.php line 47

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