Symfony 4 - поля шифрования / дешифрования, используемые в доктрине со службой? - PullRequest
0 голосов
/ 03 октября 2018

Итак, у меня есть поля в базе данных в сущности

class Person
{
    // other fields

    /**
     * @var string
     *
     * @ORM\Column(name="last_name", type="string", length=50, nullable=false)
     */
    private $lastName;

    /**
     * @var string
     *
     * @ORM\Column(name="first_name", type="string", length=50, nullable=false)
     */
    private $firstName;

   // getters and setters
}

У меня есть служба под названием SecureEncryptor.В ней есть функции Decrypt () и Encrypt () - в основном вы просто передаете ей зашифрованную / незашифрованную (соответственно) строку, и она сделает следующее.

Проблема в том, что я не уверен, как это использоватьУслуга в сочетании с сущностью - особенно с учетом форм (типов).Я имею в виду, что знаю, что мог бы просто получить поле и вызвать функцию Decrypt, но это не сработало бы с типом, связанным с сущностью Person.

Я начал создавать отдельную сущность для расшифрованного лица, а затем переключал ее, когда приходило время работать с базой данных, но это казалось неправильным.Моя другая идея состояла в том, чтобы позвонить в эту службу из Организации, но я также прочитал, что это неправильно.

Есть идеи?

Редактировать:

Этоэто в основном то, что я хочу сделать:

$builder->get('dateOfBirth')
   ->addModelTransformer(new CallbackTransformer(
       function ($encryptedDOB) {
           return $this->encryptor->decrypt($encryptedDOB, salt); // How do I get the salt value here?
       },
      function ($decryptedDOB) {
         return $this->encryptor->encrypt($decryptedDOB, salt); // How do I get the salt value here?
      }
 ));

или, возможно, расшифровать / зашифровать данные ДО этого шага, но не уверен, как это сделать.

РЕДАКТИРОВАТЬ 2:

Я нашел это , которое показывает, что вы можете получить данные сущности в событии PRE_SET_DATA, но вы не можете добавить туда преобразователь данных, поэтому не знаете, как это может работать.

Ответы [ 2 ]

0 голосов
/ 05 октября 2018

Ну, после трех дней возни с этим - вероятно, более 20 часов разочарования, я наконец нашел ПРАВИЛЬНЫЙ способ сделать это. Слушатели событий объекта

Поэтому я внес следующие изменения

app \ config \ services.yaml

parameters:
    ...
    encryption_key: '%kernel.project_dir%/path/to/my/key'

services:
    ...
    App\EventListeners\PatientListener:
        arguments: [@session]
        tags:
            - { name: doctrine.event_listener, event: prePersist }
            - { name: doctrine.event_listener, event: preUpdate }
            - { name: doctrine.event_listener, event: postLoad }

Изатем я сделал сервис

<?php

namespace App\EventListeners;

use Doctrine\Common\Persistence\Event\LifecycleEventArgs;
use App\Entity\Patients;
use ParagonIE\Halite\HiddenString;
use ParagonIE\Halite\KeyFactory;
use ParagonIE\Halite\Symmetric\Crypto as Symmetric;
use ParagonIE\Halite\Symmetric\EncryptionKey;
use Psr\Log\LoggerInterface;
use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
use Symfony\Component\HttpFoundation\Session\Session;

class PatientListener
{
    private $params;
    private $session;
    private $logger;

    public function __construct(ParameterBagInterface $params, 
                                 Session $session, LoggerInterface $logger)
    {
        $this->params = $params;
        $this->session = $session;
        $this->logger = $logger;
    }

    public function prePersist(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();

        if ($entity instanceof Patients)
        {
            $this->encryptFields($entity);
        }
    }

    public function preUpdate(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();

        if ($entity instanceof Patients)
        {
            $this->encryptFields($entity);
        }
    }

    public function postLoad(LifecycleEventArgs $args)
    {
        $entity = $args->getObject();

        if ($entity instanceof Patients)
        {
            $this->decryptFields($entity);
        }
    }

    private function loadKey() : EncryptionKey
    {
        try
        {
            KeyFactory::loadEncryptionKey($this->params->get('encryption_key'));
        }
        catch(\Throwable $e)
        {
            $this->session->getFlashBag()->add('danger', 'Unable to load encryption key!');
            $this->logger->emergency(
                'Unable to lod the encryption key!', array(
                'error' => $e->getMessage(),
            ));
            throw;
        }
    }

    private function encryptFields(Patients $patient)
    {
        $key = $this->loadKey();

        // Encrypt the variables
        $lastName = $this->encrypt('Last Name', $patient->getLastName(), $key);

        // Set the entity variables
        $patient->setLastName($lastName);

        return $patient;
    }

    private function encrypt($fieldName, $value, $key)
    {
        try {
            return Symmetric::encrypt(
                new HiddenString($value),
                $key
            );
        } catch(\Throwable $e)
        {
            $this->session->getFlashBag()->add('danger', 'Unable to encrypt field');
            $this->logger->critical(
                'Unable to encrypt field "'.$fieldName.'" in Patients entity. DB update terminated.', array(
                'error' => $e->getMessage(),
            ));
            throw;
        }

    }

    private function decryptFields(Patients $patient)
    {
        $key = $this->loadKey();
        $id = $patient->getId();

        // Decrypt the variables
        $lastName = $this->decrypt($id, 'Last Name', $patient->getLastName(), $key);

        // Set the entity variables
        $patient->setLastName($lastName);
    }

    private function decrypt($id, $fieldName, $value, $key)
    {
        try
        {
            return Symmetric::decrypt($value, $key);
        }
        catch (\Throwable $e)
        {
            $this->session->getFlashBag()->add('warning', 'Unable to decrypt field');
            $this->logger->warning(
                'Unable to decrypt field "'.$fieldName.'" in Patients entity for ID: '.$id, array(
                'error' => $e->getMessage(),
            ));
        }
    }
}

и теперь данные зашифровываются при загрузке в базу данных и дешифруются при загрузке в объект.

Этот способ является правильным, потому что при выполнении этого любым другим способом (пользовательский тип доктрины, преобразователь данных, выполнение в контроллере и т. Д.) Всегда есть вероятность, что, если кто-то другой создаст другую форму или использует объект в другомконтроллер, который, возможно, оставляет данные расшифрованными (иначе очень плохо).Этот способ гарантирует, что данные ВСЕГДА будут шифроваться и дешифроваться должным образом с помощью доктрины (если только вы не выполняете какой-либо пользовательский DQL \ SQL, в этом случае вам может потребоваться обрабатывать это самостоятельно в зависимости от того, что вы делаете).

0 голосов
/ 03 октября 2018

Если я правильно понимаю, вы ищете способ загружать / хранить зашифрованные данные в базе данных прозрачным способом.Я думаю, что я бы реализовал пользовательский тип доктрины и расшифровал / зашифровал здесь при загрузке / сохранении.

Редактировать: Его не так сложно реализовать, в качестве основы вы можете использовать Doctrine\DBAL\Types\TextType, которыйвы будете расширять, там вас интересуют convertToPHPValue() - расшифровка и convertToDatabaseValue() - шифрование.Для большего количества примеров посмотрите и определение типов доктрин и найдите тот, который соответствует вашим потребностям.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...