Commit f2476e38 authored by Christian Kuhn's avatar Christian Kuhn Committed by Andreas Wolf
Browse files

[TASK] Protect user TSconfig properties in BackendUserAuthentication

The patch sets all user TSconfig related properties of class
BackendUserAuthentication to protected. Existing usages of
property "userTS" are adapted to call getTSConfig() instead.

Resolves: #84984
Related: #84982
Releases: master
Change-Id: I6fd10164c29959e4e705699776d694d63d7f5596
Reviewed-on: https://review.typo3.org/56940


Tested-by: default avatarTYPO3com <no-reply@typo3.com>
Reviewed-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring's avatarAnja Leichsenring <aleichsenring@ab-softlab.de>
Reviewed-by: Andreas Wolf's avatarAndreas Wolf <andreas.wolf@typo3.org>
Tested-by: Andreas Wolf's avatarAndreas Wolf <andreas.wolf@typo3.org>
parent eaa591ac
......@@ -30,7 +30,7 @@ class UserTsConfig implements FormDataProviderInterface
*/
public function addData(array $result)
{
$result['userTsConfig'] = $this->getBackendUser()->userTS;
$result['userTsConfig'] = $this->getBackendUser()->getTSConfig();
return $result;
}
......
......@@ -23,7 +23,7 @@ interface ClearCacheActionsHookInterface
* Modifies CacheMenuItems array
*
* @param array $cacheActions Array of CacheMenuItems
* @param array $optionValues Array of AccessConfigurations-identifiers (typically used by userTS with options.clearCache.identifier)
* @param array $optionValues Array of AccessConfigurations-identifiers (typically used by userTS with options.clearCache.identifier)
* @return
*/
public function manipulateCacheActions(&$cacheActions, &$optionValues);
......
......@@ -865,7 +865,7 @@ class BackendUtility
}
$cacheHash = $res['hash'];
// Get User TSconfig overlay
$userTSconfig = static::getBackendUserAuthentication()->userTS['page.'] ?? null;
$userTSconfig = static::getBackendUserAuthentication()->getTSConfig()['page.'] ?? null;
if (is_array($userTSconfig)) {
ArrayUtility::mergeRecursiveWithOverrule($tsConfig, $userTSconfig);
$cacheHash .= '_user' . static::getBackendUserAuthentication()->user['uid'];
......
......@@ -15,6 +15,7 @@ namespace TYPO3\CMS\Backend\Tests\Unit\Form\FormDataProvider;
*/
use TYPO3\CMS\Backend\Form\FormDataProvider\UserTsConfig;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
/**
* Test case
......@@ -37,8 +38,9 @@ class UserTsConfigTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
public function addDataSetsUserTypoScriptInResult()
{
$expected = ['foo'];
$GLOBALS['BE_USER'] = new \stdClass();
$GLOBALS['BE_USER']->userTS = $expected;
$backendUserAuthenticationProphecy = $this->prophesize(BackendUserAuthentication::class);
$backendUserAuthenticationProphecy->getTSConfig()->willReturn($expected);
$GLOBALS['BE_USER'] = $backendUserAuthenticationProphecy->reveal();
$result = $this->subject->addData([]);
$this->assertEquals($expected, $result['userTsConfig']);
}
......
......@@ -886,7 +886,7 @@ class BackendUtilityTest extends \TYPO3\TestingFramework\Core\Unit\UnitTestCase
/**
* @test
*/
public function getModTSconfigIgnoresValuesFromUserTsConfigIfNoSet()
public function getModTSconfigIgnoresValuesFromUserTsConfigIfNotSet()
{
$completeConfiguration = [
'value' => 'bar',
......
......@@ -16,6 +16,7 @@ namespace TYPO3\CMS\Core\Authentication;
use TYPO3\CMS\Backend\Utility\BackendUtility;
use TYPO3\CMS\Core\Cache\CacheManager;
use TYPO3\CMS\Core\Compatibility\PublicPropertyDeprecationTrait;
use TYPO3\CMS\Core\Database\Connection;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Expression\ExpressionBuilder;
......@@ -40,7 +41,22 @@ use TYPO3\CMS\Core\Utility\GeneralUtility;
*/
class BackendUserAuthentication extends AbstractUserAuthentication
{
const ROLE_SYSTEMMAINTAINER = 'systemMaintainer';
use PublicPropertyDeprecationTrait;
public const ROLE_SYSTEMMAINTAINER = 'systemMaintainer';
/**
* Properties which have been moved to protected status from public
*
* @var array
*/
protected $deprecatedPublicProperties = [
'TSdataArray' => 'Using $TSdataArray of class BackendUserAuthentication from the outside is discouraged. This property is for class internal use only.',
'userTS' => 'Using $userTS of class BackendUserAuthentication from the outside is discouraged. Use getTSConfig() instead.',
'userTSUpdated' => 'Using $userTSUpdated of class BackendUserAuthentication from the outside is discouraged. This property is for class internal use only.',
'userTS_text' => 'Using $userTS_text of class BackendUserAuthentication from the outside is discouraged. This property is for class internal use only.',
'userTS_dontGetCached' => 'Using $userTS_dontGetCached of class BackendUserAuthentication is deprecated. The property will be removed in v10.',
];
/**
* Should be set to the usergroup-column (id-list) in the user-record
......@@ -125,34 +141,29 @@ class BackendUserAuthentication extends AbstractUserAuthentication
public $includeGroupArray = [];
/**
* Used to accumulate the TSconfig data of the user
* @var array
* @var array Accumulated, unparsed TSconfig data array of the user
*/
public $TSdataArray = [];
protected $TSdataArray = [];
/**
* Contains the non-parsed user TSconfig
* @var string
* @var string Accumulated, unparsed TSconfig data string of the user
*/
public $userTS_text = '';
protected $userTS_text = '';
/**
* Contains the parsed user TSconfig
* @var array
* @var array Parsed user TSconfig
*/
public $userTS = [];
protected $userTS = [];
/**
* Set internally if the user TSconfig was parsed and needs to be cached.
* @var bool
* @var bool True if the user TSconfig was parsed and needs to be cached.
*/
public $userTSUpdated = false;
protected $userTSUpdated = false;
/**
* Set this from outside if you want the user TSconfig to ALWAYS be parsed and not fetched from cache.
* @var bool
* @var bool @deprecated since v9, will be removed in v10. If true, parsed TSconfig will not be cached
*/
public $userTS_dontGetCached = false;
protected $userTS_dontGetCached = false;
/**
* Contains last error message
......@@ -1363,6 +1374,7 @@ class BackendUserAuthentication extends AbstractUserAuthentication
// Imploding with "[global]" will make sure that non-ended confinements with braces are ignored.
$this->userTS_text = implode(LF . '[GLOBAL]' . LF, $this->TSdataArray);
if (!$this->userTS_dontGetCached) {
// @deprecated: Property userTS_dontGetCached is deprecated since v9 and will be removed in v10
// Perform TS-Config parsing with condition matching
$parseObj = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Configuration\TsConfigParser::class);
$res = $parseObj->parseTSconfig($this->userTS_text, 'userTS');
......@@ -1376,6 +1388,7 @@ class BackendUserAuthentication extends AbstractUserAuthentication
$cache = GeneralUtility::makeInstance(CacheManager::class)->getCache('cache_hash');
$cachedContent = $cache->get($hash);
if (is_array($cachedContent) && !$this->userTS_dontGetCached) {
// @deprecated: Property userTS_dontGetCached is deprecated since v9 and will be removed in v10
$this->userTS = $cachedContent;
} else {
$parseObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::class);
......@@ -1874,7 +1887,7 @@ class BackendUserAuthentication extends AbstractUserAuthentication
);
// Finally overlay any userTSconfig
$permissionsTsConfig = $this->getTSConfigProp('permissions.file.default');
$permissionsTsConfig = $this->getTSConfig()['permissions.']['file.']['default.'] ?? [];
if (!empty($permissionsTsConfig)) {
array_walk(
$permissionsTsConfig,
......@@ -1903,7 +1916,7 @@ class BackendUserAuthentication extends AbstractUserAuthentication
{
$finalUserPermissions = $this->getFilePermissions();
if (!$this->isAdmin()) {
$storageFilePermissions = $this->getTSConfigProp('permissions.file.storage.' . $storageObject->getUid());
$storageFilePermissions = $this->getTSConfig()['permissions.']['file.']['storage.'][$storageObject->getUid() . '.'] ?? [];
if (!empty($storageFilePermissions)) {
array_walk(
$storageFilePermissions,
......
......@@ -1416,9 +1416,10 @@ class QueryGenerator
{
$out = [];
$enableArr = explode(',', $enableList);
$backendUserAuthentication = $this->getBackendUserAuthentication();
$userTsConfig = $this->getBackendUserAuthentication()->getTSConfig();
// Make output
if (in_array('table', $enableArr) && !$backendUserAuthentication->userTS['mod.']['dbint.']['disableSelectATable']) {
if (in_array('table', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableSelectATable']) {
$out[] = '<div class="form-group">';
$out[] = ' <label for="SET[queryTable]">Select a table:</label>';
$out[] = $this->mkTableSelect('SET[queryTable]', $this->table);
......@@ -1458,25 +1459,25 @@ class QueryGenerator
$this->enableQueryParts = (bool)$modSettings['search_query_smallparts'];
$codeArr = $this->getFormElements();
$queryCode = $this->printCodeArray($codeArr);
if (in_array('fields', $enableArr) && !$backendUserAuthentication->userTS['mod.']['dbint.']['disableSelectFields']) {
if (in_array('fields', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableSelectFields']) {
$out[] = '<div class="form-group form-group-with-button-addon">';
$out[] = ' <label for="SET[queryFields]">Select fields:</label>';
$out[] = $this->mkFieldToInputSelect('SET[queryFields]', $this->extFieldLists['queryFields']);
$out[] = '</div>';
}
if (in_array('query', $enableArr) && !$backendUserAuthentication->userTS['mod.']['dbint.']['disableMakeQuery']) {
if (in_array('query', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableMakeQuery']) {
$out[] = '<div class="form-group">';
$out[] = ' <label>Make Query:</label>';
$out[] = $queryCode;
$out[] = '</div>';
}
if (in_array('group', $enableArr) && !$backendUserAuthentication->userTS['mod.']['dbint.']['disableGroupBy']) {
if (in_array('group', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableGroupBy']) {
$out[] = '<div class="form-group form-inline">';
$out[] = ' <label for="SET[queryGroup]">Group By:</label>';
$out[] = $this->mkTypeSelect('SET[queryGroup]', $this->extFieldLists['queryGroup'], '');
$out[] = '</div>';
}
if (in_array('order', $enableArr) && !$backendUserAuthentication->userTS['mod.']['dbint.']['disableOrderBy']) {
if (in_array('order', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableOrderBy']) {
$module = $this->getModule();
$orderByArr = explode(',', $this->extFieldLists['queryOrder']);
$orderBy = [];
......@@ -1500,7 +1501,7 @@ class QueryGenerator
$out[] = implode(LF, $orderBy);
$out[] = '</div>';
}
if (in_array('limit', $enableArr) && !$backendUserAuthentication->userTS['mod.']['dbint.']['disableLimit']) {
if (in_array('limit', $enableArr) && !$userTsConfig['mod.']['dbint.']['disableLimit']) {
$limit = [];
$limit[] = '<div class="input-group">';
$limit[] = ' <div class="input-group-addon">';
......
......@@ -422,7 +422,8 @@ class QueryView
$output = '';
$this->hookArray = $GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['t3lib_fullsearch'] ?? [];
$msg = $this->procesStoreControl();
if (!$this->backendUserAuthentication->userTS['mod.']['dbint.']['disableStoreControl']) {
$userTsConfig = $this->backendUserAuthentication->getTSConfig();
if (!$userTsConfig['mod.']['dbint.']['disableStoreControl']) {
$output .= '<h2>Load/Save Query</h2>';
$output .= '<div>' . $this->makeStoreControl() . '</div>';
$output .= $msg;
......@@ -465,13 +466,13 @@ class QueryView
$fullQueryString = $selectQueryString;
$dataRows = $connection->executeQuery($selectQueryString)->fetchAll();
}
if (!$this->backendUserAuthentication->userTS['mod.']['dbint.']['disableShowSQLQuery']) {
if (!$userTsConfig['mod.']['dbint.']['disableShowSQLQuery']) {
$output .= '<h2>SQL query</h2><div><pre>' . htmlspecialchars($fullQueryString) . '</pre></div>';
}
$cPR = $this->getQueryResultCode($mQ, $dataRows, $queryGenerator->table);
$output .= '<h2>' . $cPR['header'] . '</h2><div>' . $cPR['content'] . '</div>';
} catch (DBALException $e) {
if (!$this->backendUserAuthentication->userTS['mod.']['dbint.']['disableShowSQLQuery']) {
if (!$userTsConfig['mod.']['dbint.']['disableShowSQLQuery']) {
$output .= '<h2>SQL query</h2><div><pre>' . htmlspecialchars($fullQueryString) . '</pre></div>';
}
$out = '<p><strong>Error: <span class="text-danger">'
......
.. include:: ../../Includes.txt
=====================================================================================
Deprecation: #84984 - Protected user TSconfig properties in BackendUserAuthentication
=====================================================================================
See :issue:`84984`
Description
===========
The following properties of class :php:`TYPO3\CMS\Core\Authentication\BackendUserAuthentication` have been set to protected:
* :php:`->userTS`: use method :php:`->getTSConfig()` instead
* :php:`->userTSUpdated`: Class internal property
* :php:`->userTS_text`: Class internal property
* :php:`->TSdataArray`: Class internal property
* :php:`->userTS_dontGetCached`: Will be removed in v10 without substitution
From the above list, property :php:`->userTS` is the most likely one to be used by extensions.
As a substitution, the full parsed user TSconfig data array can be retrieved calling method :php:`getTSConfig()`.
Impact
======
The properties are still accessible in v9 from outside of the class but will log a deprecation message if used.
Affected Installations
======================
Instances with extensions that add backend modules which can be configured via user TSconfig may be
affected by this change. The extension scanner should find possible usages in extensions.
Migration
=========
Use :php:`->getTSConfig()` instead of :php:`->userTS`, do not use the properties marked as internal above,
remove usage of :php:`userTS_dontGetCached` and configure the UserTSconfig cache via the caching
framework's configuration instead of setting the property `userTS_dontGetCached`
.. index:: Backend, PHP-API, TSConfig, FullyScanned
\ No newline at end of file
......@@ -257,12 +257,9 @@ class BackendUserAuthenticationTest extends UnitTestCase
public function getTSConfigReturnsCorrectArrayForGivenObjectString(array $completeConfiguration, $objectString, array $expectedConfiguration): void
{
/** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
$subject = $this->getMockBuilder(BackendUserAuthentication::class)
->setMethods(['dummy'])
->disableOriginalConstructor()
->getMock();
$subject = $this->getAccessibleMock(BackendUserAuthentication::class, ['dummy'], [], '', false);
$subject->setLogger(new NullLogger());
$subject->userTS = $completeConfiguration;
$subject->_set('userTS', $completeConfiguration);
$actualConfiguration = $subject->getTSConfig($objectString);
$this->assertSame($expectedConfiguration, $actualConfiguration);
......@@ -329,7 +326,7 @@ class BackendUserAuthenticationTest extends UnitTestCase
{
/** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
$subject = $this->getMockBuilder(BackendUserAuthentication::class)
->setMethods(['isAdmin'])
->setMethods(['isAdmin', 'getTSConfig'])
->getMock();
$subject
......@@ -338,13 +335,16 @@ class BackendUserAuthenticationTest extends UnitTestCase
->will($this->returnValue(false));
$subject->setLogger(new NullLogger());
$subject->userTS = [
'permissions.' => [
'file.' => [
'default.' => $userTsConfiguration
],
]
];
$subject
->expects($this->any())
->method('getTSConfig')
->will($this->returnValue([
'permissions.' => [
'file.' => [
'default.' => $userTsConfiguration
],
]
]));
$expectedPermissions = array_merge($this->defaultFilePermissions, $userTsConfiguration);
array_walk(
......@@ -468,7 +468,7 @@ class BackendUserAuthenticationTest extends UnitTestCase
{
/** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
$subject = $this->getMockBuilder(BackendUserAuthentication::class)
->setMethods(['isAdmin', 'getFilePermissions'])
->setMethods(['isAdmin', 'getFilePermissions', 'getTSConfig'])
->getMock();
$storageMock = $this->createMock(ResourceStorage::class);
$storageMock->expects($this->any())->method('getUid')->will($this->returnValue($storageUid));
......@@ -483,15 +483,18 @@ class BackendUserAuthenticationTest extends UnitTestCase
->method('getFilePermissions')
->will($this->returnValue($defaultPermissions));
$subject->userTS = [
'permissions.' => [
'file.' => [
'storage.' => [
$storageUid . '.' => $storagePermissions
$subject
->expects($this->any())
->method('getTSConfig')
->will($this->returnValue([
'permissions.' => [
'file.' => [
'storage.' => [
$storageUid . '.' => $storagePermissions
],
],
],
]
];
]
]));
$this->assertEquals($expectedPermissions, $subject->getFilePermissionsForStorage($storageMock));
}
......@@ -507,7 +510,7 @@ class BackendUserAuthenticationTest extends UnitTestCase
{
/** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
$subject = $this->getMockBuilder(BackendUserAuthentication::class)
->setMethods(['isAdmin', 'getFilePermissions'])
->setMethods(['isAdmin', 'getFilePermissions', 'getTSConfig'])
->getMock();
$storageMock = $this->createMock(ResourceStorage::class);
$storageMock->expects($this->any())->method('getUid')->will($this->returnValue($storageUid));
......@@ -522,15 +525,18 @@ class BackendUserAuthenticationTest extends UnitTestCase
->method('getFilePermissions')
->will($this->returnValue($defaultPermissions));
$subject->userTS = [
'permissions.' => [
'file.' => [
'storage.' => [
$storageUid . '.' => $storagePermissions
$subject
->expects($this->any())
->method('getTSConfig')
->will($this->returnValue([
'permissions.' => [
'file.' => [
'storage.' => [
$storageUid . '.' => $storagePermissions
],
],
],
]
];
]
]));
$this->assertEquals($defaultPermissions, $subject->getFilePermissionsForStorage($storageMock));
}
......@@ -656,7 +662,7 @@ class BackendUserAuthenticationTest extends UnitTestCase
{
/** @var BackendUserAuthentication|\PHPUnit_Framework_MockObject_MockObject $subject */
$subject = $this->getMockBuilder(BackendUserAuthentication::class)
->setMethods(['isAdmin'])
->setMethods(['isAdmin', 'getTSConfig'])
->getMock();
$subject
......@@ -664,7 +670,10 @@ class BackendUserAuthenticationTest extends UnitTestCase
->method('isAdmin')
->will($this->returnValue(false));
$subject->userTS = [];
$subject
->expects($this->any())
->method('getTSConfig')
->will($this->returnValue([]));
$subject->groupData['file_permissions'] = $permissionValue;
$this->assertEquals($expectedPermissions, $subject->getFilePermissions());
}
......
......@@ -14,6 +14,7 @@ namespace TYPO3\CMS\Extbase\Tests\Functional\Persistence;
* The TYPO3 project - inspiring people to share!
*/
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;
......@@ -54,6 +55,7 @@ class AddTest extends \TYPO3\TestingFramework\Core\Functional\FunctionalTestCase
$this->objectManager = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
$this->persistentManager = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class);
$this->blogRepository = $this->objectManager->get(\ExtbaseTeam\BlogExample\Domain\Repository\BlogRepository::class);
$GLOBALS['BE_USER'] = new BackendUserAuthentication();
}
/**
......
......@@ -3,6 +3,7 @@ namespace TYPO3\CMS\Extbase\Tests\Functional\Persistence\Generic\Mapper;
use ExtbaseTeam\BlogExample\Domain\Model\Comment;
use ExtbaseTeam\BlogExample\Domain\Model\DateExample;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Utility\GeneralUtility;
class DataMapperTest extends \TYPO3\TestingFramework\Core\Functional\FunctionalTestCase
......@@ -36,6 +37,8 @@ class DataMapperTest extends \TYPO3\TestingFramework\Core\Functional\FunctionalT
$this->objectManager = GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
$this->persistenceManager = $this->objectManager->get(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class);
$GLOBALS['BE_USER'] = new BackendUserAuthentication();
}
/**
......
......@@ -20,6 +20,7 @@ use ExtbaseTeam\BlogExample\Domain\Model\Tag;
use ExtbaseTeam\BlogExample\Domain\Repository\BlogRepository;
use ExtbaseTeam\BlogExample\Domain\Repository\PersonRepository;
use ExtbaseTeam\BlogExample\Domain\Repository\PostRepository;
use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
use TYPO3\CMS\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
use TYPO3\CMS\Core\Utility\GeneralUtility;
......@@ -70,6 +71,8 @@ class RelationTest extends \TYPO3\TestingFramework\Core\Functional\FunctionalTes
/* @var $blogRepository \TYPO3\CMS\Extbase\Persistence\Repository */
$blogRepository = $this->objectManager->get(BlogRepository::class);
$this->blog = $blogRepository->findByUid(1);
$GLOBALS['BE_USER'] = new BackendUserAuthentication();
}
/**
......
......@@ -552,4 +552,29 @@ return [
'Deprecation-84463-PageTsConfigOptionModweb_listnewWizardsDropped.rst',
],
],
'TYPO3\CMS\Core\Authentication\BackendUserAuthentication->TSdataArray' => [
'restFiles' => [
'Deprecation-84984-ProtectedUserTSconfigPropertiesInBackendUserAuthentication.rst'
],
],
'TYPO3\CMS\Core\Authentication\BackendUserAuthentication->userTS' => [
'restFiles' => [
'Deprecation-84984-ProtectedUserTSconfigPropertiesInBackendUserAuthentication.rst'
],
],
'TYPO3\CMS\Core\Authentication\BackendUserAuthentication->userTSUpdated' => [
'restFiles' => [
'Deprecation-84984-ProtectedUserTSconfigPropertiesInBackendUserAuthentication.rst'
],
],
'TYPO3\CMS\Core\Authentication\BackendUserAuthentication->userTS_text' => [
'restFiles' => [
'Deprecation-84984-ProtectedUserTSconfigPropertiesInBackendUserAuthentication.rst'
],
],
'TYPO3\CMS\Core\Authentication\BackendUserAuthentication->userTS_dontGetCached' => [
'restFiles' => [
'Deprecation-84984-ProtectedUserTSconfigPropertiesInBackendUserAuthentication.rst'
],
],
];
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment