specshaper/gdpr-bundle

一个用于根据GDPR要求对实体参数进行分类并加密数据的Symfony 5|6包

资助包维护!
mogilvie

安装次数: 6,040

依赖关系: 0

建议者: 0

安全: 0

星级: 65

关注者: 12

分支: 11

开放问题: 4

类型:symfony-bundle

v3.0.1-alpha 2022-03-12 22:19 UTC

This package is auto-updated.

Last update: 2024-09-25 18:19:21 UTC


README

一个辅助实现通用数据保护条例(GDPR)要求的包。

功能包括

  • 适用于Symfony版本3|4|5|6,当前主分支和v3.0是Symfony 6
  • 提供用于添加到实体参数文档块中的注解 - 此方法正在被弃用。
  • 使用PersonalData对象和数据转换器。
  • 记录实体参数的数据保护影响评估值。
  • 使用SpecShaper\EncryptBundle加密敏感数据

版本历史

版本3

针对Symfny 5.4|6和PHP8进行了更新

版本2

版本2使用PersonalData实体来存储与个人数据参数相关的GDPR参数。

可以使用自定义twig函数

  • 解密任何加密数据
  • 以正确的格式显示当前数据。
  • 在数据被清理后显示已删除/汇总/匿名化数据

版本2功能

  • 创建存储实体
  • 为实体创建twig模板,以处理显示过期数据。
  • 创建迁移命令以创建新数据库字段,并将PersonalData属性转换为PersonalData实体行。
  • 创建废弃类和服务
  • 创建用于处理数据的命令
  • 实现一个cron任务来处理数据
  • 生成活动报告
  • 创建同意表单
  • 生成同意报告
  • 导出数据命令

版本1

本项目的版本1使用注解对实体参数的个人数据进行分类。
遗憾的是,这不能扩展到管理实时数据,它会在数据过期时遇到问题。应该显示什么?如何使用注解报告实时数据状态?

版本1功能

  • 生成实体参数覆盖率报告。
  • 生成所有实体参数和GDPR注解的摘要报告。

警告

  • 此包尚未进行单元测试。

文档

该文档的源代码存储在此包的Resources/doc/文件夹中。

许可证

此包在MIT许可证下。请参阅包中的完整许可证。

Resources/meta/LICENSE

关于

GdprBundle是为Parolla网站编写的,用于编码用户的私人数据。

报告问题或功能请求

问题和功能请求在Github问题跟踪器中跟踪。

报告错误时,最好在基于Symfony Standard Edition的基本项目中重现错误,以便包的开发者通过简单地克隆它并遵循一些步骤来重现问题。

安装

步骤1:下载包

打开命令行,进入您的项目目录,然后执行以下命令以下载此包的最新版本

$ composer require specshaper/gdpr-bundle dev-master

此命令要求您全局安装Composer,具体说明请参阅Composer文档中的安装章节

步骤 2:启用包

然后,通过将其添加到项目中app/AppKernel.php文件中已注册的包列表中,启用该包

<?php
// app/AppKernel.php

// ...
class AppKernel extends Kernel
{
    public function registerBundles()
    {
        $bundles = array(
            // ...
            new SpecShaper\GdprBundle\SpecShaperGdprBundle(),
            new SpecShaper\EncryptBundle\SpecShaperEncryptBundle(),
        );
        // ...
    }
    // ...
}

步骤 2:配置包

在您的参数文件中为encrypt_key添加一个空值。

# app/config/parameters.yaml

    # ...
    encrypt_key: ~

使用Encrypt包中的命令工具生成256位32字符密钥

$ bin/console encrypt:genkey

现在,替换您的加密密钥。

# app/config/parameters.yaml

    # ...
    encrypt_key: <your_key_here>
    

配置EncryptBundle。

# app/config/config.yaml

    # ...
    spec_shaper_encrypt:
        is_disabled: false

通过删除is_disabled或将其设置为true,可以禁用数据库的加密。

配置路由以仅在开发环境中访问报告

# app/config/routes/dev/spec_shaper_gdpr.yaml

   # ...
    spec_shaper_gdpr_reporting_coverage:
      path: /gdpr/reporting/coverage
      controller: SpecShaper\GdprBundle\Controller\ReportingController::coverageAction
      methods: GET

您应确保在安全设置中,/gdpr路径位于防火墙后面。

# app/config/security.yaml
    security:
        acces_control:
            # ...
            - { path: ^/gdpr/, role: [ROLE_SUPER_ADMIN] }

将个人数据doctrine类型添加到doctrine

# app/config/config.yaml
    doctrine:
        dbal:
            types:
                personal_data:  SpecShaper\GdprBundle\Types\PersonalDataType

步骤 3:创建实体(如果使用新的个人数据类型)。

使用个人数据列类型,并传递选项。

使用属性和即将弃用

