[TASK] Clean up Page Permission handling in DataHandler 63/62763/6
authorBenni Mack <benni@typo3.org>
Fri, 27 Dec 2019 07:03:41 +0000 (08:03 +0100)
committerOliver Hader <oliver.hader@typo3.org>
Tue, 31 Dec 2019 09:56:31 +0000 (10:56 +0100)
TYPO3's page permissions are based on five fields and a bitset.

- perms_userid
- perms_groupid
- perms_user
- perms_group
- perms_everybody

For permissions of a page there is
- show page ("show")
- edit page properties ("edit page")
- edit page contents / records ("edit content")
- delete page ("delete")
- create new subpages ("new")

In addition, these can be pre-set globally or via PageTSconfig.

The DataHandler currently uses a mix between strings and integers
for defining these values.

A new PagePermissionAssembler class builds together the page permissions
now, allowing to thin out certain parts of DataHandlers responsibility.

The following properties and methods are now deprecated:
- DataHandler->defaultPermissions
- DataHandler->pMap
- DataHandler->setTSconfigPermissions()
- DataHandler->assemblePermissions()

The methods
- DataHandler->doesRecordExist()
- DataHandler->recordInfoWithPermissionCheck()
should only be called with integers as permission argument in the future.

Resolves: #90019
Releases: master
Change-Id: I3724cb8661fe2b7cc5e1f8ab34d17dd4fa68c11b
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/62763
Tested-by: TYPO3com <noreply@typo3.com>
Tested-by: Oliver Hader <oliver.hader@typo3.org>
Reviewed-by: Oliver Hader <oliver.hader@typo3.org>
typo3/sysext/core/Classes/DataHandling/DataHandler.php
typo3/sysext/core/Classes/DataHandling/PagePermissionAssembler.php [new file with mode: 0644]
typo3/sysext/core/Classes/Type/Bitmask/Permission.php
typo3/sysext/core/Documentation/Changelog/master/Deprecation-90019-ExtractionOfDataHandlerPagePermissionLogic.rst [new file with mode: 0644]
typo3/sysext/core/Tests/Functional/DataHandling/Regular/PagePermissionTest.php [new file with mode: 0644]
typo3/sysext/install/Configuration/ExtensionScanner/Php/MethodCallMatcher.php
typo3/sysext/install/Configuration/ExtensionScanner/Php/PropertyPublicMatcher.php
typo3/sysext/workspaces/Classes/Hook/DataHandlerHook.php

index fb4892e..45bc954 100644 (file)
@@ -363,6 +363,7 @@ class DataHandler implements LoggerAwareInterface
      * Can be overridden from $GLOBALS['TYPO3_CONF_VARS']
      *
      * @var array
