[FEATURE] Restrict visibility of Category for a BE User 18/24618/3
authorFabien Udriot <fabien.udriot@ecodev.ch>
Sat, 12 Oct 2013 10:34:53 +0000 (12:34 +0200)
committerSteffen Ritter <info@rs-websystems.de>
Tue, 15 Oct 2013 06:54:31 +0000 (08:54 +0200)
The change set is a first step for improving the Category
restriction for a BE User.

The patch brings an additional field in BE User Group / User
where category mount points can be defined.

A security aspect is also introduced for controlling what
mount points are going to be displayed in the tree
widget in TCEforms.

Security check related to Data Handler will be provided in
another patch.

Change-Id: Ie817e03f21ecbc8423e4d4c2c081bb99324be2d2
Releases: 6.2
Resolves: #52718
Reviewed-on: https://review.typo3.org/24618
Reviewed-by: Steffen Ritter
Tested-by: Steffen Ritter
typo3/sysext/backend/Classes/Security/CategoryPermissionsAspect.php [new file with mode: 0644]
typo3/sysext/backend/ext_localconf.php [new file with mode: 0644]
typo3/sysext/core/Classes/Authentication/BackendUserAuthentication.php
typo3/sysext/core/Classes/Tree/TableConfiguration/DatabaseTreeDataProvider.php
typo3/sysext/core/Configuration/TCA/be_groups.php
typo3/sysext/core/Configuration/TCA/be_users.php
typo3/sysext/core/ext_tables.sql
typo3/sysext/lang/locallang_tca.xlf