<?php
    // ...
    use Symfony\Component\Validator\Constraints as Assert;
    use SpecShaper\GdprBundle\Validator\Constraints as GdprAssert;
    use SpecShaper\GdprBundle\Model\PersonalData;
    // ...
    
    /**
     * Iban bank account number.
     * 
     * @GdprAssert\PersonalData({
     *     @Assert\NotBlank,
     *     @Assert\Iban
     * })
     *
     * @ORM\Column(type="personal_data", nullable=true, options={
     *     "format" = "STRING",
     *     "isSensitive"=false,
     *     "isEncrypted"=true,
     *     "idMethod"="INDIRECT",
     *     "basisOfCollection"="LEGITIMATE_INTEREST",
     *     "identifiableBy"="Can be used to identify an individual if compared with third party database",
     *     "providedBy"="The employee, the employer",
     *     "purposeFor"="Used to pay employees by SEPA",
     *     "retainFor"="P6Y",
     *     "disposeBy"="SET_NULL",
     *     "methodOfReceipt"={"HTTPS"},
     *     "receiptProtection"={"TSS"},
     *     "methodOfReturn"={"HTTPS", "PDF"},
     *     "returnProtection"={"TSS","ENCRYPTED_PDF"}
     * })
     */
    protected PersonalData $iban;
   

从Php 8开始使用注解

    #[GdprAssert\PersonalData(new IreAssert\ValidPPS(groups: ['revenue'])]
    #[ORM\Column(type: 'personal_data', nullable: true, options: [
        'format' => 'STRING',
        'isSensitive' => false,
        'isEncrypted' => true,
        'basisOfCollection' => 'LEGITIMATE_INTEREST',
        'identifiableBy' => 'Can be used to identify an individual with tax records',
        'providedBy' => 'The employee, revenue, the employer',
        'purposeFor' => 'Used to submit tax returns to revenue and to employee',
        'retainFor' => 'P6Y',
        'disposeBy' => 'SET_NULL',
        'methodOfReceipt' => ['HTTPS'],
        'receiptProtection' => ['TSS'],
        'methodOfReturn' => ['HTTPS', 'PDF'],
        'returnProtection' => ['TSS', 'ENCRYPTED_PDF'],
    ])]
    protected ?PersonalData $taxNumber;

或使用多个断言,使用嵌套属性

    #[GdprAssert\PersonalData([
        new Assert\Iban(groups: ['bank_account']),
        new Assert\NotBlank(groups: ['bank_account']),
    ])]

查看PersonalData对象常量,以获取所有可用的选项范围。

PersonalData字段可以在实体内部通过将常规约束包装在PersonalData约束中来验证。

步骤 4:转换数据库。

使用以下命令更新您的数据库。

$bin/console gdpr:update

该命令将查找所有个人数据类型的列注解,并将存储的值转换为PersonalData对象。

使用命令选项'tables'转换特定的表和字段。
您可以输入一个类,以搜索该类中的所有属性。如果要特定类属性,请附加属性名称。您也可以附加多个类。

$ bin/console gdpr:update -t AppBundle/Entity/BankDetails
$ bin/console gdpr:update -t AppBundle/Entity/BankDetails:iban
$ bin/console gdpr:update -t AppBundle/Entity/BankDetails -t AppBundle/Entity/User:firstName

步骤 5:在表单中使用

在表单中使用PersonalDataType。注意,这与doctrine PersonalDataType不同。

<?php
// ...
use SpecShaper\GdprBundle\Form\Type\PersonalDataType;
    
    // ...
    $builder    
        ->add('iban', PersonalDataType::class, array(
            'required' => true,
            'label' => 'label.iban',
            'attr' => array(
                'placeholder' => 'placeholder.aValidInternationalBankAccountNumber'
            )
        ))
        ;

在大多数情况下,您使用PersonalData约束来包装其他约束来在实体中验证输入值,因为提交的数据已经通过数据转换器。

如果在表单中验证,则不需要使用PersonalData约束。只需正常使用约束即可。

<?php
// ...
use SpecShaper\GdprBundle\Form\Type\PersonalDataType;
    
    // ...
    $builder    
        ->add('iban', PersonalDataType::class, array(
            'required' => true,
            'label' => 'label.iban',
            'attr' => array(
                'placeholder' => 'placeholder.aValidInternationalBankAccountNumber'
            ),
            'constraints' => array(
                new Iban()
            )
        ))
        ;

步骤 5:在模板中解密

要在twig模板中查看您的数据

{{ employee.bankAccount.iban }}

这将调用PersonalData对象的toString方法,该方法将数据转换为在实体字段注解中设置的格式。

如果要访问不进行任何默认转换的数据,则使用

{{ employee.bankAccount.iban.data }}

如果使用select方法查询存储库,或获取数组结果,则doctrine onLoad事件订阅者不会解密任何加密值。

在这种情况下,请在渲染时使用twig_filter来解密您的值。

{{ employee.bankAccount.iban.data | personal_data }}

