Skip to content

Commit

Permalink
[BUGFIX] Provide UpgradeWizard for outdated be_users uc settings
Browse files Browse the repository at this point in the history
Backend users each have a serialized array of individual user
configuration, called "uc" persisted in the be_users table.
Before TYPO3 9.5, the structure of this configuration could sometimes
contain stdClass objects, which is no longer supported. Logging in as
a backend user with such an outdated "uc" array would then crash several
parts of the TYPO3 backend, because the BackendUserConfiguration class
will try to access an stdClass type configuration like an array.

An UpgradeWizard called "BackendUserConfigurationUpdate" is now
provided, which allows to update any outdated "uc" structure of all
backend users.

Resolves: #89269
Resolves: #89268
Resolves: #86398
Releases: master, 9.5
Change-Id: Ic2158b34e70c7f931f4ef8acc8c39af618e241e9
Reviewed-on: https://review.typo3.org/c/Packages/TYPO3.CMS/+/62135
Tested-by: Tymoteusz Motylewski <t.motylewski@gmail.com>
Reviewed-by: Tymoteusz Motylewski <t.motylewski@gmail.com>
  • Loading branch information
IndyIndyIndy authored and tmotyl committed Oct 29, 2019
1 parent 7ce5dc5 commit b7a9e09
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 0 deletions.
@@ -0,0 +1,141 @@
<?php
declare(strict_types = 1);

namespace TYPO3\CMS\Install\Updates;

/*
* 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\Core\Database\ConnectionPool;
use TYPO3\CMS\Core\Utility\GeneralUtility;

/**
* Update the backend user "uc" array to use arrays for its structure, as old TYPO3 versions sometimes used stdClasses
* @internal This class is only meant to be used within EXT:install and is not part of the TYPO3 Core API.
*/
class BackendUserConfigurationUpdate implements UpgradeWizardInterface
{
/**
* @return string Unique identifier of this updater
*/
public function getIdentifier(): string
{
return 'backendUsersConfiguration';
}

/**
* @return string Title of this updater
*/
public function getTitle(): string
{
return 'Update backend user configuration array';
}

/**
* @return string Longer description of this updater
*/
public function getDescription(): string
{
return 'The backend user "uc" array, which is persisted in the db, now only allows for'
. ' arrays inside its structure instead of stdClass objects.'
. ' Update the uc structure for all backend users.';
}

/**
* Checks if an update is needed
*
* @return bool Whether an update is needed (TRUE) or not (FALSE)
*/
public function updateNecessary(): bool
{
$needsExecution = false;

foreach ($this->getAffectedBackendUsers() as $backendUser) {
$userConfig = $this->unserializeUserConfig($backendUser['uc']);

array_walk_recursive($userConfig, function (&$item) use (&$needsExecution) {
if ($item instanceof \stdClass) {
$needsExecution = true;
}
});

if ($needsExecution) {
break;
}
}

return $needsExecution;
}

/**
* @return string[] All new fields and tables must exist
*/
public function getPrerequisites(): array
{
return [
DatabaseUpdatedPrerequisite::class
];
}

/**
* Performs the database update for be_users
*
* @return bool
*/
public function executeUpdate(): bool
{
foreach ($this->getAffectedBackendUsers() as $backendUser) {
$userConfig = $this->unserializeUserConfig($backendUser['uc']);
array_walk_recursive($userConfig, function (&$item) {
if ($item instanceof \stdClass) {
$item = json_decode(json_encode($item), true);
}
});

$this->updateBackendUser($backendUser['uid'], $userConfig);
}

return true;
}

private function unserializeUserConfig(string $userConfig)
{
return unserialize($userConfig, ['allowed_classes' => ['stdClass']]);
}

private function getAffectedBackendUsers(): iterable
{
$queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
->getQueryBuilderForTable('be_users');
$queryBuilder->getRestrictions()->removeAll();
$statement = $queryBuilder
->select('uid', 'uc')
->from('be_users')
->where(
$queryBuilder->expr()->like(
'uc',
$queryBuilder->createNamedParameter(
'%"stdClass"%'
)
)
);

return $statement->execute();
}

private function updateBackendUser(int $userId, array $userConfig): void
{
$connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('be_users');
$connection->update('be_users', ['uc' => serialize($userConfig)], ['uid' => $userId]);
}
}
2 changes: 2 additions & 0 deletions typo3/sysext/install/ext_localconf.php
Expand Up @@ -68,6 +68,8 @@
= \TYPO3\CMS\Install\Updates\PopulatePageSlugs::class;
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['argon2iPasswordHashes']
= \TYPO3\CMS\Install\Updates\Argon2iPasswordHashes::class;
$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['ext/install']['update']['backendUsersConfiguration']
= \TYPO3\CMS\Install\Updates\BackendUserConfigurationUpdate::class;

$iconRegistry = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Imaging\IconRegistry::class);
$icons = [
Expand Down

0 comments on commit b7a9e09

Please sign in to comment.