specshaper / gdpr-bundle
一个用于根据GDPR要求对实体参数进行分类并加密数据的Symfony 5|6包
Requires
- php: >=8.0
- roromix/spreadsheetbundle: ^1.0
- specshaper/encrypt-bundle: ^3.0
- symfony/console: ^5.4|^6.0
- symfony/form: ^5.4|^6.0
- symfony/framework-bundle: ^5.4|^6.0
- symfony/orm-pack: *
- symfony/security-bundle: ^5.4|^6.0
- symfony/twig-bundle: ^5.4|^6.0
- symfony/validator: ^5.4|^6.0
- twig/twig: ^3.0
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对象。