待办事项:使用twig_filter为personal_data传递渲染选项

{{ employee.bankAccount.iban.data | personal_data("date", "d M Y") }}
{{ employee.salary.data | personal_data("currency", "EUR") }}
{{ employee.height.data | personal_data("decimal", 2) }}

步骤 6:在存储库中解密

在数据库中加密数据的问题在于它不能再用于排序或搜索。

我们在实体存储库中使用了trait,以提供处理PersonalData对象的常用功能。

<?php
// src/AppBundle/Repository/Traits/GdprTrait.php

namespace AppBundle\Repository\Traits;

use SpecShaper\EncryptBundle\Encryptors\EncryptorInterface;
use SpecShaper\GdprBundle\Utils\Sorter;

/**
 * Trait GdprTrait
 *
 * Trait to provide common functions for encrypted fields in a repository.
 * - Decrypt & concatenate first and last name
 * - Sort by two fields.
 *
 * @package AppBundle\Repository\Traits
 */
trait GdprTrait
{
    protected EncryptorInterface $encryptor;

    /**
     * Setter injection Encryptor into repository.
     */
    public function setEncryptor(EncryptorInterface $encryptor): EncryptorInterface
    {
        $this->encryptor = $encryptor;
        return $this;
    }

    /**
     * Get the Encryptor.
     */
    public function getEncryptor(): EncryptorInterface
    {
        return $this->encryptor;
    }

    /**
     * Function to concat two encrypted values into one new value.
     */
    public function concatToFullName(
         array &$collection, 
         ?string $firstNameField = 'firstName',
         ?string $lastNameField = 'lastName',
         ?string $outputField = 'fullName'
         ): array
         {

        foreach($collection as $key => $entity){
            $firstName = $this->getEncryptor()->decrypt($entity[$firstNameField]->getData());
            $lastName = $this->getEncryptor()->decrypt($entity[$lastNameField]->getData());
            $collection[$key][$firstNameField]->setData($firstName);
            $collection[$key][$lastNameField]->setData($lastName);
            $collection[$key][$outputField] = $firstName . ' ' . $lastName;
        }

        return $collection;
    }

    /**
     * Sort a array hydrated query result by two columns.
     */
    public function sortByTwoColumns(
        array &$result,
        ?string $firstOrder = 'employeeId',
        ?string$secondOrder = 'lastName'
        ): array
        {
            // Use SpecShaper\ThemeBundle\Util\Sorter:sortByTwoColumnsCallback as a callback
            usort($result, array(new Sorter($firstOrder, $secondOrder),'sortByTwoColumnsCallback'));
            return $result;
        }
  
}

该trait用于存储库。

<?php

namespace AppBundle\Repository;

use AppBundle\Repository\Traits\GdprTrait;

/**
 * EmployeeRepository
 *
 * This class was generated by the Doctrine ORM. Add your own custom
 * repository methods below.
 */
class EmployeeRepository extends \Doctrine\ORM\EntityRepository
{
    use GdprTrait;
    //....
}

作为服务使用存储库时,在构造时注入加密器。

<?php

namespace App\Repository;

use App\Entity\Organisation;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\ORM\QueryBuilder;
use Doctrine\Persistence\ManagerRegistry;
use SpecShaper\EncryptBundle\Encryptors\EncryptorInterface;

/**
 * OrganisationRepository
 */
class OrganisationRepository extends ServiceEntityRepository
{
    private EncryptorInterface $encryptor;
    
    public function __construct(ManagerRegistry $registry, EncryptorInterface $encryptor)
    {
        parent::__construct($registry, Organisation::class);
        $this->encryptor = $encryptor;
    }
    //....    
}

或者,在控制器中使用setter。

<?php

namespace AppBundle\Controller;

use AppBundle\Entity\Employee;
use SpecShaper\EncryptBundle\Encryptors\EncryptorInterface;

/**
 * Employee controller.
 *
 * @Route("/employee")
 */
class EmployeeController extends Controller
{
    /**
     * Lists all Employee entities.
     *
     * @Route("/{id}/all", name="employee_all")
     * @Method("GET")
     */
    public function allAction(EncryptorInterface $encryptor)
    {
        $employee = $this->getDoctrine()
            ->getRepository(Employee::class)
            ->setEncryptor($encryptor)
            ->findAll();
    }
}

步骤 7:报告

覆盖率报告

通过在浏览器中导航到'\gdpr\reporting\coverage'来访问覆盖率报告。

这将提供包含所有由实体管理器管理的实体和参数的Excel文件。如果任何参数包含"personal_data"列类型,它还将列出每个属性的值。

请注意,目前我们只从默认实体管理器中提取信息。我需要改进覆盖率报告以获取所有entityManagers。

历史报告

@todo 尚未编写。

日志条目的报告

  • 创建和更新PersonalData对象。
  • 销毁PersonalData对象。
  • 导出PersonalData对象。