[TASK] Harden system maintainer handling 14/58314/6
authorChristian Kuhn <lolli@schwarzbu.ch>
Tue, 18 Sep 2018 13:13:35 +0000 (15:13 +0200)
committerAnja Leichsenring <aleichsenring@ab-softlab.de>
Wed, 19 Sep 2018 13:55:13 +0000 (15:55 +0200)
Better information handling and restrictions for non
system maintainer trying to change admin flag or password
of system maintainers.

Resolves: #86297
Releases: master
Change-Id: I1849c6ed7edfb78327b4802f4bc9b20f1fd701aa
Reviewed-on: https://review.typo3.org/58314
Reviewed-by: Markus Klein <markus.klein@typo3.org>
Tested-by: TYPO3com <no-reply@typo3.com>
Tested-by: Markus Klein <markus.klein@typo3.org>
Tested-by: Andreas Fernandez <a.fernandez@scripting-base.de>
Reviewed-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
Tested-by: Anja Leichsenring <aleichsenring@ab-softlab.de>
typo3/sysext/backend/Classes/Form/FieldInformation/AdminIsSystemMaintainer.php [new file with mode: 0644]
typo3/sysext/backend/Classes/Form/NodeExpansion/FieldInformation.php
typo3/sysext/backend/Classes/Form/NodeFactory.php
typo3/sysext/core/Classes/DataHandling/DataHandler.php
typo3/sysext/core/Configuration/TCA/be_users.php
typo3/sysext/core/Resources/Private/Language/locallang_core.xlf
typo3/sysext/setup/Classes/Controller/SetupModuleController.php

