Implement permission checks.

This commit is contained in:
David Molineus
2018-12-05 14:05:01 +01:00
parent f2959b2df0
commit fa1ceb9402
8 changed files with 508 additions and 55 deletions

View File

@@ -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:

View File

@@ -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'],
],
@@ -94,17 +95,20 @@ $GLOBALS['TL_DCA']['tl_leaflet_layer'] = [
'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',
'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'],
@@ -112,6 +116,7 @@ $GLOBALS['TL_DCA']['tl_leaflet_layer'] = [
'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' => [

View File

@@ -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' => [
@@ -49,12 +52,14 @@ $GLOBALS['TL_DCA']['tl_leaflet_marker'] = [
'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();"',
'button_callback' => [OperationsListener::class, 'popupOperation'],
],
'all' => [
'label' => &$GLOBALS['TL_LANG']['MSC']['all'],

View File

@@ -10,6 +10,8 @@
* @filesource
*/
use Netzmacht\Contao\Leaflet\Listener\Dca\OperationsListener;
$GLOBALS['TL_DCA']['tl_leaflet_vector'] = [
'config' => [
'dataContainer' => 'Table',
@@ -49,12 +51,14 @@ $GLOBALS['TL_DCA']['tl_leaflet_vector'] = [
'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();"',
'button_callback' => [OperationsListener::class, 'popupOperation'],
],
'all' => [
'label' => &$GLOBALS['TL_LANG']['MSC']['all'],

View File

@@ -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,6 +115,13 @@ class LayerDcaListener extends AbstractListener
*/
private $backendAdapter;
/**
* State button callback listener.
*
* @var StateButtonCallbackListener
*/
private $stateButtonCallbackListener;
/**
* Construct.
*
@@ -112,6 +130,8 @@ class LayerDcaListener extends AbstractListener
* @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.
@@ -126,6 +146,8 @@ class LayerDcaListener extends AbstractListener
RepositoryManager $repositoryManager,
Translator $translator,
LayerLabelRenderer $labelRenderer,
BackendUser $backendUser,
StateButtonCallbackListener $stateButtonCallbackListener,
$backendAdapter,
array $layers,
array $tileProviders,
@@ -143,6 +165,46 @@ class LayerDcaListener extends AbstractListener
$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;
}
}
}

View File

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

View File

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

View File

@@ -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.
*