All Projects → mogilvie → GdprBundle

mogilvie / GdprBundle

Licence: MIT license
A symfony3 bundle to assist with defining data in accordance with GDPR, and for encrypting and reporting.

Programming Languages

PHP
23972 projects - #3 most used programming language

Projects that are alternatives of or similar to GdprBundle

Library-Spring
The library web application where you can borrow books. It's Spring MVC and Hibernate project.
Stars: ✭ 73 (+19.67%)
Mutual labels:  annotations
DyAnnotationExtractor
DyAnnotationExtractor is software for extracting annotations (highlighted text and comments) from e-documents like PDF.
Stars: ✭ 34 (-44.26%)
Mutual labels:  annotations
code-art
🌈 Collect beautiful art text, never bug. 收集好看的艺术代码,佛祖保佑,永无 Bug。找好看的注释,看这里。
Stars: ✭ 38 (-37.7%)
Mutual labels:  annotations
GEAN
This toolkit deals with GEnomic sequence and genome structure ANnotation files between inbreeding lines and species.
Stars: ✭ 36 (-40.98%)
Mutual labels:  annotations
cookieconsent
🍪 Simple cross-browser cookie-consent plugin written in vanilla js
Stars: ✭ 2,158 (+3437.7%)
Mutual labels:  gdpr
tag-manager
Website analytics, JavaScript error tracking + analytics, tag manager, data ingest endpoint creation (tracking pixels). GDPR + CCPA compliant.
Stars: ✭ 279 (+357.38%)
Mutual labels:  gdpr
attributes
PHP Attributes Reader. Subtree split of the Spiral Attributes component (see spiral/framework)
Stars: ✭ 22 (-63.93%)
Mutual labels:  annotations
jedy
Jedy CMS Multi-language is created with Symfony 3
Stars: ✭ 31 (-49.18%)
Mutual labels:  symfony3
goat
Annotate Images (or goats) On The Web™
Stars: ✭ 75 (+22.95%)
Mutual labels:  annotations
clothing-detection-ecommerce-dataset
Clothing detection dataset
Stars: ✭ 43 (-29.51%)
Mutual labels:  annotations
obsidian-hypothesis-plugin
An Obsidian.md plugin that syncs highlights from Hypothesis.
Stars: ✭ 164 (+168.85%)
Mutual labels:  annotations
TouchPortalPluginSDK
This Project is an SDK to create a Touch Portal Plugin using Java or Kotlin and Gradle
Stars: ✭ 32 (-47.54%)
Mutual labels:  annotations
anor
anor: an annotation and visualization system based on R and Shiny framework
Stars: ✭ 28 (-54.1%)
Mutual labels:  annotations
boost-reflection
This library provides Java-like Reflection API to C++ language.
Stars: ✭ 16 (-73.77%)
Mutual labels:  annotations
annotate
Create 3D labelled bounding boxes in RViz
Stars: ✭ 104 (+70.49%)
Mutual labels:  annotations
serde with
This crate provides custom de/serialization helpers to use in combination with serde's `with`-annotation and with the improved `serde_as`-annotation.
Stars: ✭ 392 (+542.62%)
Mutual labels:  annotations
young-examples
java学习和项目中一些典型的应用场景样例代码
Stars: ✭ 21 (-65.57%)
Mutual labels:  annotations
auto-parcel
A fast annotation processor to make your objects `Parcelable` without writing any of the boilerplate.
Stars: ✭ 80 (+31.15%)
Mutual labels:  annotations
privapi
Detect Sensitive REST API communication using Deep Neural Networks
Stars: ✭ 42 (-31.15%)
Mutual labels:  gdpr
privera
Use the tools you know. Respect users' privacy. Forget cookie consents. Comply with GDPR, ePrivacy, COPPA, CalOPPA, PECR, PIPEDA, CASL; you name it.
Stars: ✭ 23 (-62.3%)
Mutual labels:  gdpr

GDPR Bundle

A bundle to aid with the General Data Protection Regulation requirements.

Features include:

  • Written for Symfony version 3|4|5|6, current master and v3.0 is Symfony 6
  • Provides annotation for adding to entity parameter doc blocks - this method is being deprecated.
  • Uses a PersonalData object and data transformers.
  • Records values for Data Protection Impact Assessments of entity parameters.
  • Uses SpecShaper\EncryptBundle to encrypt sensitive data

Version History

Version 3

Updated for Symfny 5.4|6 and PHP8

Version 2

Version 2 uses a PersonalData entity to store the GDPR parameters associated with the personal data parameter.

A custom twig function can be used to:

  • Decrypt any encrypted data
  • Display current data in its correct format.
  • Display deleted/aggregated/annonymised data once it has been sanitised

Version 2 Features:

  • Create a storage entity
  • Create twig templates for entity to handle displaying expired data.
  • Create a migration command to create new database fields, and convert PersonalData attributes to PersonalData entity rows.
  • Create disposal classes and service
  • Create a command to dispose of data
  • Implement a cron task to dispose of data
  • Generate activity report
  • Create consent forms
  • Generate consent report
  • Export data command