diff --git a/typo3/sysext/backend/Classes/Security/CategoryPermissionsAspect.php b/typo3/sysext/backend/Classes/Security/CategoryPermissionsAspect.php
new file mode 100644 (file)
index 0000000..47eb52f
--- /dev/null
@@ -0,0 +1,134 @@
+<?php
+namespace TYPO3\CMS\Backend\Security;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Fabien Udriot <fabien.udriot@typo3.org>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Backend\Tree\TreeNode;
+use TYPO3\CMS\Backend\Tree\TreeNodeCollection;
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeDataProvider;
+
+/**
+ * We do not have AOP in TYPO3 for now, thus the aspect which
+ * deals with tree data security is a slot which reacts on a signal
+ * on data data object initialization.
+ *
+ * The aspect define category mount points according to BE User permissions.
+ */
+class CategoryPermissionsAspect {
+
+       /**
+        * @var string
+        */
+       protected $categoryTableName = 'sys_category';
+
+       /**
+        * @var BackendUserAuthentication
+        */
+       protected $backendUserAuthentication;
+
+       /**
+        * @param BackendUserAuthentication|null $backendUserAuthentication
+        */
+       public function __construct($backendUserAuthentication = NULL) {
+               $this->backendUserAuthentication = $backendUserAuthentication ?: $GLOBALS['BE_USER'];
+       }
+
+       /**
+        * The slot for the signal in DatabaseTreeDataProvider.
+        *
+        * @param DatabaseTreeDataProvider $dataProvider
+        * @param TreeNode $treeData
+        * @return void
+        */
+       public function addUserPermissionsToCategoryTreeData(DatabaseTreeDataProvider $dataProvider, $treeData) {
+
+               if (!$this->backendUserAuthentication->isAdmin() && $dataProvider->getTableName() === $this->categoryTableName) {
+
+                       // Get User permissions related to category
+                       $categoryMountPoints = $this->backendUserAuthentication->getCategoryMountPoints();
+
+                       // Backup child nodes to be processed.
+                       $treeNodeCollection = $treeData->getChildNodes();
+
+                       if (!empty($categoryMountPoints) && !empty($treeNodeCollection)) {
+
+                               // First, remove all child nodes which must be analysed to be considered as "secure".
+                               // The nodes were backed up in variable $treeNodeCollection beforehand.
+                               $treeData->removeChildNodes();
+
+                               // Create an empty tree node collection to receive the secured nodes.
+                               /** @var TreeNodeCollection $securedTreeNodeCollection */
+                               $securedTreeNodeCollection = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Tree\\TreeNodeCollection');
+
+                               foreach ($categoryMountPoints as $categoryMountPoint) {
+
+                                       $treeNode = $this->lookUpCategoryMountPointInTreeNodes((int) $categoryMountPoint, $treeNodeCollection);
+                                       if (!is_null($treeNode)) {
+                                               $securedTreeNodeCollection->append($treeNode);
+                                       }
+                               }
+
+                               // Reset child nodes.
+                               $treeData->setChildNodes($securedTreeNodeCollection);
+                       }
+               }
+       }
+
+       /**
+        * Recursively look up for a category mount point within a tree.
+        *
+        * @param int $categoryMountPoint
+        * @param TreeNodeCollection $treeNodeCollection
+        * @return NULL|TreeNode
+        */
+       protected function lookUpCategoryMountPointInTreeNodes($categoryMountPoint, TreeNodeCollection $treeNodeCollection) {
+
+               $result = NULL;
+
+               // If any User permission, recursively traverse the tree and set tree part as mount point
+               foreach ($treeNodeCollection as $treeNode) {
+
+                       /** @var \TYPO3\CMS\Backend\Tree\TreeNode $treeNode */
+                       if ((int) $treeNode->getId() === $categoryMountPoint) {
+                               $result = $treeNode;
+                               break;
+                       }
+
+                       if ($treeNode->hasChildNodes()) {
+
+                               /** @var TreeNode $node */
+                               $node = $this->lookUpCategoryMountPointInTreeNodes($categoryMountPoint, $treeNode->getChildNodes());
+                               if (! is_null($node)) {
+                                       $result = $node;
+                                       break;
+                               }
+                       }
+               }
+               return $result;
+       }
+}
diff --git a/typo3/sysext/backend/ext_localconf.php b/typo3/sysext/backend/ext_localconf.php
new file mode 100644 (file)
index 0000000..9af38ec
--- /dev/null
@@ -0,0 +1,13 @@
+<?php
+if (!defined('TYPO3_MODE')) {
+       die('Access denied.');
+}
+
+if (TYPO3_MODE === 'BE') {
+       \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher')->connect(
+               'TYPO3\\CMS\\Core\\Tree\\TableConfiguration\\TableConfiguration\\DatabaseTreeDataProvider',
+               \TYPO3\CMS\Core\Tree\TableConfiguration\DatabaseTreeDataProvider::SIGNAL_PostProcessTreeData,
+               'TYPO3\\CMS\\Backend\\Security\\CategoryPermissionsAspect',
+               'addUserPermissionsToCategoryTreeData'
+       );
+}
index 7e9ca52..12f046f 100644 (file)
@@ -1469,6 +1469,37 @@ class BackendUserAuthentication extends \TYPO3\CMS\Core\Authentication\AbstractU
        }
 
        /**
+        * Returns an array of category mount points. The category permissions from BE Groups are also taken into consideration
+        * and are merged into User permissions.
+        *
+        * @return array
+        */
+       public function getCategoryMountPoints() {
+               $categoryMountPoints = '';
+
+               // Category mounts of the groups
+               if (is_array($this->userGroups)) {
+                       foreach ($this->userGroups as $group) {
+                               if ($group['category_perms']) {
+                                       $categoryMountPoints .= ',' . $group['category_perms'];
+                               }
+                       }
+               }
+
+               // Category mounts of the user record
+               if ($this->user['category_perms']) {
+                       $categoryMountPoints .= ',' . $this->user['category_perms'];
+               }
+
+               // Make the ids unique
+               $categoryMountPoints = GeneralUtility::trimExplode(',', $categoryMountPoints);
+               $categoryMountPoints = array_filter($categoryMountPoints); // remove empty value
+               $categoryMountPoints = array_unique($categoryMountPoints); // remove unique value
+
+               return $categoryMountPoints;
+       }
+
+       /**
         * Returns an array of file mount records, taking workspaces and user home and group home directories into account
         * Needs to be called AFTER the groups have been loaded.
         *
index 9c72861..b4a9902 100644 (file)
@@ -26,6 +26,8 @@ namespace TYPO3\CMS\Core\Tree\TableConfiguration;
  *
  *  This copyright notice MUST APPEAR in all copies of the script!
  ***************************************************************/
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+
 /**
  * TCA tree data provider
  *
@@ -33,6 +35,7 @@ namespace TYPO3\CMS\Core\Tree\TableConfiguration;
  */
 class DatabaseTreeDataProvider extends \TYPO3\CMS\Core\Tree\TableConfiguration\AbstractTableConfigurationTreeDataProvider {
 
+       const SIGNAL_PostProcessTreeData = 'PostProcessTreeData';
        const MODE_CHILDREN = 1;
        const MODE_PARENT = 2;
        /**
@@ -95,6 +98,11 @@ class DatabaseTreeDataProvider extends \TYPO3\CMS\Core\Tree\TableConfiguration\A
        protected $generatedTSConfig = array();
 
        /**
+        * @var \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
+        */
+       protected $signalSlotDispatcher;
+
+       /**
         * Sets the label field
         *
         * @param string $labelField
@@ -289,6 +297,8 @@ class DatabaseTreeDataProvider extends \TYPO3\CMS\Core\Tree\TableConfiguration\A
                if ($childNodes !== NULL) {
                        $this->treeData->setChildNodes($childNodes);
                }
+
+               $this->emitPostProcessTreeDataSignal();
        }
 
        /**
@@ -432,4 +442,37 @@ class DatabaseTreeDataProvider extends \TYPO3\CMS\Core\Tree\TableConfiguration\A
                return $uidArray;
        }
 
+       /**
+        * Emits the post processing tree data signal.
+        *
+        * @return void
+        */
+       protected function emitPostProcessTreeDataSignal() {
+               $this->getSignalSlotDispatcher()->dispatch('TYPO3\\CMS\\Core\\Tree\\TableConfiguration\\TableConfiguration\\DatabaseTreeDataProvider',
+                       self::SIGNAL_PostProcessTreeData,
+                       array($this, $this->treeData)
+               );
+       }
+
+       /**
+        * Get the SignalSlot dispatcher
+        *
+        * @return \TYPO3\CMS\Extbase\SignalSlot\Dispatcher
+        */
+       protected function getSignalSlotDispatcher() {
+               if (!isset($this->signalSlotDispatcher)) {
+                       $this->signalSlotDispatcher = $this->getObjectManager()->get('TYPO3\\CMS\\Extbase\\SignalSlot\\Dispatcher');
+               }
+               return $this->signalSlotDispatcher;
+       }
+
+       /**
+        * Get the ObjectManager
+        *
+        * @return \TYPO3\CMS\Extbase\Object\ObjectManager
+        */
+       protected function getObjectManager() {
+               return GeneralUtility::makeInstance('TYPO3\\CMS\\Extbase\\Object\\ObjectManager');
+       }
+
 }
