vendor/shopware/core/Framework/DataAbstractionLayer/EntityProtection/EntityProtectionValidator.php line 56

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Core\Framework\DataAbstractionLayer\EntityProtection;
  3. use Shopware\Core\Framework\Context;
  4. use Shopware\Core\Framework\DataAbstractionLayer\EntityDefinition;
  5. use Shopware\Core\Framework\DataAbstractionLayer\Event\EntitySearchedEvent;
  6. use Shopware\Core\Framework\DataAbstractionLayer\Field\AssociationField;
  7. use Shopware\Core\Framework\DataAbstractionLayer\Field\Field;
  8. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  9. use Shopware\Core\Framework\DataAbstractionLayer\Write\Validation\PreWriteValidationEvent;
  10. use Shopware\Core\Framework\Log\Package;
  11. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  12. use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
  13. /**
  14. * @deprecated tag:v6.5.0 - reason:becomes-internal - EventSubscribers will become internal in v6.5.0
  15. */
  16. #[Package('core')]
  17. class EntityProtectionValidator implements EventSubscriberInterface
  18. {
  19. /**
  20. * @return array<string, string|array{0: string, 1: int}|list<array{0: string, 1?: int}>>
  21. */
  22. public static function getSubscribedEvents()
  23. {
  24. return [
  25. PreWriteValidationEvent::class => 'validateWriteCommands',
  26. EntitySearchedEvent::class => 'validateEntitySearch',
  27. ];
  28. }
  29. /**
  30. * @param list<array{entity: string, value: string|null, definition: EntityDefinition, field: Field|null}> $pathSegments
  31. * @param array<string> $protections FQCN of the protections that need to be validated
  32. */
  33. public function validateEntityPath(array $pathSegments, array $protections, Context $context): void
  34. {
  35. foreach ($pathSegments as $pathSegment) {
  36. /** @var EntityDefinition $definition */
  37. $definition = $pathSegment['definition'];
  38. foreach ($protections as $protection) {
  39. $protectionInstance = $definition->getProtections()->get($protection);
  40. if (!$protectionInstance || $protectionInstance->isAllowed($context->getScope())) {
  41. continue;
  42. }
  43. throw new AccessDeniedHttpException(
  44. sprintf('API access for entity "%s" not allowed.', $pathSegment['entity'])
  45. );
  46. }
  47. }
  48. }
  49. public function validateEntitySearch(EntitySearchedEvent $event): void
  50. {
  51. $definition = $event->getDefinition();
  52. $readProtection = $definition->getProtections()->get(ReadProtection::class);
  53. $context = $event->getContext();
  54. if ($readProtection && !$readProtection->isAllowed($context->getScope())) {
  55. throw new AccessDeniedHttpException(
  56. sprintf(
  57. 'Read access to entity "%s" not allowed for scope "%s".',
  58. $definition->getEntityName(),
  59. $context->getScope()
  60. )
  61. );
  62. }
  63. $this->validateCriteriaAssociation(
  64. $definition,
  65. $event->getCriteria()->getAssociations(),
  66. $context
  67. );
  68. }
  69. public function validateWriteCommands(PreWriteValidationEvent $event): void
  70. {
  71. foreach ($event->getCommands() as $command) {
  72. // Don't validate commands that fake operations on DB level, e.g. cascade deletes
  73. if (!$command->isValid()) {
  74. continue;
  75. }
  76. $writeProtection = $command->getDefinition()->getProtections()->get(WriteProtection::class);
  77. if ($writeProtection && !$writeProtection->isAllowed($event->getContext()->getScope())) {
  78. throw new AccessDeniedHttpException(
  79. sprintf(
  80. 'Write access to entity "%s" are not allowed in scope "%s".',
  81. $command->getDefinition()->getEntityName(),
  82. $event->getContext()->getScope()
  83. )
  84. );
  85. }
  86. }
  87. }
  88. /**
  89. * @param array<string, Criteria> $associations
  90. */
  91. private function validateCriteriaAssociation(EntityDefinition $definition, array $associations, Context $context): void
  92. {
  93. /** @var Criteria $criteria */
  94. foreach ($associations as $associationName => $criteria) {
  95. $field = $definition->getField($associationName);
  96. if (!$field instanceof AssociationField) {
  97. continue;
  98. }
  99. $associationDefinition = $field->getReferenceDefinition();
  100. $readProtection = $associationDefinition->getProtections()->get(ReadProtection::class);
  101. if ($readProtection && !$readProtection->isAllowed($context->getScope())) {
  102. throw new AccessDeniedHttpException(
  103. sprintf(
  104. 'Read access to nested association "%s" on entity "%s" not allowed for scope "%s".',
  105. $associationName,
  106. $definition->getEntityName(),
  107. $context->getScope()
  108. )
  109. );
  110. }
  111. $this->validateCriteriaAssociation($associationDefinition, $criteria->getAssociations(), $context);
  112. }
  113. }
  114. }