Version 1

Version 1 of this project used annotations to classify entity parameter personal data.
This unfortunately could not be extended to managing live data, it runs into problems where data become expired. What should get displayed instead? How can live data status be reported with annotations?

Version 1 Features:

  • Generate a entity parameter coverage report.
  • Generate a summary report of all entity parameters and GDPR annotations.

Warning

  • This bundle has not been unit tested.

Documentation

The source of the documentation is stored in the Resources/doc/ folder in this bundle.

License

This bundle is under the MIT license. See the complete license in the bundle:

Resources/meta/LICENSE

About

GdprBundle has been written for Parolla website to encode users private data.

Reporting an issue or a feature request

Issues and feature requests are tracked in the Github issue tracker.

When reporting a bug, it may be a good idea to reproduce it in a basic project built using the Symfony Standard Edition to allow developers of the bundle to reproduce the issue by simply cloning it and following some steps.

Installation

Step 1: Download the bundle

Open a command console, enter your project directory and execute the following command to download the latest version of this bundle:

$ composer require specshaper/gdpr-bundle dev-master

This command requires you to have Composer installed globally, as explained in the installation chapter of the Composer documentation.

Step 2: Enable the bundle

Then, enable the bundle by adding it to the list of registered bundles in the app/AppKernel.php file of your project:

<?php
// app/AppKernel.php

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

Step 2: Configure the bundle

Add an empty value for encrypt_key to your parameters file.

# app/config/parameters.yaml

    # ...
    encrypt_key: ~

Geneate a 256 bit 32 character key using the command tool in the Encrypt bundle

$ bin/console encrypt:genkey

Now, replace your encryption key.

# app/config/parameters.yaml

    # ...
    encrypt_key: <your_key_here>
    

Configure the EncryptBundle.

# app/config/config.yaml

    # ...
    spec_shaper_encrypt:
        is_disabled: false

You can disable encryption of the database by setting deleting is_disabled or setting it true.

Configure the routing to access the reports in dev environment only:

# 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

You should make sure that the /gdpr path is behind a firewall in your security settings.

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

Add the personal_data doctrine type to doctrine

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

Step 3: Create the entities if using the new personal_data type.

User the personal_data column type, and pass the options.

Using Attributes and to be deprecated:

<?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;
   

Using Annotations going forward under 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;

Or with multiple Assertions use nested attributes

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

Look at the PersonalData object constants for the full range of options available.

The PersonalData field can be validated from within the entity by wrapping regular constraints within the PersonalData constraint.

Step 4: Converting your database.

Use the command below to update your database.

$bin/console gdpr:update

The command will find all Column annotations of type personal_data and convert the stored value to a PersonalData object.

Use the command option 'tables' to convert specific tables and fields.
You can enter a class, to search every property in the class. Or for specific class properties then append the property name. You can also append multiple classes.

$ 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

Step 5: Use in forms

Use the PersonalDataType in forms. Note that this is different from the 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'
            )
        ))
        ;

In most cases you validate the entered value in the enitity using the PersonalData constraint to wrap other constraints. This is because the submitted data has been through the data transformer.

If you are validating in the form then you do not need to use the PersonalData constraint. Just use your constraints as normal.

<?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()
            )
        ))
        ;

Step 5: Decrypt in templates

To view your data in a twig template:

{{ employee.bankAccount.iban }}

This will call the toString method of the PersonalData object, which will convert the data to its format as set in the entity field annotation.

If you want to access the data without any default conversion then use:

{{ employee.bankAccount.iban.data }}

If you query a repository using a select method, or get an array result then the doctrine onLoad event subscriber will not decyrpt any encrypted values.

In this case, use the twig_filter to decrypt your value when rendering.

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

Todo: Use the twig_filter for personal_data to pass rendering options:

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

Step 6: Decrypt in repository

The problem with encrypting data in the DB is that it can no longer be used for ordering or searching.

We use a trait in the entity repositories to provide common functions for dealing with PersonalData objects.

<?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;
        }
  
}

The trait is used in the repository.

<?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;
    //....
}

Using a repository as a service, inject the encryptor during construction.

<?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;
    }
    //....    
}

Alternatively, use the setter in the controller.

<?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();
    }
}

Step 7: Reporting

Coverage Report

Access the coverage report by navigating your browser to '\gdpr\reporting\coverage'.

This will serve an excel file that contains all the entities and parameters managed by the entity manager. If any of the parameters contain the "personal_data" column type it will also list each of the attributes values.

Note that at the moment we are only pulling information from the default entity manager. I need to improve the coverage report to get all entityManagers.

History Report

@todo Not yet written.

A report of log entries for:

  • PersonalData object creation and updates.
  • PersonalData object disposal.
  • PersonalData objects exported.
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].