index 62fadf0..c46a899 100644 (file)
@@ -291,17 +291,38 @@ return array(
                                'maxitems' => 20,
                                'iconsInOptionTags' => 1
                        )
+               ),
+               'category_perms' => array(
+                       'label' => 'LLL:EXT:lang/locallang_tca.xlf:category_perms',
+                       'config' => array(
+                               'type' => 'select',
+                               'foreign_table' => 'sys_category',
+                               'foreign_table_where' => ' AND (sys_category.sys_language_uid = 0 OR sys_category.l10n_parent = 0) ORDER BY sys_category.sorting',
+                               'renderMode' => 'tree',
+                               'treeConfig' => array(
+                                       'parentField' => 'parent',
+                                       'appearance' => array(
+                                               'expandAll' => FALSE,
+                                               'showHeader' => FALSE,
+                                               'maxLevels' => 99,
+                                       ),
+                               ),
+                               'size' => 10,
+                               'autoSizeMax' => 20,
+                               'minitems' => 0,
+                               'maxitems' => 9999
+                       )
                )
        ),
        'types' => array(
                '0' => array('showitem' => 'hidden;;;;1-1-1, title;;;;2-2-2, description, subgroup;;;;3-3-3,
                        --div--;LLL:EXT:lang/locallang_tca.xlf:be_groups.tabs.base_rights, inc_access_lists;;;;1-1-1,
-                       --div--;LLL:EXT:lang/locallang_tca.xlf:be_groups.tabs.mounts_and_workspaces, workspace_perms;;;;1-1-1, db_mountpoints;;;;2-2-2, file_mountpoints;;;;3-3-3, file_permissions,
+                       --div--;LLL:EXT:lang/locallang_tca.xlf:be_groups.tabs.mounts_and_workspaces, workspace_perms;;;;1-1-1, db_mountpoints;;;;2-2-2, file_mountpoints;;;;3-3-3, file_permissions, category_perms
                        --div--;LLL:EXT:lang/locallang_tca.xlf:be_groups.tabs.options, lockToDomain;;;;1-1-1, hide_in_lists;;;;2-2-2, TSconfig;;;;3-3-3,
                        --div--;LLL:EXT:lang/locallang_tca.xlf:be_groups.tabs.extended'),
                '1' => array('showitem' => 'hidden;;;;1-1-1, title;;;;2-2-2, description, subgroup;;;;3-3-3,
                        --div--;LLL:EXT:lang/locallang_tca.xlf:be_groups.tabs.base_rights, inc_access_lists;;;;1-1-1, groupMods, tables_select, tables_modify, pagetypes_select, non_exclude_fields, explicit_allowdeny , allowed_languages;;;;2-2-2, custom_options;;;;3-3-3,
-                       --div--;LLL:EXT:lang/locallang_tca.xlf:be_groups.tabs.mounts_and_workspaces, workspace_perms;;;;1-1-1, db_mountpoints;;;;2-2-2, file_mountpoints;;;;3-3-3, file_permissions,
+                       --div--;LLL:EXT:lang/locallang_tca.xlf:be_groups.tabs.mounts_and_workspaces, workspace_perms;;;;1-1-1, db_mountpoints;;;;2-2-2, file_mountpoints;;;;3-3-3, file_permissions, category_perms
                        --div--;LLL:EXT:lang/locallang_tca.xlf:be_groups.tabs.options, lockToDomain;;;;1-1-1, hide_in_lists;;;;2-2-2, TSconfig;;;;3-3-3,
                        --div--;LLL:EXT:lang/locallang_tca.xlf:be_groups.tabs.extended')
        )
index 6ad7439..285cd5e 100644 (file)
@@ -338,12 +338,33 @@ return array(
                                'eval' => 'datetime',
                                'default' => 0
                        )
+               ),
+               'category_perms' => array(
+                       'label' => 'LLL:EXT:lang/locallang_tca.xlf:category_perms',
+                       'config' => array(
+                               'type' => 'select',
+                               'foreign_table' => 'sys_category',
+                               'foreign_table_where' => ' AND (sys_category.sys_language_uid = 0 OR sys_category.l10n_parent = 0) ORDER BY sys_category.sorting',
+                               'renderMode' => 'tree',
+                               'treeConfig' => array(
+                                       'parentField' => 'parent',
+                                       'appearance' => array(
+                                               'expandAll' => FALSE,
+                                               'showHeader' => FALSE,
+                                               'maxLevels' => 99,
+                                       ),
+                               ),
+                               'size' => 10,
+                               'autoSizeMax' => 20,
+                               'minitems' => 0,
+                               'maxitems' => 9999
+                       )
                )
        ),
        'types' => array(
                '0' => array('showitem' => 'disable;;;;1-1-1, username;;;;2-2-2, password, usergroup;;;;3-3-3, admin;;;;1-1-1, realName;;;;3-3-3, email, lang, lastlogin;;;;1-1-1,
                        --div--;LLL:EXT:lang/locallang_tca.xlf:be_users.tabs.rights, userMods;;;;2-2-2, allowed_languages,
-                       --div--;LLL:EXT:lang/locallang_tca.xlf:be_users.tabs.mounts_and_workspaces, workspace_perms;;;;1-1-1, db_mountpoints;;;;2-2-2, options, file_mountpoints;;;;3-3-3, file_permissions,
+                       --div--;LLL:EXT:lang/locallang_tca.xlf:be_users.tabs.mounts_and_workspaces, workspace_perms;;;;1-1-1, db_mountpoints;;;;2-2-2, options, file_mountpoints;;;;3-3-3, file_permissions, category_perms
                        --div--;LLL:EXT:lang/locallang_tca.xlf:be_users.tabs.options, lockToDomain;;;;1-1-1, disableIPlock, TSconfig;;;;2-2-2,
                        --div--;LLL:EXT:lang/locallang_tca.xlf:be_users.tabs.access, starttime;;;;1-1-1,endtime,
                        --div--;LLL:EXT:lang/locallang_tca.xlf:be_users.tabs.extended'),
index d002947..433054d 100644 (file)
@@ -30,6 +30,7 @@ CREATE TABLE be_groups (
   subgroup text,
   hide_in_lists tinyint(4) DEFAULT '0' NOT NULL,
   workspace_perms tinyint(3) DEFAULT '1' NOT NULL,
+  category_perms varchar(255) DEFAULT '' NOT NULL,
   PRIMARY KEY (uid),
   KEY parent (pid)
 );
@@ -86,6 +87,7 @@ CREATE TABLE be_users (
   usergroup_cached_list text,
   workspace_id int(11) DEFAULT '0' NOT NULL,
   workspace_preview tinyint(3) DEFAULT '1' NOT NULL,
+  category_perms varchar(255) DEFAULT '' NOT NULL,
   PRIMARY KEY (uid),
   KEY parent (pid),
   KEY username (username)
index f5d39a0..52bb689 100644 (file)
@@ -24,6 +24,9 @@
                        <trans-unit id="allowed_languages" xml:space="preserve">
                                <source>Limit to languages:</source>
                        </trans-unit>
+                       <trans-unit id="category_perms" xml:space="preserve">
+                               <source>Category Mounts:</source>
+                       </trans-unit>
                        <trans-unit id="workspace_perms" xml:space="preserve">
                                <source>Workspace permissions:</source>
                        </trans-unit>