platform/src/Core/Framework/DataAbstractionLayer/EntityProtection/EntityProtectionValidator.php line 46

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