+     * @deprecated will be removed in TYPO3 11. use PagePermissionAssembler instead.
      */
     public $defaultPermissions = [
         'user' => 'show,edit,delete,new,editcontent',
@@ -371,6 +372,11 @@ class DataHandler implements LoggerAwareInterface
     ];
 
     /**
+     * @var PagePermissionAssembler
+     */
+    protected $pagePermissionAssembler;
+
+    /**
      * The list of <table>-<fields> that cannot be edited by user. This is compiled from TCA/exclude-flag combined with non_exclude_fields for the user.
      *
      * @var array
@@ -418,6 +424,7 @@ class DataHandler implements LoggerAwareInterface
      * Permission mapping
      *
      * @var array
+     * @deprecated will be removed in TYPO3 11. use PagePermissionAssembler instead.
      */
     public $pMap = [
         'show' => 1,
@@ -632,6 +639,7 @@ class DataHandler implements LoggerAwareInterface
         $this->checkStoredRecords = (bool)$GLOBALS['TYPO3_CONF_VARS']['BE']['checkStoredRecords'];
         $this->checkStoredRecords_loose = (bool)$GLOBALS['TYPO3_CONF_VARS']['BE']['checkStoredRecordsLoose'];
         $this->runtimeCache = $this->getRuntimeCache();
+        $this->pagePermissionAssembler = GeneralUtility::makeInstance(PagePermissionAssembler::class, $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPermissions']);
     }
 
     /**
@@ -1056,10 +1064,12 @@ class DataHandler implements LoggerAwareInterface
                 // Here the "pid" is set IF NOT the old pid was a string pointing to a place in the subst-id array.
                 [$tscPID] = BackendUtility::getTSCpid($table, $id, $old_pid_value ?: $fieldArray['pid']);
                 if ($status === 'new' && $table === 'pages') {
-                    $TSConfig = BackendUtility::getPagesTSconfig($tscPID)['TCEMAIN.'] ?? [];
-                    if (isset($TSConfig['permissions.']) && is_array($TSConfig['permissions.'])) {
-                        $fieldArray = $this->setTSconfigPermissions($fieldArray, $TSConfig['permissions.']);
-                    }
+                    $fieldArray = $this->pagePermissionAssembler->applyDefaults(
+                        $fieldArray,
+                        (int)$tscPid,
+                        (int)$this->userid,
+                        (int)$this->BE_USER->firstMainGroup
+                    );
                 }
                 // Processing of all fields in incomingFieldArray and setting them in $fieldArray
                 $fieldArray = $this->fillInFieldArray($table, $id, $fieldArray, $incomingFieldArray, $theRealPid, $status, $tscPID);
@@ -3153,7 +3163,7 @@ class DataHandler implements LoggerAwareInterface
         }
 
         // Fetch record with permission check
-        $row = $this->recordInfoWithPermissionCheck($table, $uid, 'show');
+        $row = $this->recordInfoWithPermissionCheck($table, $uid, Permission::PAGE_SHOW);
 
         // This checks if the record can be selected which is all that a copy action requires.
         if ($row === false) {
@@ -3478,7 +3488,7 @@ class DataHandler implements LoggerAwareInterface
         }
 
         // Fetch record with permission check
-        $row = $this->recordInfoWithPermissionCheck($table, $uid, 'show');
+        $row = $this->recordInfoWithPermissionCheck($table, $uid, Permission::PAGE_SHOW);
 
         // This checks if the record can be selected which is all that a copy action requires.
         if ($row === false) {
@@ -3937,7 +3947,7 @@ class DataHandler implements LoggerAwareInterface
             // Edit rights for the record...
             $mayMoveAccess = $this->checkRecordUpdateAccess($table, $uid);
         } else {
-            $mayMoveAccess = $this->doesRecordExist($table, $uid, 'delete');
+            $mayMoveAccess = $this->doesRecordExist($table, $uid, Permission::PAGE_DELETE);
         }
         // Finding out, if the record may be moved TO another place. Here we check insert-rights (non-pages = edit, pages = new),
         // unless the pages are moved on the same pid, then edit-rights are checked
@@ -4302,7 +4312,7 @@ class DataHandler implements LoggerAwareInterface
             return false;
         }
 
-        if (!$this->doesRecordExist($table, $uid, 'show')) {
+        if (!$this->doesRecordExist($table, $uid, Permission::PAGE_SHOW)) {
             $this->newlog('Attempt to localize record ' . $table . ':' . $uid . ' without permission.', SystemLogErrorClassification::USER_ERROR);
             return false;
         }
@@ -4715,7 +4725,7 @@ class DataHandler implements LoggerAwareInterface
             $this->log($table, $uid, SystemLogDatabaseAction::DELETE, 0, SystemLogErrorClassification::USER_ERROR, 'Attempt to delete record without delete-permissions');
             return;
         }
-        if (!$noRecordCheck && !$this->doesRecordExist($table, $uid, 'delete')) {
+        if (!$noRecordCheck && !$this->doesRecordExist($table, $uid, Permission::PAGE_DELETE)) {
             return;
         }
 
@@ -4950,16 +4960,16 @@ class DataHandler implements LoggerAwareInterface
         // Because it is currently only deleting the translation
         $defaultLanguagePageId = $this->getDefaultLanguagePageId($uid);
         if ($defaultLanguagePageId !== $uid) {
-            if ($this->doesRecordExist('pages', (int)$defaultLanguagePageId, 'delete')) {
+            if ($this->doesRecordExist('pages', (int)$defaultLanguagePageId, Permission::PAGE_DELETE)) {
                 $isTranslatedPage = true;
             } else {
                 return 'Attempt to delete page without permissions';
             }
-        } elseif (!$this->doesRecordExist('pages', $uid, 'delete')) {
+        } elseif (!$this->doesRecordExist('pages', $uid, Permission::PAGE_DELETE)) {
             return 'Attempt to delete page without permissions';
         }
 
-        $pageIdsInBranch = $this->doesBranchExist('', $uid, $this->pMap['delete'], true);
+        $pageIdsInBranch = $this->doesBranchExist('', $uid, Permission::PAGE_DELETE, true);
 
         if ($this->deleteTree) {
             if ($pageIdsInBranch === -1) {
@@ -5003,7 +5013,7 @@ class DataHandler implements LoggerAwareInterface
             $res = $this->canDeletePage($id);
             return is_array($res) ? false : $res;
         }
-        return $this->doesRecordExist($table, $id, 'delete') ? false : 'No permission to delete record';
+        return $this->doesRecordExist($table, $id, Permission::PAGE_DELETE) ? false : 'No permission to delete record';
     }
 
     /**
@@ -5183,7 +5193,7 @@ class DataHandler implements LoggerAwareInterface
         }
 
         // Fetch record with permission check
-        $row = $this->recordInfoWithPermissionCheck($table, $id, 'show');
+        $row = $this->recordInfoWithPermissionCheck($table, $id, Permission::PAGE_SHOW);
 
         // This checks if the record can be selected which is all that a copy action requires.
         if ($row === false) {
@@ -5984,7 +5994,13 @@ class DataHandler implements LoggerAwareInterface
                 return $cachedValue;
             }
 
-            if ($this->doesRecordExist($table, $id, 'edit')) {
+            if ($table === 'pages' || ($table === 'sys_file_reference' && array_key_exists('pages', $this->datamap))) {
+                // @todo: find a more generic way to handle content relations of a page (without needing content editing access to that page)
+                $perms = Permission::PAGE_EDIT;
+            } else {
+                $perms = Permission::CONTENT_EDIT;
+            }
+            if ($this->doesRecordExist($table, $id, $perms)) {
                 $res = 1;
             }
             // Cache the result
@@ -6015,12 +6031,12 @@ class DataHandler implements LoggerAwareInterface
 
         $res = false;
         if ($insertTable === 'pages') {
-            $perms = $this->pMap['new'];
+            $perms = Permission::PAGE_NEW;
         } elseif (($insertTable === 'sys_file_reference') && array_key_exists('pages', $this->datamap)) {
             // @todo: find a more generic way to handle content relations of a page (without needing content editing access to that page)
-            $perms = $this->pMap['edit'];
+            $perms = Permission::PAGE_EDIT;
         } else {
-            $perms = $this->pMap['editcontent'];
+            $perms = Permission::CONTENT_EDIT;
         }
         $pageExists = (bool)$this->doesRecordExist('pages', $pid, $perms);
         // If either admin and root-level or if page record exists and 1) if 'pages' you may create new ones 2) if page-content, new content items may be inserted on the $pid page
@@ -6080,13 +6096,16 @@ class DataHandler implements LoggerAwareInterface
      *
      * @param string $table Record table name
      * @param int $id Record UID
-     * @param int|string $perms Permission restrictions to observe: Either an integer that will be bitwise AND'ed or a string, which points to a key in the ->pMap array
+     * @param int|string $perms Permission restrictions to observe: Either an integer that will be bitwise AND'ed or a string, which points to a key in the ->pMap array. Only integers are supported starting with TYPO3 v11.
      * @return bool Returns TRUE if the record given by $table, $id and $perms can be selected
      *
      * @throws \RuntimeException
      */
     public function doesRecordExist($table, $id, $perms)
     {
+        if (!MathUtility::canBeInterpretedAsInteger($perms)) {
+            trigger_error('Support for handing in permissions as string into "doesRecordExist" will be not be supported with TYPO3 v11 anymore. Use the Permission BitSet class instead.', E_USER_DEPRECATED);
+        }
         return $this->recordInfoWithPermissionCheck($table, $id, $perms, 'uid, pid') !== false;
     }
 
@@ -6379,7 +6398,7 @@ class DataHandler implements LoggerAwareInterface
      *
      * @param string $table Record table name
      * @param int $id Record UID
-     * @param int|string $perms Permission restrictions to observe: Either an integer that will be bitwise AND'ed or a string, which points to a key in the ->pMap array
+     * @param int|string $perms Permission restrictions to observe: Either an integer that will be bitwise AND'ed or a string, which points to a key in the ->pMap array. With TYPO3 v11, only integers are allowed
      * @param string $fieldList - fields - default is '*'
      * @throws \RuntimeException
      * @return array|bool Row if exists and accessible, false otherwise
@@ -6402,12 +6421,11 @@ class DataHandler implements LoggerAwareInterface
         }
         // Processing the incoming $perms (from possible string to integer that can be AND'ed)
         if (!MathUtility::canBeInterpretedAsInteger($perms)) {
+            trigger_error('Support for handing in permissions as string into "recordInfoWithPermissionCheck" will be not be supported with TYPO3 v11 anymore. Use the Permission BitSet class instead.', E_USER_DEPRECATED);
             if ($table !== 'pages') {
                 switch ($perms) {
                     case 'edit':
-
                     case 'delete':
-
                     case 'new':
                         // This holds it all in case the record is not page!!
                         if ($table === 'sys_file_reference' && array_key_exists('pages', $this->datamap)) {
@@ -6418,7 +6436,7 @@ class DataHandler implements LoggerAwareInterface
                         break;
                 }
             }
-            $perms = (int)$this->pMap[$perms];
+            $perms = (int)(Permission::getMap()[$perms] ?? 0);
         } else {
             $perms = (int)$perms;
         }
@@ -7084,9 +7102,11 @@ class DataHandler implements LoggerAwareInterface
      * @param array $fieldArray Field Array, returned with modifications
      * @param array $TSConfig_p TSconfig properties
      * @return array Modified Field Array
+     * @deprecated will be removed in TYPO3 v11.0 - Use PagePermissionAssembler instead.
      */
     public function setTSconfigPermissions($fieldArray, $TSConfig_p)
     {
+        trigger_error('DataHandler->setTSconfigPermissions will be removed in TYPO3 v11.0. Use the PagePermissionAssembler API instead.', E_USER_DEPRECATED);
         if ((string)$TSConfig_p['userid'] !== '') {
             $fieldArray['perms_userid'] = (int)$TSConfig_p['userid'];
         }
@@ -7124,14 +7144,6 @@ class DataHandler implements LoggerAwareInterface
                 }
             }
         }