diff --git a/typo3/sysext/backend/Classes/Form/FieldInformation/AdminIsSystemMaintainer.php b/typo3/sysext/backend/Classes/Form/FieldInformation/AdminIsSystemMaintainer.php
new file mode 100644 (file)
index 0000000..b3e01de
--- /dev/null
@@ -0,0 +1,97 @@
+<?php
+declare(strict_types = 1);
+namespace TYPO3\CMS\Backend\Form\FieldInformation;
+
+/*
+ * 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\Form\AbstractNode;
+use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
+use TYPO3\CMS\Core\Localization\LanguageService;
+
+/**
+ * This field information node is used in be_user admin field
+ * to show some additional information if the edited user
+ * is a system maintainer or becomes one if togging the admin flag.
+ */
+class AdminIsSystemMaintainer extends AbstractNode
+{
+    /**
+     * Handler for single nodes
+     *
+     * @return array As defined in initializeResultArray() of AbstractNode
+     * @throws \RuntimeException
+     */
+    public function render(): array
+    {
+        if ($this->data['tableName'] !== 'be_users' || $this->data['fieldName'] !== 'admin') {
+            throw new \RuntimeException(
+                'The adminIsSystemMaintainer field information can be used for admin field in be_users table only',
+                1537273550
+            );
+        }
+
+        $resultArray = $this->initializeResultArray();
+        if ($this->data['command'] === 'new') {
+            // Early return on 'new' records - nothing we can do here
+            return $resultArray;
+        }
+
+        // False if current user is not in system maintainer list or if switch to user mode is active
+        $isCurrentUserSystemMaintainer = $this->getBackendUser()->isSystemMaintainer();
+        $systemMaintainers = array_map('intval', $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemMaintainers'] ?? []);
+        $isTargetUserInSystemMaintainerList = in_array((int)$this->data['vanillaUid'], $systemMaintainers, true);
+
+        if ($isTargetUserInSystemMaintainerList) {
+            $languageService = $this->getLanguageService();
+            $isTargetUserAdmin = (int)$this->data['databaseRow']['admin'] === 1;
+            if ($isCurrentUserSystemMaintainer) {
+                if ($isTargetUserAdmin) {
+                    // User is a system maintainer
+                    $fieldInformationText = '<strong>' . htmlspecialchars($languageService->sL(
+                            'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:formEngine.beUser.admin.information.userIsSystemMaintainer'
+                        )) . '</strong>';
+                } else {
+                    // User is currently not an admin, but set as system maintainer (in-effective).
+                    // If admin field is set to 1, the user is therefore system maintainer again.
+                    $fieldInformationText = '<strong>' . htmlspecialchars($languageService->sL(
+                            'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:formEngine.beUser.admin.information.userWillBecomeSystemMaintainer'
+                        )) . '</strong>';
+                }
+            } else {
+                // User is in system maintainer list, user can not change admin and password
+                $fieldInformationText = '<strong>' . htmlspecialchars($languageService->sL(
+                        'LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:formEngine.beUser.admin.information.userAdminAndPasswordChangeNotAllowed'
+                    )) . '</strong>';
+            }
+            $resultArray['html'] = $fieldInformationText;
+        }
+        return $resultArray;
+    }
+
+    /**
+     * @return LanguageService
+     */
+    protected function getLanguageService(): LanguageService
+    {
+        return $GLOBALS['LANG'];
+    }
+
+    /**
+     * @return BackendUserAuthentication
+     */
+    protected function getBackendUser(): BackendUserAuthentication
+    {
+        return $GLOBALS['BE_USER'];
+    }
+}
index 5bafc08..3dd1d3e 100644 (file)
@@ -66,7 +66,7 @@ class FieldInformation extends AbstractNode
             $options['renderData']['fieldInformationOptions'] = $orderedFieldInformationConfiguration['options'] ?? [];
             $informationResult = $this->nodeFactory->create($options)->render();
 
-            $allowedTags = '<a><br><br/><div><em><i><p><strong><span>';
+            $allowedTags = '<a><br><br/><div><em><i><p><strong><span><code>';
             if (strip_tags($informationResult['html'], $allowedTags) !== $informationResult['html']) {
                 throw new \RuntimeException(
                     'The field information API supports only a limited number of HTML tags within the result'
index e98a5f5..a08a000 100644 (file)
@@ -104,6 +104,7 @@ class NodeFactory
 
         // Element information
         'tcaDescription' => FieldInformation\TcaDescription::class,
+        'adminIsSystemMaintainer' => FieldInformation\AdminIsSystemMaintainer::class,
 
         // Element wizards
         'defaultLanguageDifferences' => FieldWizard\DefaultLanguageDifferences::class,
index d9e5d6f..0420b5d 100644 (file)
@@ -1649,6 +1649,34 @@ class DataHandler implements LoggerAwareInterface
             }
         }
 
+        if ($table === 'be_users'
+            && ($field === 'admin' || $field === 'password')
+            && $status === 'update'
+        ) {
+            // Do not allow a non system maintainer admin to change admin flag and password of system maintainers
+            $systemMaintainers = array_map('intval', $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemMaintainers'] ?? []);
+            // False if current user is not in system maintainer list or if switch to user mode is active
+            $isCurrentUserSystemMaintainer = $this->BE_USER->isSystemMaintainer();
+            $isTargetUserInSystemMaintainerList = in_array((int)$id, $systemMaintainers, true);
+            if ($field === 'admin') {
+                $isFieldChanged = (int)$curValueRec[$field] !== (int)$value;
+            } else {
+                $isFieldChanged = $curValueRec[$field] !== $value;
+            }
+            if (!$isCurrentUserSystemMaintainer && $isTargetUserInSystemMaintainerList && $isFieldChanged) {
+                $value = $curValueRec[$field];
+                $message = GeneralUtility::makeInstance(
+                    FlashMessage::class,
+                    $this->getLanguageService()->sL('LLL:EXT:core/Resources/Private/Language/locallang_core.xlf:error.adminCanNotChangeSystemMaintainer'),
+                    '',
+                    FlashMessage::ERROR,
+                    true
+                );
+                $flashMessageService = GeneralUtility::makeInstance(FlashMessageService::class);
+                $flashMessageService->getMessageQueueByIdentifier()->enqueue($message);
+            }
+        }
+
         // Getting config for the field
         $tcaFieldConf = $GLOBALS['TCA'][$table]['columns'][$field]['config'];
 
index d78ea05..9807518 100644 (file)
@@ -219,7 +219,12 @@ return [
                         1 => '',
                     ],
                 ],
-            ]
+                'fieldInformation' => [
+                    'adminIsSystemMaintainer' => [
+                        'renderType' => 'adminIsSystemMaintainer',
+                    ],
+                ],
+            ],
         ],
         'options' => [
             'label' => 'LLL:EXT:core/Resources/Private/Language/locallang_tca.xlf:be_users.options',
index 66b8c6c..260e726 100644 (file)
@@ -1300,6 +1300,18 @@ Do you want to refresh it now?</source>
                        <trans-unit id="error.invalidEmail">
                                <source>"%s" is not a valid e-mail address.</source>
                        </trans-unit>
+                       <trans-unit id="error.adminCanNotChangeSystemMaintainer">
+                               <source>Only system maintainers can change the admin flag and password of other system maintainers. The value has not been updated.</source>
+                       </trans-unit>
+                       <trans-unit id="formEngine.beUser.admin.information.userIsSystemMaintainer">
+                               <source>This user is a system maintainer</source>
+                       </trans-unit>
+                       <trans-unit id="formEngine.beUser.admin.information.userWillBecomeSystemMaintainer">
+                               <source>This user is in the list of allowed system maintainers and will gain system level access if enabling admin access.</source>
+                       </trans-unit>
+                       <trans-unit id="formEngine.beUser.admin.information.userAdminAndPasswordChangeNotAllowed">
+                               <source>This user is a system maintainer. Changing the admin flag and changing password is denied.</source>
+                       </trans-unit>
                        <trans-unit id="error.backendUserGroupListTypeError.header">
                                <source>Possible misconfiguration detected</source>
                        </trans-unit>
index fa9ddac..d2a3c65 100644 (file)
@@ -878,13 +878,31 @@ class SetupModuleController
     protected function getFieldsFromShowItem()
     {
         $allowedFields = GeneralUtility::trimExplode(',', $GLOBALS['TYPO3_USER_SETTINGS']['showitem'], true);
-        // do not ask for current password if admin (unknown for other users and no security gain)
         if ($this->isAdmin) {
+            // Do not ask for current password if admin (unknown for other users and no security gain)
             $key = array_search('passwordCurrent', $allowedFields);
             if ($key !== false) {
                 unset($allowedFields[$key]);
             }
         }
+
+        $backendUser = $this->getBackendUser();
+        $systemMaintainers = array_map('intval', $GLOBALS['TYPO3_CONF_VARS']['SYS']['systemMaintainers'] ?? []);
+        $isCurrentUserInSystemMaintainerList = in_array((int)$backendUser->user['uid'], $systemMaintainers, true);
+        $isInSimulateUserMode = (int)$backendUser->user['ses_backuserid'] !== 0;
+        if ($isInSimulateUserMode && $isCurrentUserInSystemMaintainerList) {
+            // DataHandler denies changing password of system maintainer users in switch user mode.
+            // Do not show the password fields is this case.
+            $key = array_search('password', $allowedFields);
+            if ($key !== false) {
+                unset($allowedFields[$key]);
+            }
+            $key = array_search('password2', $allowedFields);
+            if ($key !== false) {
+                unset($allowedFields[$key]);
+            }
+        }
+
         if (!is_array($this->tsFieldConf)) {
             return $allowedFields;
         }