-
Notifications
You must be signed in to change notification settings - Fork 3
Progressing GDPR #1502
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Progressing GDPR #1502
Changes from 14 commits
Commits
Show all changes
37 commits
Select commit
Hold shift + click to select a range
10aca52
Progressing GDPR
stunnerparas d1e2af3
Apply php-cs-fixer changes
stunnerparas 28fc4da
Addition of new manager function to run job and fixes
stunnerparas afe0939
Apply php-cs-fixer changes
stunnerparas 86cf5e3
Static code analysis fixes
stunnerparas bfdbf3e
Apply php-cs-fixer changes
stunnerparas f481324
Addition fixes
stunnerparas 8ab6810
Apply php-cs-fixer changes
stunnerparas c4bd9ea
Addition fixes
stunnerparas 73f9b14
Apply php-cs-fixer changes
stunnerparas 8654501
Addition fixes
stunnerparas 8eefb6a
pimcore user and finalize manager service
stunnerparas 0e2edf3
Apply php-cs-fixer changes
stunnerparas dd3a387
Merge branch '1.x' into 840_gdpr
stunnerparas 51322c1
Additional changes and improvements
stunnerparas 0343841
Merge branch '1.x' into 840_gdpr
stunnerparas db0a05c
changes
stunnerparas 4b8c6ce
Apply php-cs-fixer changes
stunnerparas 7cf7a93
sonar fixes
stunnerparas 107468a
type fix docs
stunnerparas 2028712
Apply php-cs-fixer changes
stunnerparas 68712e5
Re update Json event, Handle json error, Parameter, schema, event han…
stunnerparas 26d9554
Apply php-cs-fixer changes
stunnerparas 5e5176e
Minor Changes.
martineiber 4a0bb7e
Apply php-cs-fixer changes
martineiber 0bc6f6c
Additional imporovements and new features added in json download
stunnerparas a7ddb19
Apply php-cs-fixer changes
stunnerparas 7b118c7
fixed sonar cloud issue
stunnerparas 3aff281
fix sonar cloud issue
stunnerparas e23d98f
changes and ,sonar fix
stunnerparas 56486c4
Apply php-cs-fixer changes
stunnerparas 0f3a394
set the user that is non deletable
stunnerparas 8e65bed
update docs
stunnerparas 95008c0
Apply php-cs-fixer changes
stunnerparas 2e5e175
Minor Changes.
martineiber 9842536
update docs
stunnerparas 7cd6879
Merge branch '1.x' into 840_gdpr
stunnerparas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| services: | ||
| _defaults: | ||
| autowire: true | ||
| autoconfigure: true | ||
| public: false | ||
|
|
||
| # controllers are imported separately to make sure they're public | ||
| # and have a tag that allows actions to type-hint services | ||
| Pimcore\Bundle\StudioBackendBundle\Gdpr\Controller\: | ||
| resource: "../src/Gdpr/Controller/*" | ||
| public: true | ||
| tags: ["controller.service_arguments"] | ||
|
|
||
| # --- GDPR Service Layer --- | ||
|
|
||
| Pimcore\Bundle\StudioBackendBundle\Gdpr\Service\GdprManagerServiceInterface: | ||
| class: Pimcore\Bundle\StudioBackendBundle\Gdpr\Service\GdprManagerService | ||
|
|
||
| Pimcore\Bundle\StudioBackendBundle\Gdpr\Service\DataProviderLoaderInterface: | ||
| class: Pimcore\Bundle\StudioBackendBundle\Gdpr\Service\Loader\TaggedIteratorDataProviderLoader | ||
|
|
||
| # --- GDPR Providers --- | ||
| Pimcore\Bundle\StudioBackendBundle\Gdpr\Provider\PimcoreUserProvider: | ||
| tags: ["pimcore.studio_backend.gdpr_data_provider"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| # Extending GDPR Data Providers | ||
|
|
||
| The GDPR Data Provider system provides a centralized interface to find and export personal data from any part of your Pimcore application. You can add new data sources (like Data Objects, Assets, Users, or any custom entity) by creating your own provider. | ||
|
|
||
| New providers are created by implementing the `Pimcore\Bundle\StudioBackendBundle\Gdpr\Provider\DataProviderInterface` and tagging your class as a service with `pimcore.studio_backend.gdpr_data_provider` in gdpr.yaml. | ||
|
|
||
| If you're using the default service configuration, simply placing your class in the `src/Gdpr/Provider/` directory is all you need for it to be registered. | ||
|
|
||
| ## How does it work | ||
|
|
||
| The `GdprManagerService` acts as the central coordinator for all registered providers. It automatically discovers your tagged service. | ||
stunnerparas marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| ### 🔎 For Searching | ||
martineiber marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 1. The manager loads all tagged providers to build the search interface. It calls your provider's `getName()`, `getKey()`, `getSortPriority()`, and `getAvailableColumns()` methods. | ||
| 2. When a user performs a search, the manager first checks `getRequiredPermission()` to see if the current user is allowed to use your provider. | ||
| 3. If permitted, the manager calls your provider's `findData()` method, passing the user's search terms. The results are then displayed in the grid. | ||
|
|
||
| ### For Exporting (Direct Download) | ||
|
|
||
| The export process is a "direct download" flow. | ||
|
|
||
| 1. **Request:** The user makes a `GET` request to the export endpoint, specifying the item `id` in the URL and the `providerKey` as a query parameter. | ||
| `GET /pimcore-studio/api/gdpr/export-data/1?providerKey=pimcore_users` | ||
| 2. **Logic:** The `GdprManagerService` resolves the one provider specified (`pimcore_users`). | ||
| 3. **Permission Check:** It calls your provider's `getRequiredPermission()` to check if the user is allowed. | ||
| 4. **Data Retrieval:** If permitted, the manager calls your provider's `getSingleItemForDownload(1)` method. | ||
| 5. **Response:** Your provider returns the raw data (like a DataObject or an array). The `GdprManagerService` automatically serializes this data into a downloadable JSON file, a "Save As..." dialog in the user's browser. | ||
stunnerparas marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| --- | ||
|
|
||
| ## Example Data Provider | ||
|
|
||
| Here is an example of a provider that supports both searching and direct exporting for **Customer** data objects. | ||
|
|
||
| ```php | ||
| <?php | ||
| declare(strict_types=1); | ||
|
|
||
| namespace App\Gdpr\Provider; | ||
|
|
||
| // 1. Import all required classes | ||
| use Pimcore\Bundle\StudioBackendBundle\Exception\Api\ForbiddenException; | ||
| use Pimcore\Bundle\StudioBackendBundle\Exception\Api\NotFoundException; | ||
| use Pimcore\Bundle\StudioBackendBundle\Gdpr\Attribute\Request\SearchTerms; | ||
| use Pimcore\Bundle\StudioBackendBundle\Gdpr\Provider\DataProviderInterface; | ||
| use Pimcore\Bundle\StudioBackendBundle\Gdpr\Schema\GdprDataColumn; | ||
| use Pimcore\Bundle\StudioBackendBundle\Util\Constant\UserPermissions; | ||
| use Pimcore\Model\DataObject; | ||
| // You need to import the class for your DataObject | ||
| use Pimcore\Model\DataObject\Customer; | ||
|
|
||
| // 2. Add the AutoconfigureTag to register the provider | ||
| final class CustomerObjectProvider implements DataProviderInterface | ||
stunnerparas marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| /** | ||
| * You can inject any services you need. | ||
| */ | ||
| public function __construct( | ||
| // e.g. private readonly SecurityService $securityService | ||
| ) { | ||
| } | ||
|
|
||
| /** | ||
| * A unique key for your provider. | ||
| */ | ||
| public function getKey(): string | ||
| { | ||
| return 'customers'; | ||
| } | ||
|
|
||
| /** | ||
| * A human-friendly name shown in the UI. | ||
| */ | ||
| public function getName(): string | ||
| { | ||
| return 'Customer Objects'; | ||
| } | ||
|
|
||
| /** | ||
| * Sort order for the UI. Higher numbers appear first. | ||
| */ | ||
| public function getSortPriority(): int | ||
| { | ||
| return 10; | ||
| } | ||
|
|
||
| /** | ||
| * The general permission needed to use this provider. | ||
| */ | ||
| public function getRequiredPermission(): UserPermissions | ||
| { | ||
| // Users must have 'objects' permission to use this provider | ||
| return UserPermissions::OBJECTS; | ||
| } | ||
|
|
||
| /** | ||
| * Defines the columns for the search result grid. | ||
| * The 'key' must match the key in the array returned by findData(). | ||
| * | ||
| * @return GdprDataColumn[] | ||
| */ | ||
| public function getAvailableColumns(): array | ||
| { | ||
| return [ | ||
| new GdprDataColumn('id', 'ID'), | ||
| new GdprDataColumn('email', 'Email Address'), | ||
| new GdprDataColumn('path', 'Full Path'), | ||
| ]; | ||
| } | ||
|
|
||
| /** | ||
| * The core search logic. | ||
| * | ||
| * @return array<array<string, mixed>> | ||
| */ | ||
| public function findData(?SearchTerms $terms): array | ||
| { | ||
| // Note: $terms can be null | ||
| if ($terms === null || empty($terms->value)) { | ||
| return []; | ||
| } | ||
|
|
||
| $listing = new DataObject\Customer\Listing(); | ||
| $listing->setCondition('email LIKE ?', ['%' . $terms->value . '%']); | ||
| $listing->load(); | ||
|
|
||
| $results = []; | ||
| foreach ($listing as $customer) { | ||
| // The keys here MUST match the keys in getAvailableColumns() | ||
| $results[] = [ | ||
| 'id' => $customer->getId(), | ||
| 'email' => $customer->getEmail(), | ||
| 'path' => $customer->getFullPath(), | ||
| ]; | ||
| } | ||
|
|
||
| return $results; | ||
| } | ||
|
|
||
| /** | ||
| * Fetches a single item's data for export. | ||
| * The returned data (array or object) will be serialized by the manager. | ||
| * | ||
| * @param int $id The ID of the item to fetch | ||
| * @return array|object The data to be serialized | ||
| * | ||
| * @throws NotFoundException | ||
| * @throws ForbiddenException | ||
| */ | ||
| public function getSingleItemForDownload(int $id): array|object | ||
| { | ||
| // 1. Find the item | ||
| $customer = Customer::getById($id); | ||
|
|
||
| if ($customer === null) { | ||
| throw new NotFoundException('Customer', $id); | ||
| } | ||
|
|
||
| // 2. (Optional) Check for specific permissions | ||
| // if ($this->securityService->isAllowedToSee($customer) === false) { | ||
| // throw new ForbiddenException('You are not allowed to export this item.'); | ||
| // } | ||
|
|
||
| // 3. Return the data. | ||
| // The GdprManagerService will receive this and must serialize it. | ||
| return $customer; | ||
|
|
||
| } | ||
| } | ||
|
|
||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| <?php | ||
| declare(strict_types=1); | ||
|
|
||
| /** | ||
| * This source file is available under the terms of the | ||
| * Pimcore Open Core License (POCL) | ||
| * Full copyright and license information is available in | ||
| * LICENSE.md which is distributed with this source code. | ||
| * | ||
| * @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com) | ||
| * @license Pimcore Open Core License (POCL) | ||
| */ | ||
|
|
||
| namespace Pimcore\Bundle\StudioBackendBundle\DependencyInjection\CompilerPass; | ||
|
|
||
| use Pimcore\Bundle\StudioBackendBundle\Exception\MustImplementInterfaceException; | ||
| use Pimcore\Bundle\StudioBackendBundle\Gdpr\Provider\DataProviderInterface; | ||
| use Pimcore\Bundle\StudioBackendBundle\Gdpr\Service\Loader\TaggedIteratorDataProviderLoader; | ||
| use Pimcore\Bundle\StudioBackendBundle\Util\Trait\MustImplementInterfaceTrait; | ||
| use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; | ||
| use Symfony\Component\DependencyInjection\ContainerBuilder; | ||
|
|
||
| /** | ||
| * @internal | ||
| */ | ||
| final readonly class DataProviderPass implements CompilerPassInterface | ||
| { | ||
| use MustImplementInterfaceTrait; | ||
|
|
||
| /** | ||
| * @throws MustImplementInterfaceException | ||
| */ | ||
| public function process(ContainerBuilder $container): void | ||
| { | ||
| $taggedServices = array_keys( | ||
| [ | ||
| ... $container->findTaggedServiceIds(TaggedIteratorDataProviderLoader::DATA_PROVIDER_TAG), | ||
|
|
||
| ] | ||
| ); | ||
|
|
||
| foreach ($taggedServices as $environmentType) { | ||
| $this->checkInterface($environmentType, DataProviderInterface::class); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| <?php | ||
| declare(strict_types=1); | ||
|
|
||
| /** | ||
| * This source file is available under the terms of the | ||
| * Pimcore Open Core License (POCL) | ||
| * Full copyright and license information is available in | ||
| * LICENSE.md which is distributed with this source code. | ||
| * | ||
| * @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com) | ||
| * @license Pimcore Open Core License (POCL) | ||
| */ | ||
|
|
||
| namespace Pimcore\Bundle\StudioBackendBundle\Gdpr\Attribute\Request; | ||
|
|
||
| use Attribute; | ||
| use OpenApi\Attributes\JsonContent; | ||
| use OpenApi\Attributes\Property; | ||
| use OpenApi\Attributes\RequestBody; | ||
|
|
||
| /** | ||
| * @internal | ||
| */ | ||
| #[Attribute(Attribute::TARGET_METHOD)] | ||
| final class GdprRequestBody extends RequestBody | ||
| { | ||
| public function __construct() | ||
| { | ||
| parent::__construct( | ||
| required: true, | ||
| content: new JsonContent( | ||
| required: ['providerName'], | ||
| properties: [ | ||
| new Property( | ||
| property: 'providerName', | ||
| description: 'The key of the single provider to search (e.g., pimcore_user)', | ||
| type: 'string', | ||
| example: 'pimcore_user' | ||
| ), | ||
| ], | ||
| type: 'object', | ||
| ), | ||
| ); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| <?php | ||
| declare(strict_types=1); | ||
|
|
||
| /** | ||
| * This source file is available under the terms of the | ||
| * Pimcore Open Core License (POCL) | ||
| * Full copyright and license information is available in | ||
| * LICENSE.md which is distributed with this source code. | ||
| * | ||
| * @copyright Copyright (c) Pimcore GmbH (https://www.pimcore.com) | ||
| * @license Pimcore Open Core License (POCL) | ||
| */ | ||
|
|
||
| namespace Pimcore\Bundle\StudioBackendBundle\Gdpr\Attribute\Request; | ||
|
|
||
| use OpenApi\Attributes\Property; | ||
| use OpenApi\Attributes\Schema; | ||
| use Symfony\Component\Validator\Constraints\Email; | ||
| use Symfony\Component\Validator\Constraints\Type; | ||
|
|
||
| /** | ||
| * @internal | ||
| */ | ||
| #[Schema( | ||
| title: 'GDPR Search Terms', | ||
| description: 'Object containing the values to search for. All fields are optional.', | ||
| type: 'object' | ||
| )] | ||
| final readonly class SearchTerms | ||
| { | ||
| public function __construct( | ||
| #[Property(description: 'The ID to search for.', type: 'string', nullable: true)] | ||
| #[Type('string')] | ||
| public ?string $id = null, | ||
|
|
||
| #[Property(description: 'The first name to search for.', type: 'string', nullable: true)] | ||
| #[Type('string')] | ||
| public ?string $firstname = null, | ||
|
|
||
| #[Property(description: 'The last name to search for.', type: 'string', nullable: true)] | ||
| #[Type('string')] | ||
| public ?string $lastname = null, | ||
|
|
||
| #[Property(description: 'The email address to search for.', type: 'string', nullable: true)] | ||
| #[Type('string')]//why is #[Email] constraint causing issues | ||
| public ?string $email = null, | ||
| ) { | ||
| } | ||
|
|
||
| public function getId(): ?string | ||
| { | ||
| return $this->id; | ||
| } | ||
|
|
||
| public function getFirstname(): ?string | ||
| { | ||
| return $this->firstname; | ||
| } | ||
|
|
||
| public function getLastname(): ?string | ||
| { | ||
| return $this->lastname; | ||
| } | ||
|
|
||
| public function getEmail(): ?string | ||
| { | ||
| return $this->email; | ||
| } | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.