-        // Set default permissions for a page.
-        if ($table === 'pages') {
-            $fieldArray['perms_userid'] = $this->userid;
-            $fieldArray['perms_groupid'] = (int)$this->BE_USER->firstMainGroup;
-            $fieldArray['perms_user'] = $this->assemblePermissions($this->defaultPermissions['user']);
-            $fieldArray['perms_group'] = $this->assemblePermissions($this->defaultPermissions['group']);
-            $fieldArray['perms_everybody'] = $this->assemblePermissions($this->defaultPermissions['everybody']);
-        }
         return $fieldArray;
     }
 
@@ -7282,14 +7294,17 @@ class DataHandler implements LoggerAwareInterface
      * @return int Integer mask
      * @see setTSconfigPermissions()
      * @see newFieldArray()
+     * @deprecated will be removed in TYPO3 v11.0 - Use PagePermissionAssembler instead.
      */
     public function assemblePermissions($string)
     {
+        trigger_error('DataHandler->assemblePermissions will be removed in TYPO3 v11.0. Use the PagePermissionAssembler API instead.', E_USER_DEPRECATED);
         $keyArr = GeneralUtility::trimExplode(',', $string, true);
         $value = 0;
+        $permissionMap = Permission::getMap();
         foreach ($keyArr as $key) {
-            if ($key && isset($this->pMap[$key])) {
-                $value |= $this->pMap[$key];
+            if ($key && isset($permissionMap[$key])) {
+                $value |= $permissionMap[$key];
             }
         }
         return $value;
@@ -7443,7 +7458,7 @@ class DataHandler implements LoggerAwareInterface
                 ->where($queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($pid, \PDO::PARAM_INT)))
                 ->orderBy('sorting', 'DESC');
             if (!$this->admin) {
-                $queryBuilder->andWhere($this->BE_USER->getPagePermsClause($this->pMap['show']));
+                $queryBuilder->andWhere($this->BE_USER->getPagePermsClause(Permission::PAGE_SHOW));
             }
             if ((int)$this->BE_USER->workspace === 0) {
                 $queryBuilder->andWhere(
diff --git a/typo3/sysext/core/Classes/DataHandling/PagePermissionAssembler.php b/typo3/sysext/core/Classes/DataHandling/PagePermissionAssembler.php
new file mode 100644 (file)
index 0000000..77ea47e
--- /dev/null
@@ -0,0 +1,133 @@
+<?php
+declare(strict_types = 1);
+
+namespace TYPO3\CMS\Core\DataHandling;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Type\Bitmask\Permission;
+use TYPO3\CMS\Core\Utility\GeneralUtility;
+use TYPO3\CMS\Core\Utility\MathUtility;
+
+/**
+ * Class to determine and set proper permissions to a new (or copied) page.
+ *
+ * The following order applies:
+ * - defaultPermissions as defined in this class.
+ * - TYPO3_CONF_VARS[BE][defaultPermissions]
+ * - PageTSconfig va TCEMAIN.permissions
+ */
+class PagePermissionAssembler
+{
+
+    /**
+     * Can be overridden from $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPermissions']
+     *
+     * @var array
+     */
+    protected $defaultPermissions = [
+        'user' => 'show,edit,delete,new,editcontent',
+        'group' => 'show,edit,new,editcontent',
+        'everybody' => ''
+    ];
+
+    public function __construct(array $defaultPermissions = null)
+    {
+        // Initializing default permissions for pages
+        if (isset($defaultPermissions['user'])) {
+            $this->defaultPermissions['user'] = $defaultPermissions['user'];
+        }
+        if (isset($defaultPermissions['group'])) {
+            $this->defaultPermissions['group'] = $defaultPermissions['group'];
+        }
+        if (isset($defaultPermissions['everybody'])) {
+            $this->defaultPermissions['everybody'] = $defaultPermissions['everybody'];
+        }
+    }
+
+    /**
+     * Set default permissions of a new page, and override via pageTSconfig.
+     *
+     * @param array $fieldArray the field array to be used
+     * @param int $pid the parent page ID
+     * @param int $backendUserId the owner of the page to be set
+     * @param int $backendUserGroupId the owner group of the page to be set
+     * @return array the enriched field array
+     */
+    public function applyDefaults(array $fieldArray, int $pid, int $backendUserId, int $backendUserGroupId): array
+    {
+        $fieldArray['perms_userid'] = $backendUserId;
+        $fieldArray['perms_groupid'] = $backendUserGroupId;
+        $fieldArray['perms_user'] = $this->assemblePermissions($this->defaultPermissions['user']);
+        $fieldArray['perms_group'] = $this->assemblePermissions($this->defaultPermissions['group']);
+        $fieldArray['perms_everybody'] = $this->assemblePermissions($this->defaultPermissions['everybody']);
+        $TSConfig = BackendUtility::getPagesTSconfig($pid)['TCEMAIN.'] ?? [];
+        if (isset($TSConfig['permissions.']) && is_array($TSConfig['permissions.'])) {
+            return $this->setTSconfigPermissions($fieldArray, $TSConfig['permissions.']);
+        }
+        return $fieldArray;
+    }
+
+    /**
+     * Setting up perms_* fields in $fieldArray based on TSconfig input
+     * Used for new pages and pages that are copied.
+     *
+     * @param array $fieldArray Field Array, returned with modifications
+     * @param array $tsconfig TSconfig properties
+     * @return array Modified Field Array
+     */
+    protected function setTSconfigPermissions(array $fieldArray, array $tsconfig): array
+    {
+        if ((string)$tsconfig['userid'] !== '') {
+            $fieldArray['perms_userid'] = (int)$tsconfig['userid'];
+        }
+        if ((string)$tsconfig['groupid'] !== '') {
+            $fieldArray['perms_groupid'] = (int)$tsconfig['groupid'];
+        }
+        if ((string)$tsconfig['user'] !== '') {
+            $fieldArray['perms_user'] = $this->assemblePermissions($tsconfig['user']);
+        }
+        if ((string)$tsconfig['group'] !== '') {
+            $fieldArray['perms_group'] = $this->assemblePermissions($tsconfig['group']);
+        }
+        if ((string)$tsconfig['everybody'] !== '') {
+            $fieldArray['perms_everybody'] = $this->assemblePermissions($tsconfig['everybody']);
+        }
+        return $fieldArray;
+    }
+
+    /**
+     * Calculates the bitvalue of the permissions given in a string, comma-separated
+     *
+     * @param string $listOfPermissions a comma-separated list like "show,delete", usually from pageTSconfig
+     * @return int Integer mask
+     */
+    protected function assemblePermissions($listOfPermissions): int
+    {
+        // Already set as integer, so this one is used.
+        if (MathUtility::canBeInterpretedAsInteger($listOfPermissions)) {
+            return (int)$listOfPermissions;
+        }
+        $keyArr = GeneralUtility::trimExplode(',', $listOfPermissions, true);
+        $value = 0;
+        $permissionMap = Permission::getMap();
+        foreach ($keyArr as $key) {
+            if ($key && isset($permissionMap[$key])) {
+                $value |= $permissionMap[$key];
+            }
+        }
+        return $value;
+    }
+}
index 86f55cd..7f30381 100644 (file)
@@ -55,4 +55,26 @@ final class Permission extends Enumeration
      * @var int
      */
     const ALL = 31;
+
+    /**
+     * Permission mapping
+     * Used for instance in PageTS
+     *
+     * @return array
+     * @internal
+     */
+    public static function getMap(): array
+    {
+        return [
+            'show' => static::PAGE_SHOW,
+            // 1st bit
+            'edit' => static::PAGE_EDIT,
+            // 2nd bit
+            'delete' => static::PAGE_DELETE,
+            // 3rd bit
+            'new' => static::PAGE_NEW,
+            // 4th bit
+            'editcontent' => static::CONTENT_EDIT
+        ];
+    }
 }
diff --git a/typo3/sysext/core/Documentation/Changelog/master/Deprecation-90019-ExtractionOfDataHandlerPagePermissionLogic.rst b/typo3/sysext/core/Documentation/Changelog/master/Deprecation-90019-ExtractionOfDataHandlerPagePermissionLogic.rst
new file mode 100644 (file)
index 0000000..f73fe71
--- /dev/null
@@ -0,0 +1,45 @@
+.. include:: ../../Includes.txt
+
+=====================================================================
+Deprecation: #90019 - Extraction of DataHandler page permission logic
+=====================================================================
+
+See :issue:`90019`
+
+Description
+===========
+
+A new PagePermissionAssembler class builds the page permissions, allowing to thin out certain parts of DataHandlers responsibilities.
+
+The following properties and methods within :php:`DataHandler` are now deprecated:
+- :php:`TYPO3\CMS\Core\DataHandling\DataHandler->defaultPermissions`
+- :php:`TYPO3\CMS\Core\DataHandling\DataHandler->pMap`
+- :php:`TYPO3\CMS\Core\DataHandling\DataHandler->setTSconfigPermissions()`
+- :php:`TYPO3\CMS\Core\DataHandling\DataHandler->assemblePermissions()`
+
+The methods
+- :php:`TYPO3\CMS\Core\DataHandling\DataHandler->doesRecordExist()`
+- :php:`TYPO3\CMS\Core\DataHandling\DataHandler->recordInfoWithPermissionCheck()`
+
+should only be called with integers as permission argument.
+
+
+Impact
+======
+
+Calling the mentioned methods will trigger a PHP deprecation warning but will continue to work until TYPO3 v11.0.
+
+
+Affected Installations
+======================
+
+Any TYPO3 installations that enrich page permission handling and directly access the methods or properties in DataHandler.
+
+
+Migration
+=========
+
+Ensure to use the new :php:`PagePermissionAssembler` PHP class
+which serves as a proper API for creating page records.
+
+.. index:: PHP-API, PartiallyScanned, ext:core
\ No newline at end of file
diff --git a/typo3/sysext/core/Tests/Functional/DataHandling/Regular/PagePermissionTest.php b/typo3/sysext/core/Tests/Functional/DataHandling/Regular/PagePermissionTest.php
new file mode 100644 (file)
index 0000000..b9b3fc8
--- /dev/null
@@ -0,0 +1,83 @@
+<?php
+namespace TYPO3\CMS\Core\Tests\Functional\DataHandling\Regular;
+
+use TYPO3\CMS\Backend\Utility\BackendUtility;
+use TYPO3\CMS\Core\Tests\Functional\DataHandling\AbstractDataHandlerActionTestCase;
+use TYPO3\CMS\Core\Type\Bitmask\Permission;
+
+/**
+ * Tests related to DataHandler setting proper page permissions
+ */
+class PagePermissionTest extends AbstractDataHandlerActionTestCase
+{
+    /**
+     * @var string
+     */
+    protected $scenarioDataSetDirectory = 'typo3/sysext/core/Tests/Functional/DataHandling/Regular/DataSet/';
+
+    protected function setUp(): void
+    {
+        parent::setUp();
+        $this->importScenarioDataSet('LiveDefaultPages');
+    }
+
+    /**
+     * @test
+     */
+    public function newPageReceivesDefaultPermissionSet()
+    {
+        $this->backendUser->user['uid'] = 13;
+        $this->backendUser->firstMainGroup = 14;
+        $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPermissions'] = [
+            'user' => 'show,editcontent,edit,delete',
+            'group' => 'show,editcontent,new',
+            'everybody' => 'show'
+        ];
+        $record = $this->insertPage();
+        self::assertEquals(13, $record['perms_userid']);
+        self::assertEquals(14, $record['perms_groupid']);
+        self::assertEquals(Permission::PAGE_SHOW + Permission::CONTENT_EDIT + Permission::PAGE_EDIT + Permission::PAGE_DELETE, $record['perms_user']);
+        self::assertEquals(Permission::PAGE_SHOW + Permission::CONTENT_EDIT + Permission::PAGE_NEW, $record['perms_group']);
+        self::assertEquals(Permission::PAGE_SHOW, $record['perms_everybody']);
+    }
+
+    /**
+     * @test
+     */
+    public function newPageReceivesOverriddenPageTsPermissionSet()
+    {
+        $this->backendUser->user['uid'] = 13;
+        $this->backendUser->firstMainGroup = 14;
+        $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPermissions'] = [
+            'user' => 'show,editcontent,edit,delete',
+            'group' => 'show,editcontent,new',
+            'everybody' => 'show'
+        ];
+        $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultPageTSconfig'] = '
+TCEMAIN.permissions.userid = 12
+TCEMAIN.permissions.groupid = 42
+TCEMAIN.permissions.user = show,edit
+TCEMAIN.permissions.group = show,delete
+TCEMAIN.permissions.everybody = show,delete
+';
+        $record = $this->insertPage();
+        self::assertEquals(12, $record['perms_userid']);
+        self::assertEquals(42, $record['perms_groupid']);
+        self::assertEquals(Permission::PAGE_SHOW + Permission::PAGE_EDIT, $record['perms_user']);
+        self::assertEquals(Permission::PAGE_SHOW + Permission::PAGE_DELETE, $record['perms_group']);
+        self::assertEquals(Permission::PAGE_SHOW + Permission::PAGE_DELETE, $record['perms_everybody']);
+    }
+
+    /**
+     * @return array
+     */
+    protected function insertPage()
+    {
+        // pid 88 comes from LiveDefaultPages
+        $result = $this->actionService->createNewRecord('pages', 88, [
+            'title' => 'Test page'
+        ]);
+        $recordUid = $result['pages'][0];
+        return BackendUtility::getRecord('pages', $recordUid);
+    }
+}
index 80105ac..9bbaacd 100644 (file)
@@ -4378,4 +4378,18 @@ return [
             'Deprecation-89868-RemoveReqCHashFunctionalityForPlugins.rst',
         ],
     ],
