From 8c5a7ac3e1443585f09d77b803bef58cc6cd2350 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Julien=20Fastr=C3=A9?= Date: Thu, 24 Apr 2025 14:21:51 +0200 Subject: [PATCH] Restrict export filters and aggregators for limited users Added restrictions on export filters and aggregators based on user permissions. Introduced `ExportConfigProcessor` to handle allowed configurations and updated form components to respect these restrictions. Enhanced validation to enforce access control for unauthorized filter editing. --- .../Controller/ExportController.php | 11 +++++ .../Form/Type/Export/AggregatorType.php | 2 + .../Form/Type/Export/ExportType.php | 40 ++++++++++++++++--- .../Form/Type/Export/FilterType.php | 2 + .../config/services/export.yaml | 2 + 5 files changed, 51 insertions(+), 6 deletions(-) diff --git a/src/Bundle/ChillMainBundle/Controller/ExportController.php b/src/Bundle/ChillMainBundle/Controller/ExportController.php index 6075f2f22..3f7cfc186 100644 --- a/src/Bundle/ChillMainBundle/Controller/ExportController.php +++ b/src/Bundle/ChillMainBundle/Controller/ExportController.php @@ -16,6 +16,7 @@ use Chill\MainBundle\Entity\SavedExport; use Chill\MainBundle\Entity\User; use Chill\MainBundle\Export\DirectExportInterface; use Chill\MainBundle\Export\ExportConfigNormalizer; +use Chill\MainBundle\Export\ExportConfigProcessor; use Chill\MainBundle\Export\ExportFormHelper; use Chill\MainBundle\Export\ExportInterface; use Chill\MainBundle\Export\ExportManager; @@ -24,6 +25,7 @@ use Chill\MainBundle\Form\Type\Export\ExportType; use Chill\MainBundle\Form\Type\Export\FormatterType; use Chill\MainBundle\Form\Type\Export\PickCenterType; use Chill\MainBundle\Repository\SavedExportOrExportGenerationRepository; +use Chill\MainBundle\Security\Authorization\ChillExportVoter; use Chill\MainBundle\Security\Authorization\SavedExportVoter; use Doctrine\Common\Collections\Collection; use Doctrine\ORM\EntityManagerInterface; @@ -64,6 +66,7 @@ class ExportController extends AbstractController private readonly ClockInterface $clock, private readonly ExportConfigNormalizer $exportConfigNormalizer, private readonly SavedExportOrExportGenerationRepository $savedExportOrExportGenerationRepository, + private readonly ExportConfigProcessor $exportConfigProcessor, ) { $this->filterStatsByCenters = $parameterBag->get('chill_main')['acl']['filter_stats_by_center']; } @@ -114,11 +117,19 @@ class ExportController extends AbstractController /** @var ExportManager $exportManager */ $exportManager = $this->exportManager; $isGenerate = str_starts_with($step, 'generate_'); + $canEditFull = $this->security->isGranted(ChillExportVoter::COMPOSE_EXPORT); + + if (!$canEditFull && null === $savedExport) { + throw new AccessDeniedHttpException('The user is not allowed to edit all filter, it should edit only SavedExport'); + } $options = match ($step) { 'export', 'generate_export' => [ 'export_alias' => $alias, 'picked_centers' => $this->exportFormHelper->getPickedCenters($data), + 'can_edit_full' => $canEditFull, + 'allowed_filters' => $canEditFull ? null : $this->exportConfigProcessor->retrieveUsedFilters($savedExport->getOptions()['filters']), + 'allowed_aggregators' => $canEditFull ? null : $this->exportConfigProcessor->retrieveUsedAggregators($savedExport->getOptions()['aggregators']), ], 'formatter', 'generate_formatter' => [ 'export_alias' => $alias, diff --git a/src/Bundle/ChillMainBundle/Form/Type/Export/AggregatorType.php b/src/Bundle/ChillMainBundle/Form/Type/Export/AggregatorType.php index 6ee61d945..cc6e583d5 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/Export/AggregatorType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/Export/AggregatorType.php @@ -32,6 +32,7 @@ class AggregatorType extends AbstractType ->add(self::ENABLED_FIELD, CheckboxType::class, [ 'value' => true, 'required' => false, + 'disabled' => $options['disable_enable_field'], ]); $aggregatorFormBuilder = $builder->create('form', FormType::class, [ @@ -55,6 +56,7 @@ class AggregatorType extends AbstractType { $resolver->setRequired('aggregator_alias') ->setRequired('export_manager') + ->setDefault('disable_enable_field', false) ->setDefault('compound', true) ->setDefault('error_bubbling', false); } diff --git a/src/Bundle/ChillMainBundle/Form/Type/Export/ExportType.php b/src/Bundle/ChillMainBundle/Form/Type/Export/ExportType.php index fe4c43830..8f654ced2 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/Export/ExportType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/Export/ExportType.php @@ -35,7 +35,7 @@ class ExportType extends AbstractType public function __construct( private readonly ExportManager $exportManager, private readonly SortExportElement $sortExportElement, - protected ParameterBagInterface $parameterBag, + ParameterBagInterface $parameterBag, ) { $this->personFieldsConfig = $parameterBag->get('chill_person.person_fields'); } @@ -43,6 +43,8 @@ class ExportType extends AbstractType public function buildForm(FormBuilderInterface $builder, array $options) { $export = $this->exportManager->getExport($options['export_alias']); + /** @var bool $canEditFull */ + $canEditFull = $options['can_edit_full']; $exportOptions = [ 'compound' => true, @@ -59,8 +61,18 @@ class ExportType extends AbstractType if ($export instanceof \Chill\MainBundle\Export\ExportInterface) { // add filters - $filters = $this->exportManager->getFiltersApplyingOn($export, $options['picked_centers']); + $filterAliases = $options['allowed_filters']; + $filters = []; + if (is_iterable($filterAliases)) { + foreach ($filterAliases as $alias => $filter) { + $filters[$alias] = $filter; + } + } else { + $filters = $this->exportManager->getFiltersApplyingOn($export, $options['picked_centers']); + } + $this->sortExportElement->sortFilters($filters); + $filterBuilder = $builder->create(self::FILTER_KEY, FormType::class, ['compound' => true]); foreach ($filters as $alias => $filter) { @@ -70,15 +82,26 @@ class ExportType extends AbstractType 'constraints' => [ new ExportElementConstraint(['element' => $filter]), ], + 'disable_enable_field' => !$canEditFull, ]); } $builder->add($filterBuilder); // add aggregators - $aggregators = $this->exportManager - ->getAggregatorsApplyingOn($export, $options['picked_centers']); + $aggregatorsAliases = $options['allowed_aggregators']; + $aggregators = []; + if (is_iterable($aggregatorsAliases)) { + foreach ($aggregatorsAliases as $alias => $aggregator) { + $aggregators[$alias] = $aggregator; + } + } else { + $aggregators = $this->exportManager + ->getAggregatorsApplyingOn($export, $options['picked_centers']); + } + $this->sortExportElement->sortAggregators($aggregators); + $aggregatorBuilder = $builder->create( self::AGGREGATOR_KEY, FormType::class, @@ -96,11 +119,11 @@ class ExportType extends AbstractType } } - $aggregatorBuilder->add($alias, AggregatorType::class, [ 'aggregator_alias' => $alias, 'export_manager' => $this->exportManager, 'label' => $aggregator->getTitle(), + 'disable_enable_field' => !$canEditFull, 'constraints' => [ new ExportElementConstraint(['element' => $aggregator]), ], @@ -125,8 +148,13 @@ class ExportType extends AbstractType public function configureOptions(OptionsResolver $resolver) { - $resolver->setRequired(['export_alias', 'picked_centers']) + $resolver->setRequired(['export_alias', 'picked_centers', 'can_edit_full']) ->setAllowedTypes('export_alias', ['string']) + ->setAllowedValues('can_edit_full', [true, false]) + ->setDefault('allowed_filters', null) + ->setAllowedTypes('allowed_filters', ['iterable', 'null']) + ->setDefault('allowed_aggregators', null) + ->setAllowedTypes('allowed_aggregators', ['iterable', 'null']) ->setDefault('compound', true) ->setDefault('constraints', [ // new \Chill\MainBundle\Validator\Constraints\Export\ExportElementConstraint() diff --git a/src/Bundle/ChillMainBundle/Form/Type/Export/FilterType.php b/src/Bundle/ChillMainBundle/Form/Type/Export/FilterType.php index d4873df83..7e1550878 100644 --- a/src/Bundle/ChillMainBundle/Form/Type/Export/FilterType.php +++ b/src/Bundle/ChillMainBundle/Form/Type/Export/FilterType.php @@ -34,6 +34,7 @@ class FilterType extends AbstractType ->add(self::ENABLED_FIELD, CheckboxType::class, [ 'value' => true, 'required' => false, + 'disabled' => $options['disable_enable_field'], ]); $filterFormBuilder = $builder->create('form', FormType::class, [ @@ -58,6 +59,7 @@ class FilterType extends AbstractType $resolver ->setRequired('filter') ->setAllowedTypes('filter', [FilterInterface::class]) + ->setDefault('disable_enable_field', false) ->setDefault('compound', true) ->setDefault('error_bubbling', false); } diff --git a/src/Bundle/ChillMainBundle/config/services/export.yaml b/src/Bundle/ChillMainBundle/config/services/export.yaml index 2bda5a191..acbb53f8a 100644 --- a/src/Bundle/ChillMainBundle/config/services/export.yaml +++ b/src/Bundle/ChillMainBundle/config/services/export.yaml @@ -16,6 +16,8 @@ services: Chill\MainBundle\Export\ExportConfigNormalizer: ~ + Chill\MainBundle\Export\ExportConfigProcessor: ~ + chill.main.export_element_validator: class: Chill\MainBundle\Validator\Constraints\Export\ExportElementConstraintValidator tags: