From fa1ceb94022c918cca76a69f6ae6c6d58b4d4c14 Mon Sep 17 00:00:00 2001 From: David Molineus Date: Wed, 5 Dec 2018 14:05:01 +0100 Subject: [PATCH] Implement permission checks. --- src/Bundle/Resources/config/listeners.yml | 4 + .../Resources/contao/dca/tl_leaflet_layer.php | 35 +- .../contao/dca/tl_leaflet_marker.php | 21 +- .../contao/dca/tl_leaflet_vector.php | 20 +- src/Listener/Dca/LayerDcaListener.php | 330 ++++++++++++++++-- src/Listener/Dca/MarkerDcaListener.php | 70 +++- src/Listener/Dca/VectorDcaListener.php | 75 +++- src/Model/LayerModel.php | 8 + 8 files changed, 508 insertions(+), 55 deletions(-) diff --git a/src/Bundle/Resources/config/listeners.yml b/src/Bundle/Resources/config/listeners.yml index b9432b0..dfc5d00 100644 --- a/src/Bundle/Resources/config/listeners.yml +++ b/src/Bundle/Resources/config/listeners.yml @@ -51,6 +51,8 @@ services: - '@netzmacht.contao_toolkit.repository_manager' - '@translator' - '@netzmacht.contao_leaflet.layer_label_renderer' + - '@netzmacht.contao_toolkit.contao.backend_user' + - '@netzmacht.contao_toolkit.dca.listeners.state_button_callback' - '@netzmacht.contao_toolkit.contao.backend_adapter' - '%netzmacht.contao_leaflet.layers%' - '%netzmacht.contao_leaflet.providers%' @@ -63,6 +65,7 @@ services: arguments: - '@database_connection' - '@netzmacht.contao_toolkit.repository_manager' + - '@netzmacht.contao_toolkit.contao.backend_user' netzmacht.contao_leaflet.listeners.dca.vector: class: Netzmacht\Contao\Leaflet\Listener\Dca\VectorDcaListener @@ -70,6 +73,7 @@ services: arguments: - '@netzmacht.contao_toolkit.dca.manager' - '@netzmacht.contao_toolkit.repository_manager' + - '@netzmacht.contao_toolkit.contao.backend_user' - '%netzmacht.contao_leaflet.vectors%' netzmacht.contao_leaflet.listeners.dca.icon: diff --git a/src/Bundle/Resources/contao/dca/tl_leaflet_layer.php b/src/Bundle/Resources/contao/dca/tl_leaflet_layer.php index ef0cef7..d3de89c 100644 --- a/src/Bundle/Resources/contao/dca/tl_leaflet_layer.php +++ b/src/Bundle/Resources/contao/dca/tl_leaflet_layer.php @@ -17,9 +17,6 @@ $GLOBALS['TL_DCA']['tl_leaflet_layer'] = [ 'dataContainer' => 'Table', 'enableVersioning' => true, 'ctable' => ['tl_leaflet_vector', 'tl_leaflet_marker'], - 'ondelete_callback' => [ - ['netzmacht.contao_leaflet.listeners.dca.layer', 'deleteRelations'], - ], 'sql' => [ 'keys' => [ 'id' => 'primary', @@ -28,8 +25,12 @@ $GLOBALS['TL_DCA']['tl_leaflet_layer'] = [ ], ], 'onload_callback' => [ + ['netzmacht.contao_leaflet.listeners.dca.layer', 'checkPermissions'], ['netzmacht.contao_leaflet.listeners.dca.leaflet', 'loadLanguageFile'], ], + 'ondelete_callback' => [ + ['netzmacht.contao_leaflet.listeners.dca.layer', 'deleteRelations'], + ], 'onsubmit_callback' => [ ['netzmacht.contao_leaflet.listeners.dca.leaflet', 'clearCache'], ], @@ -91,27 +92,31 @@ $GLOBALS['TL_DCA']['tl_leaflet_layer'] = [ 'button_callback' => ['netzmacht.contao_leaflet.listeners.dca.layer', 'generateVectorsButton'], ], 'edit' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_layer']['edit'], - 'href' => 'act=edit', - 'icon' => 'header.gif', + 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_layer']['edit'], + 'href' => 'act=edit', + 'icon' => 'header.gif', + 'button_callback' => ['netzmacht.contao_leaflet.listeners.dca.layer', 'generateEditButton'], ], 'copy' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_layer']['copy'], - 'href' => 'act=copy', - 'icon' => 'copy.gif', + 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_layer']['copy'], + 'href' => 'act=copy', + 'icon' => 'copy.gif', + 'button_callback' => ['netzmacht.contao_leaflet.listeners.dca.layer', 'generateCopyButton'], ], 'cut' => [ 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_layer']['cut'], 'href' => 'act=paste&mode=cut', 'icon' => 'cut.gif', 'attributes' => 'onclick="Backend.getScrollOffset()"', + 'button_callback' => ['netzmacht.contao_leaflet.listeners.dca.layer', 'generateEditButton'], ], 'delete' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_layer']['delete'], - 'href' => 'act=delete', - 'icon' => 'delete.gif', - 'attributes' => 'onclick="if(!confirm(\'' . $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] + 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_layer']['delete'], + 'href' => 'act=delete', + 'icon' => 'delete.gif', + 'attributes' => 'onclick="if(!confirm(\'' . $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] . '\'))return false;Backend.getScrollOffset()"', + 'button_callback' => ['netzmacht.contao_leaflet.listeners.dca.layer', 'generateDeleteButton'], ], 'toggle' => [ 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_layer']['toggle'], @@ -119,8 +124,8 @@ $GLOBALS['TL_DCA']['tl_leaflet_layer'] = [ 'attributes' => 'onclick="Backend.getScrollOffset(); return ContaoLeafletAjaxRequest.toggleVisibility(this,%s)"', 'button_callback' => [ - 'netzmacht.contao_toolkit.dca.listeners.state_button_callback', - 'handleButtonCallback', + 'netzmacht.contao_leaflet.listeners.dca.layer', + 'generateToggleButton', ], 'toolkit' => [ 'state_button' => [ diff --git a/src/Bundle/Resources/contao/dca/tl_leaflet_marker.php b/src/Bundle/Resources/contao/dca/tl_leaflet_marker.php index 0b24991..60e5224 100644 --- a/src/Bundle/Resources/contao/dca/tl_leaflet_marker.php +++ b/src/Bundle/Resources/contao/dca/tl_leaflet_marker.php @@ -10,6 +10,8 @@ * @filesource */ +use Netzmacht\Contao\Leaflet\Listener\Dca\OperationsListener; + $GLOBALS['TL_DCA']['tl_leaflet_marker'] = [ 'config' => [ 'dataContainer' => 'Table', @@ -23,6 +25,7 @@ $GLOBALS['TL_DCA']['tl_leaflet_marker'] = [ ], ], 'onload_callback' => [ + ['netzmacht.contao_leaflet.listeners.dca.marker', 'checkPermissions'], ['netzmacht.contao_leaflet.listeners.dca.leaflet', 'loadLanguageFile'], ], 'onsubmit_callback' => [ @@ -45,16 +48,18 @@ $GLOBALS['TL_DCA']['tl_leaflet_marker'] = [ ], 'global_operations' => [ 'icons' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_marker']['icons'], - 'href' => 'table=tl_leaflet_icon&id=', - 'icon' => 'bundles/netzmachtcontaoleaflet/img/icons.png', - 'attributes' => 'onclick="Backend.getScrollOffset();" accesskey="e"', + 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_marker']['icons'], + 'href' => 'table=tl_leaflet_icon&id=', + 'icon' => 'bundles/netzmachtcontaoleaflet/img/icons.png', + 'attributes' => 'onclick="Backend.getScrollOffset();" accesskey="e"', + 'button_callback' => [OperationsListener::class, 'iconOperation'], ], 'popups' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_marker']['popups'], - 'href' => 'table=tl_leaflet_popup', - 'icon' => 'bundles/netzmachtcontaoleaflet/img/popup.png', - 'attributes' => 'onclick="Backend.getScrollOffset();"', + 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_marker']['popups'], + 'href' => 'table=tl_leaflet_popup', + 'icon' => 'bundles/netzmachtcontaoleaflet/img/popup.png', + 'attributes' => 'onclick="Backend.getScrollOffset();"', + 'button_callback' => [OperationsListener::class, 'popupOperation'], ], 'all' => [ 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], diff --git a/src/Bundle/Resources/contao/dca/tl_leaflet_vector.php b/src/Bundle/Resources/contao/dca/tl_leaflet_vector.php index 4e2fe66..0c4556b 100644 --- a/src/Bundle/Resources/contao/dca/tl_leaflet_vector.php +++ b/src/Bundle/Resources/contao/dca/tl_leaflet_vector.php @@ -10,6 +10,8 @@ * @filesource */ +use Netzmacht\Contao\Leaflet\Listener\Dca\OperationsListener; + $GLOBALS['TL_DCA']['tl_leaflet_vector'] = [ 'config' => [ 'dataContainer' => 'Table', @@ -45,16 +47,18 @@ $GLOBALS['TL_DCA']['tl_leaflet_vector'] = [ ], 'global_operations' => [ 'styles' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_vector']['styles'], - 'href' => 'table=tl_leaflet_style', - 'icon' => 'bundles/netzmachtcontaoleaflet/img/style.png', - 'attributes' => 'onclick="Backend.getScrollOffset();"', + 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_vector']['styles'], + 'href' => 'table=tl_leaflet_style', + 'icon' => 'bundles/netzmachtcontaoleaflet/img/style.png', + 'attributes' => 'onclick="Backend.getScrollOffset();"', + 'button_callback' => [OperationsListener::class, 'styleOperation'], ], 'popups' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_vector']['popups'], - 'href' => 'table=tl_leaflet_popup', - 'icon' => 'bundles/netzmachtcontaoleaflet/img/popup.png', - 'attributes' => 'onclick="Backend.getScrollOffset();"', + 'label' => &$GLOBALS['TL_LANG']['tl_leaflet_vector']['popups'], + 'href' => 'table=tl_leaflet_popup', + 'icon' => 'bundles/netzmachtcontaoleaflet/img/popup.png', + 'attributes' => 'onclick="Backend.getScrollOffset();"', + 'button_callback' => [OperationsListener::class, 'popupOperation'], ], 'all' => [ 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], diff --git a/src/Listener/Dca/LayerDcaListener.php b/src/Listener/Dca/LayerDcaListener.php index 120de1e..bf330ac 100644 --- a/src/Listener/Dca/LayerDcaListener.php +++ b/src/Listener/Dca/LayerDcaListener.php @@ -13,9 +13,12 @@ namespace Netzmacht\Contao\Leaflet\Listener\Dca; use Contao\Backend; +use Contao\BackendUser; +use Contao\CoreBundle\Exception\AccessDeniedException; use Contao\CoreBundle\Framework\Adapter; use Contao\DataContainer; use Contao\Image; +use Contao\Input; use Contao\StringUtil; use Doctrine\DBAL\Connection; use Netzmacht\Contao\Leaflet\Backend\Renderer\Label\Layer\LayerLabelRenderer; @@ -23,6 +26,7 @@ use Netzmacht\Contao\Leaflet\Model\IconModel; use Netzmacht\Contao\Leaflet\Model\LayerModel; use Netzmacht\Contao\Toolkit\Data\Model\RepositoryManager; use Netzmacht\Contao\Toolkit\Dca\Listener\AbstractListener; +use Netzmacht\Contao\Toolkit\Dca\Listener\Button\StateButtonCallbackListener; use Netzmacht\Contao\Toolkit\Dca\Manager; use Netzmacht\Contao\Toolkit\Dca\Options\OptionsBuilder; use Symfony\Component\Translation\TranslatorInterface as Translator; @@ -30,7 +34,7 @@ use Symfony\Component\Translation\TranslatorInterface as Translator; /** * Class Layer is the helper class for the tl_leaflet_layer dca. * - * @package Netzmacht\Contao\Leaflet\Dca + * @SuppressWarnings(PHPMD.TooManyPublicMethods) */ class LayerDcaListener extends AbstractListener { @@ -76,6 +80,13 @@ class LayerDcaListener extends AbstractListener */ private $translator; + /** + * Backend user. + * + * @var BackendUser + */ + private $backendUser; + /** * OSM amenities. * @@ -104,19 +115,28 @@ class LayerDcaListener extends AbstractListener */ private $backendAdapter; + /** + * State button callback listener. + * + * @var StateButtonCallbackListener + */ + private $stateButtonCallbackListener; + /** * Construct. * - * @param Manager $manager Data container manager. - * @param Connection $connection Database connection. - * @param RepositoryManager $repositoryManager Repository manager. - * @param Translator $translator Translator. - * @param LayerLabelRenderer $labelRenderer Layer label renderer. - * @param Adapter|Backend $backendAdapter Backend adapter. - * @param array $layers Leaflet layer configuration. - * @param array $tileProviders Tile providers. - * @param array $amenities OSM amenities. - * @param array $fileFormats File formats. + * @param Manager $manager Data container manager. + * @param Connection $connection Database connection. + * @param RepositoryManager $repositoryManager Repository manager. + * @param Translator $translator Translator. + * @param LayerLabelRenderer $labelRenderer Layer label renderer. + * @param BackendUser $backendUser Backend user. + * @param StateButtonCallbackListener $stateButtonCallbackListener State button callback listener. + * @param Adapter|Backend $backendAdapter Backend adapter. + * @param array $layers Leaflet layer configuration. + * @param array $tileProviders Tile providers. + * @param array $amenities OSM amenities. + * @param array $fileFormats File formats. * * @SuppressWarnings(PHPMD.ExcessiveParameterList) */ @@ -126,6 +146,8 @@ class LayerDcaListener extends AbstractListener RepositoryManager $repositoryManager, Translator $translator, LayerLabelRenderer $labelRenderer, + BackendUser $backendUser, + StateButtonCallbackListener $stateButtonCallbackListener, $backendAdapter, array $layers, array $tileProviders, @@ -134,15 +156,55 @@ class LayerDcaListener extends AbstractListener ) { parent::__construct($manager); - $this->connection = $connection; - $this->layers = $layers; - $this->tileProviders = $tileProviders; - $this->translator = $translator; - $this->amenities = $amenities; - $this->labelRenderer = $labelRenderer; - $this->fileFormats = $fileFormats; - $this->repositoryManager = $repositoryManager; - $this->backendAdapter = $backendAdapter; + $this->connection = $connection; + $this->layers = $layers; + $this->tileProviders = $tileProviders; + $this->translator = $translator; + $this->amenities = $amenities; + $this->labelRenderer = $labelRenderer; + $this->fileFormats = $fileFormats; + $this->repositoryManager = $repositoryManager; + $this->backendAdapter = $backendAdapter; + $this->backendUser = $backendUser; + $this->stateButtonCallbackListener = $stateButtonCallbackListener; + } + + /** + * Check the permissions. + * + * @param DataContainer $dataContainer Data container. + * + * @return void + * + * @throws AccessDeniedException If permissions are not granted. + */ + public function checkPermissions(DataContainer $dataContainer): void + { + if ($this->backendUser->isAdmin) { + return; + } + + $action = Input::get('act'); + $permission = $this->determinePermission($action); + + if ($permission && !$this->backendUser->hasAccess($permission, 'leaflet_layer_permissions')) { + throw new AccessDeniedException( + sprintf('Permission "%s" not granted to access layer "%s"', $permission, $dataContainer->id) + ); + } + + $this->getDefinition()->set(['list', 'sorting', 'root'], $this->backendUser->leaflet_layers); + + $layerId = $this->determineLayerId(); + if ($layerId && !$this->backendUser->hasAccess($layerId, 'leaflet_layers')) { + throw new AccessDeniedException( + sprintf('User "%s" not allowed to access layer "%s"', $this->backendUser->id, $layerId) + ); + } + + if (!$this->backendUser->hasAccess(LayerModel::PERMISSION_CREATE, 'leaflet_layer_permissions')) { + $this->getDefinition()->set(['config', 'closed'], true); + } } /** @@ -306,6 +368,148 @@ class LayerDcaListener extends AbstractListener return $buffer; } + /** + * Generate the edit button. + * + * @param array $row Current row. + * @param string $href The button href. + * @param string $label The button label. + * @param string $title The button title. + * @param string $icon The button icon. + * @param string $attributes Optional attributes. + * + * @return string + */ + public function generateEditButton($row, $href, $label, $title, $icon, $attributes): string + { + if ($this->backendUser->hasAccess('edit', 'leaflet_layer_permissions')) { + return $this->generateButton($row, $href, $label, $title, $icon, $attributes); + } + + return ''; + } + + /** + * Generate the cut button. + * + * @param array $row Current row. + * @param string $href The button href. + * @param string $label The button label. + * @param string $title The button title. + * @param string $icon The button icon. + * @param string $attributes Optional attributes. + * + * @return string + */ + public function generateCutButton($row, $href, $label, $title, $icon, $attributes): string + { + if ($this->backendUser->hasAccess('edit', 'leaflet_layer_permissions')) { + return $this->generateButton($row, $href, $label, $title, $icon, $attributes); + } + + return ''; + } + + /** + * Generate the copy button. + * + * @param array $row Current row. + * @param string $href The button href. + * @param string $label The button label. + * @param string $title The button title. + * @param string $icon The button icon. + * @param string $attributes Optional attributes. + * + * @return string + */ + public function generateCopyButton($row, $href, $label, $title, $icon, $attributes): string + { + if ($this->backendUser->hasAccess('create', 'leaflet_layer_permissions')) { + return $this->generateButton($row, $href, $label, $title, $icon, $attributes); + } + + return ''; + } + + /** + * Generate the toggle button. + * + * @param array $row Current data row. + * @param string|null $href Button link. + * @param string|null $label Button label. + * @param string|null $title Button title. + * @param string|null $icon Enabled button icon. + * @param string|null $attributes Html attributes as string. + * @param string $tableName Table name. + * @param array|null $rootIds Root ids. + * @param array|null $childRecordIds Child record ids. + * @param bool $circularReference Circular reference flag. + * @param string|null $previous Previous button name. + * @param string|null $next Next button name. + * @param DataContainer $dataContainer Data container driver. + * + * @return string + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @SuppressWarnings(PHPMD.ExcessiveParameterList) + */ + public function generateToggleButton( + array $row, + $href, + $label, + $title, + $icon, + $attributes, + string $tableName, + $rootIds, + $childRecordIds, + bool $circularReference, + $previous, + $next, + $dataContainer + ): string { + if ($this->backendUser->hasAccess('edit', 'leaflet_layer_permissions')) { + return $this->stateButtonCallbackListener->handleButtonCallback( + $row, + $href, + $label, + $title, + $icon, + $attributes, + $tableName, + $rootIds, + $childRecordIds, + $circularReference, + $previous, + $next, + $dataContainer + ); + } + + return ''; + } + + /** + * Generate the edit button. + * + * @param array $row Current row. + * @param string $href The button href. + * @param string $label The button label. + * @param string $title The button title. + * @param string $icon The button icon. + * @param string $attributes Optional attributes. + * + * @return string + */ + public function generateDeleteButton($row, $href, $label, $title, $icon, $attributes): string + { + if ($this->backendUser->hasAccess('delete', 'leaflet_layer_permissions')) { + return $this->generateButton($row, $href, $label, $title, $icon, $attributes); + } + + return ''; + } + /** * Generate the markers button. * @@ -324,6 +528,10 @@ class LayerDcaListener extends AbstractListener return ''; } + if (!$this->backendUser->hasAccess('data', 'leaflet_layer_permissions')) { + return ''; + } + return $this->generateButton($row, $href, $label, $title, $icon, $attributes); } @@ -345,6 +553,10 @@ class LayerDcaListener extends AbstractListener return ''; } + if (!$this->backendUser->hasAccess('data', 'leaflet_layer_permissions')) { + return ''; + } + return $this->generateButton($row, $href, $label, $title, $icon, $attributes); } @@ -522,4 +734,82 @@ class LayerDcaListener extends AbstractListener Image::getHtml($icon, $label, $attributes) ); } + + /** + * Determine permission for current action. + * + * @param string|null $action Given action. + * + * @return string|null + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + private function determinePermission(?string $action): ?string + { + $permission = null; + + switch ($action) { + case 'edit': + case 'toggle': + case 'editAll': + case 'overrideAll': + case 'cutAll': + return LayerModel::PERMISSION_EDIT; + + case 'delete': + case 'deleteAll': + return LayerModel::PERMISSION_DELETE; + + case 'copyAll': + return LayerModel::PERMISSION_CREATE; + + case 'paste': + $mode = Input::get('mode'); + + switch ($mode) { + case 'create': + return LayerModel::PERMISSION_CREATE; + + case 'cut': + return LayerModel::PERMISSION_EDIT; + + default: + return $permission; + } + + // Comment just to please phpcs + default: + return $permission; + } + } + + + /** + * Determine the layer id. + * + * @return int|null + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + private function determineLayerId(): ?int + { + // Check the current action + switch (Input::get('act')) { + case 'edit': + case 'delete': + case 'paste': + case 'show': + return (int) Input::get('id'); + + case 'editAll': + case 'deleteAll': + case 'overrideAll': + case 'cutAll': + case 'copyAll': + return (int) CURRENT_ID; + + default: + return null; + } + } } diff --git a/src/Listener/Dca/MarkerDcaListener.php b/src/Listener/Dca/MarkerDcaListener.php index 741657c..1a5ef62 100644 --- a/src/Listener/Dca/MarkerDcaListener.php +++ b/src/Listener/Dca/MarkerDcaListener.php @@ -12,10 +12,13 @@ namespace Netzmacht\Contao\Leaflet\Listener\Dca; -use Contao\Controller; +use Contao\BackendUser; +use Contao\CoreBundle\Exception\AccessDeniedException; +use Contao\Input; use Contao\StringUtil; use Doctrine\DBAL\Connection; use Netzmacht\Contao\Leaflet\Model\IconModel; +use Netzmacht\Contao\Leaflet\Model\LayerModel; use Netzmacht\Contao\Leaflet\Model\PopupModel; use Netzmacht\Contao\Toolkit\Data\Model\RepositoryManager; use Netzmacht\Contao\Toolkit\Dca\Options\OptionsBuilder; @@ -41,16 +44,48 @@ class MarkerDcaListener */ private $repositoryManager; + /** + * Backend user. + * + * @var BackendUser + */ + private $backendUser; + /** * MarkerDcaListener constructor. * * @param Connection $connection Database connection. * @param RepositoryManager $repositoryManager Repository manager. + * @param BackendUser $backendUser Backend user. */ - public function __construct(Connection $connection, RepositoryManager $repositoryManager) + public function __construct(Connection $connection, RepositoryManager $repositoryManager, BackendUser $backendUser) { $this->connection = $connection; $this->repositoryManager = $repositoryManager; + $this->backendUser = $backendUser; + } + + /** + * Check permissions. + * + * @return void + * + * @throws AccessDeniedException When permission is not granted. + */ + public function checkPermissions(): void + { + if (!$this->backendUser->hasAccess(LayerModel::PERMISSION_DATA, 'leaflet_layer_permissions')) { + throw new AccessDeniedException( + sprintf('User "%s" not allowed to access layer data.', $this->backendUser->id) + ); + } + + $layerId = $this->determineLayerId(); + if ($layerId && !$this->backendUser->hasAccess($layerId, 'leaflet_layers')) { + throw new AccessDeniedException( + sprintf('User "%s" not allowed to access layer "%s"', $this->backendUser->id, $layerId) + ); + } } /** @@ -165,4 +200,35 @@ class MarkerDcaListener return ''; } + + /** + * Determine the layer id. + * + * @return int|null + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + private function determineLayerId(): ?int + { + // Check the current action + switch (Input::get('act')) { + case 'paste': + return null; + + case '': + case 'create': + case 'select': + return (int) Input::get('id'); + + case 'editAll': + case 'deleteAll': + case 'overrideAll': + case 'cutAll': + case 'copyAll': + return (int) Input::get('pid'); + + default: + return (int) CURRENT_ID; + } + } } diff --git a/src/Listener/Dca/VectorDcaListener.php b/src/Listener/Dca/VectorDcaListener.php index 34f234d..6b10cbf 100644 --- a/src/Listener/Dca/VectorDcaListener.php +++ b/src/Listener/Dca/VectorDcaListener.php @@ -12,6 +12,10 @@ namespace Netzmacht\Contao\Leaflet\Listener\Dca; +use Contao\BackendUser; +use Contao\CoreBundle\Exception\AccessDeniedException; +use Contao\Input; +use Netzmacht\Contao\Leaflet\Model\LayerModel; use Netzmacht\Contao\Leaflet\Model\PopupModel; use Netzmacht\Contao\Leaflet\Model\StyleModel; use Netzmacht\Contao\Toolkit\Data\Model\RepositoryManager; @@ -47,19 +51,55 @@ class VectorDcaListener extends AbstractListener */ private $repositoryManager; + /** + * Backend user. + * + * @var BackendUser + */ + private $backendUser; + /** * Construct. * * @param Manager $dcaManager Data container manager. * @param RepositoryManager $repositoryManager Repository manager. + * @param BackendUser $backendUser Backend user. * @param array $vectors Vectors. */ - public function __construct(Manager $dcaManager, RepositoryManager $repositoryManager, array $vectors) - { + public function __construct( + Manager $dcaManager, + RepositoryManager $repositoryManager, + BackendUser $backendUser, + array $vectors + ) { parent::__construct($dcaManager); $this->vectors = $vectors; $this->repositoryManager = $repositoryManager; + $this->backendUser = $backendUser; + } + + /** + * Check permissions. + * + * @return void + * + * @throws AccessDeniedException When permission is not granted. + */ + public function checkPermissions(): void + { + if (!$this->backendUser->hasAccess(LayerModel::PERMISSION_DATA, 'leaflet_layer_permissions')) { + throw new AccessDeniedException( + sprintf('User "%s" not allowed to access layer data.', $this->backendUser->id) + ); + } + + $layerId = $this->determineLayerId(); + if ($layerId && !$this->backendUser->hasAccess($layerId, 'leaflet_layers')) { + throw new AccessDeniedException( + sprintf('User "%s" not allowed to access layer "%s"', $this->backendUser->id, $layerId) + ); + } } /** @@ -114,4 +154,35 @@ class VectorDcaListener extends AbstractListener return $builder->getOptions(); } + + /** + * Determine the layer id. + * + * @return int|null + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + private function determineLayerId(): ?int + { + // Check the current action + switch (Input::get('act')) { + case 'paste': + return null; + + case '': + case 'create': + case 'select': + return (int) Input::get('id'); + + case 'editAll': + case 'deleteAll': + case 'overrideAll': + case 'cutAll': + case 'copyAll': + return (int) Input::get('pid'); + + default: + return (int) CURRENT_ID; + } + } } diff --git a/src/Model/LayerModel.php b/src/Model/LayerModel.php index 483e125..301c43b 100644 --- a/src/Model/LayerModel.php +++ b/src/Model/LayerModel.php @@ -27,6 +27,14 @@ use Contao\Model\Collection; */ class LayerModel extends AbstractActiveModel { + public const PERMISSION_EDIT = 'edit'; + + public const PERMISSION_CREATE = 'create'; + + public const PERMISSION_DELETE = 'delete'; + + public const PERMISSION_DATA = 'data'; + /** * Model table. *