+    'TYPO3\CMS\Core\DataHandling\DataHandler->setTSconfigPermissions' => [
+        'numberOfMandatoryArguments' => 2,
+        'maximumNumberOfArguments' => 2,
+        'restFiles' => [
+            'Deprecation-90019-ExtractionOfDataHandlerPagePermissionLogic.rst',
+        ],
+    ],
+    'TYPO3\CMS\Core\DataHandling\DataHandler->assemblePermissions' => [
+        'numberOfMandatoryArguments' => 1,
+        'maximumNumberOfArguments' => 1,
+        'restFiles' => [
+            'Deprecation-90019-ExtractionOfDataHandlerPagePermissionLogic.rst',
+        ],
+    ],
 ];
index 5161ec3..eccc021 100644 (file)
@@ -646,4 +646,14 @@ return [
             'Deprecation-89868-RemoveReqCHashFunctionalityForPlugins.rst',
         ],
     ],
+    'TYPO3\CMS\Core\DataHandling\DataHandler->defaultPermissions' => [
+        'restFiles' => [
+            'Deprecation-90019-ExtractionOfDataHandlerPagePermissionLogic.rst',
+        ],
+    ],
+    'TYPO3\CMS\Core\DataHandling\DataHandler->pMap' => [
+        'restFiles' => [
+            'Deprecation-90019-ExtractionOfDataHandlerPagePermissionLogic.rst',
+        ],
+    ],
 ];
index 5515a59..25e5857 100644 (file)
@@ -810,7 +810,7 @@ class DataHandlerHook
             $dataHandler->newlog('Records in workspace #' . $swapVersion['t3ver_wsid'] . ' can only be published when in "Publish" stage.', SystemLogErrorClassification::USER_ERROR);
             return;
         }
-        if (!($dataHandler->doesRecordExist($table, $swapWith, 'show') && $dataHandler->checkRecordUpdateAccess($table, $swapWith))) {
+        if (!($dataHandler->doesRecordExist($table, $swapWith, Permission::PAGE_SHOW) && $dataHandler->checkRecordUpdateAccess($table, $swapWith))) {
             $dataHandler->newlog('You cannot publish a record you do not have edit and show permissions for', SystemLogErrorClassification::USER_ERROR);
             return;
         }