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 $violationList, WriteCommand $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. }