vendor/eckinox/sensitive-data-bundle/src/EventSubscriber/SensitiveFieldSubscriber.php line 51

Open in your IDE?
  1. <?php
  2. namespace Eckinox\SensitiveDataBundle\EventSubscriber;
  3. use Eckinox\SensitiveDataBundle\Form\SensitiveTypeInterface;
  4. use Eckinox\SensitiveDataBundle\Security\SensitiveDataMapper;
  5. use Eckinox\SensitiveDataBundle\Security\SensitiveDataRetriever;
  6. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  7. use Symfony\Component\Form\FormEvent;
  8. use Symfony\Component\Form\FormEvents;
  9. use Symfony\Component\Form\FormInterface;
  10. use Symfony\Component\HttpFoundation\Request;
  11. use Symfony\Component\HttpFoundation\RequestStack;
  12. use Symfony\Component\Serializer\NameConverter\CamelCaseToSnakeCaseNameConverter;
  13. /**
  14.  * The `SensitiveFieldSubscriber`'s job is to set the data of
  15.  * sensitive fields to their original value if they haven't been
  16.  * changed by the user, in order to prevent empty or bogus values
  17.  * from replacing the real user data when submitting the form.
  18.  */
  19. class SensitiveFieldSubscriber implements EventSubscriberInterface
  20. {
  21.     /** @var SensitiveDataMapper */
  22.     private $sensitiveDataMapper;
  23.     /** @var SensitiveDataRetriever */
  24.     private $sensitiveDataRetriever;
  25.     /** @var Request */
  26.     private $request;
  27.     /** @var array<string,array<int,object>> */
  28.     private $preChangeEntities = [];
  29.     public function __construct(SensitiveDataMapper $sensitiveDataMapperSensitiveDataRetriever $sensitiveDataRetrieverRequestStack $requestStack)
  30.     {
  31.         $this->sensitiveDataMapper $sensitiveDataMapper;
  32.         $this->sensitiveDataRetriever $sensitiveDataRetriever;
  33.         $this->request $requestStack->getCurrentRequest();
  34.     }
  35.     public static function getSubscribedEvents()
  36.     {
  37.         return [
  38.             FormEvents::POST_SET_DATA => 'onPostSetData',
  39.             FormEvents::SUBMIT => 'onSubmit',
  40.         ];
  41.     }
  42.     public function onPostSetData(FormEvent $event): void
  43.     {
  44.         if (empty($this->request->request->all())) {
  45.             return;
  46.         }
  47.         $form $event->getForm();
  48.         $this->storeSensitiveEntityData($form);
  49.     }
  50.     public function onSubmit(FormEvent $event): void
  51.     {
  52.         $form $event->getForm();
  53.         $this->restoreUnchangedSensitiveValues($form);
  54.     }
  55.     /**
  56.      * Stores a version of each entity affected by the form before the user's
  57.      * submitted data is loaded into them.
  58.      *
  59.      * This allows us to restore the sensitive data on submit for situations
  60.      * where it would've been replaced by the "• • • • •" placeholder value.
  61.      */
  62.     private function storeSensitiveEntityData(FormInterface $form): void
  63.     {
  64.         static $handledFormTypes = [];
  65.         $objectId spl_object_id($form);
  66.         if (isset($handledFormTypes[$objectId])) {
  67.             return;
  68.         }
  69.         $handledFormTypes[$objectId] = true;
  70.         // Check if the form is sensitive - if so, it needs to be processed
  71.         if ($form->getConfig()->getType()->getInnerType() instanceof SensitiveTypeInterface) {
  72.             $entity $this->sensitiveDataMapper->getEntityFromForm($form);
  73.             $entityClass get_class($entity);
  74.             if (!isset($this->preChangeEntities[$entityClass])) {
  75.                 $this->preChangeEntities[$entityClass] = [];
  76.             }
  77.             $this->preChangeEntities[$entityClass][$entity->getId()] = clone $entity;
  78.         }
  79.         // Recurse on all child forms
  80.         foreach ($form->all() as $childForm) {
  81.             $this->storeSensitiveEntityData($childForm);
  82.         }
  83.     }
  84.     /**
  85.      * Restores the sensitive data that has been replaced by the "• • • • •"
  86.      * placeholder value.
  87.      */
  88.     private function restoreUnchangedSensitiveValues(FormInterface $form): void
  89.     {
  90.         static $handledFormTypes = [];
  91.         $objectId spl_object_id($form);
  92.         if (isset($handledFormTypes[$objectId])) {
  93.             return;
  94.         }
  95.         $handledFormTypes[$objectId] = true;
  96.         // Check if the form is sensitive - if so, it needs to be processed
  97.         if ($form->getConfig()->getType()->getInnerType() instanceof SensitiveTypeInterface) {
  98.             $value $form->getData();
  99.             // Check if the value is the sensitive data replacement/placeholder
  100.             if (strpos($value'•') !== false && !strlen(str_replace([' ''•'], ''$value))) {
  101.                 // Retrieve the secret from the pre-submit entity
  102.                 $entity $this->sensitiveDataMapper->getEntityFromForm($form);
  103.                 $preChangeEntity $this->preChangeEntities[get_class($entity)][$entity->getId()] ?? null;
  104.                 $mapping $this->sensitiveDataMapper->getMapping($entity$form->getName());
  105.                 $realValue $this->sensitiveDataRetriever->getSecret($mapping$preChangeEntity ?: $entity);
  106.                 // Restore the secret in the entity
  107.                 $caseConverter = new CamelCaseToSnakeCaseNameConverter();
  108.                 $setter 'set'.ucfirst($caseConverter->denormalize($form->getName()));
  109.                 $entity->$setter($realValue);
  110.             }
  111.         }
  112.         // Recurse on all child forms
  113.         foreach ($form->all() as $childForm) {
  114.             $this->restoreUnchangedSensitiveValues($childForm);
  115.         }
  116.     }
  117. }