[FEATURE] Add a hook to hide credentials in the Configuration module
[Packages/TYPO3.CMS.git] / typo3 / sysext / lowlevel / Classes / Controller / ConfigurationController.php
1 <?php
2 declare(strict_types=1);
3 namespace TYPO3\CMS\Lowlevel\Controller;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use Psr\Http\Message\ResponseInterface;
19 use Psr\Http\Message\ServerRequestInterface;
20 use TYPO3\CMS\Backend\Routing\Router;
21 use TYPO3\CMS\Backend\Template\ModuleTemplate;
22 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
23 use TYPO3\CMS\Core\Localization\LanguageService;
24 use TYPO3\CMS\Core\Utility\ArrayUtility;
25 use TYPO3\CMS\Core\Utility\GeneralUtility;
26 use TYPO3\CMS\Fluid\View\StandaloneView;
27 use TYPO3\CMS\Lowlevel\Utility\ArrayBrowser;
28
29 /**
30 * View configuration arrays in the backend
31 */
32 class ConfigurationController
33 {
34 /**
35 * Available trees to render.
36 * * label is an LLL identifier
37 * * type is used to identify the data source type
38 * * globalKey (only for type=global) is the name of a global variable
39 *
40 * @var array
41 */
42 protected $treeSetup = [
43 'confVars' => [
44 'label' => 'typo3ConfVars',
45 'type' => 'global',
46 'globalKey' => 'TYPO3_CONF_VARS',
47 ],
48 'tca' => [
49 'label' => 'tca',
50 'type' => 'global',
51 'globalKey' => 'TCA',
52 ],
53 'tcaDescr' => [
54 'label' => 'tcaDescr',
55 'type' => 'global',
56 'globalKey' => 'TCA_DESCR',
57 ],
58 'loadedExt' => [
59 'label' => 'loadedExt',
60 'type' => 'global',
61 'globalKey' => 'TYPO3_LOADED_EXT',
62 ],
63 'services' => [
64 'label' => 't3services',
65 'key' => 'services',
66 'type' => 'global',
67 'globalKey' => 'T3_SERVICES',
68 ],
69 'tbeModules' => [
70 'label' => 'tbemodules',
71 'type' => 'global',
72 'globalKey' => 'TBE_MODULES',
73 ],
74 'tbeModulesExt' => [
75 'label' => 'tbemodulesext',
76 'type' => 'global',
77 'globalKey' => 'TBE_MODULES_EXT',
78 ],
79 'tbeStyles' => [
80 'label' => 'tbeStyles',
81 'type' => 'global',
82 'globalKey' => 'TBE_STYLES',
83 ],
84 'userSettings' => [
85 'label' => 'usersettings',
86 'type' => 'global',
87 'globalKey' => 'TYPO3_USER_SETTINGS',
88 ],
89 'pagesTypes' => [
90 'label' => 'pagesTypes',
91 'type' => 'global',
92 'globalKey' => 'PAGES_TYPES',
93 ],
94 'beUserUc' => [
95 'label' => 'beUser',
96 'type' => 'uc',
97 ],
98 'beRoutes' => [
99 'label' => 'routes',
100 'type' => 'routes',
101 ],
102 ];
103
104 /**
105 * Blind configurations which should not be visible to mortal admins
106 *
107 * @var array
108 */
109 protected $blindedConfigurationOptions = [
110 'TYPO3_CONF_VARS' => [
111 'DB' => [
112 'database' => '******',
113 'host' => '******',
114 'password' => '******',
115 'port' => '******',
116 'socket' => '******',
117 'username' => '******',
118 'Connections' => [
119 'Default' => [
120 'dbname' => '******',
121 'host' => '******',
122 'password' => '******',
123 'port' => '******',
124 'user' => '******',
125 'unix_socket' => '******',
126 ],
127 ],
128 ],
129 'SYS' => [
130 'encryptionKey' => '******'
131 ],
132 ],
133 ];
134
135 /**
136 * Main controller action determines get/post values, takes care of
137 * stored backend user settings for this module, determines tree
138 * and renders it.
139 *
140 * @param ServerRequestInterface $request the current request
141 * @param ResponseInterface $response
142 * @return ResponseInterface the response with the content
143 * @throws \RuntimeException
144 */
145 public function mainAction(ServerRequestInterface $request, ResponseInterface $response)
146 {
147 $backendUser = $this->getBackendUser();
148 $languageService = $this->getLanguageService();
149
150 $queryParams = $request->getQueryParams();
151 $postValues = $request->getParsedBody();
152
153 $moduleState = $backendUser->uc['moduleData']['system_config'] ?? [];
154
155 // Determine validated tree key and tree detail setup
156 $selectedTreeKey = $this->treeSetup[$queryParams['tree']] ? $queryParams['tree']
157 : ($this->treeSetup[$moduleState['tree']] ? $moduleState['tree'] : key($this->treeSetup));
158 $selectedTreeDetails = $this->treeSetup[$selectedTreeKey];
159 $moduleState['tree'] = $selectedTreeKey;
160
161 // Search string given or regex search enabled?
162 $searchString = (string)($postValues['searchString'] ? trim($postValues['searchString']) : '');
163 $moduleState['regexSearch'] = (bool)($postValues['regexSearch'] ?? $moduleState['regexSearch'] ?? false);
164
165 // Prepare main array
166 if ($selectedTreeDetails['type'] === 'global') {
167 $globalArrayKey = $selectedTreeDetails['globalKey'];
168 $renderArray = $GLOBALS[$globalArrayKey];
169
170 // Hook for Processing blindedConfigurationOptions
171 $blindedConfigurationOptions = $this->blindedConfigurationOptions;
172
173 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS'][self::class]['modifyBlindedConfigurationOptions'] ?? [] as $classReference) {
174 $processingObject = GeneralUtility::makeInstance($classReference);
175 $blindedConfigurationOptions = $processingObject->modifyBlindedConfigurationOptions($blindedConfigurationOptions, $this);
176 }
177
178 if (isset($blindedConfigurationOptions[$globalArrayKey])) {
179 // Prepare blinding for all database connection types
180 foreach (array_keys($GLOBALS['TYPO3_CONF_VARS']['DB']['Connections']) as $connectionName) {
181 if ($connectionName !== 'Default') {
182 $blindedConfigurationOptions['TYPO3_CONF_VARS']['DB']['Connections'][$connectionName] =
183 $blindedConfigurationOptions['TYPO3_CONF_VARS']['DB']['Connections']['Default'];
184 }
185 }
186 ArrayUtility::mergeRecursiveWithOverrule(
187 $renderArray,
188 ArrayUtility::intersectRecursive($blindedConfigurationOptions[$globalArrayKey], $renderArray)
189 );
190 }
191 } elseif ($selectedTreeDetails['type'] === 'uc') {
192 $renderArray = $backendUser->uc;
193 } elseif ($selectedTreeDetails['type'] === 'routes') {
194 $router = GeneralUtility::makeInstance(Router::class);
195 $routes = $router->getRoutes();
196 $renderArray = [];
197 foreach ($routes as $identifier => $route) {
198 /** @var $route \TYPO3\CMS\Backend\Routing\Route */
199 $renderArray[$identifier] = [
200 'path' => $route->getPath(),
201 'options' => $route->getOptions()
202 ];
203 }
204 } else {
205 throw new \RuntimeException('Unknown array type "' . $selectedTreeDetails['type'] . '"', 1507845662);
206 }
207 ArrayUtility::naturalKeySortRecursive($renderArray);
208
209 // Prepare array renderer class, apply search and expand / collapse states
210 $arrayBrowser = GeneralUtility::makeInstance(ArrayBrowser::class);
211 $arrayBrowser->dontLinkVar = true;
212 $arrayBrowser->searchKeysToo = true;
213 $arrayBrowser->regexMode = $moduleState['regexSearch'];
214 $node = $queryParams['node'];
215 if ($searchString) {
216 $arrayBrowser->depthKeys = $arrayBrowser->getSearchKeys($renderArray, '', $searchString, []);
217 } elseif (is_array($node)) {
218 $newExpandCollapse = $arrayBrowser->depthKeys($node, $moduleState['node_' . $selectedTreeKey]);
219 $arrayBrowser->depthKeys = $newExpandCollapse;
220 $moduleState['node_' . $selectedTreeKey] = $newExpandCollapse;
221 } else {
222 $arrayBrowser->depthKeys = $moduleState['node_' . $selectedTreeKey] ?? [];
223 }
224
225 // Store new state
226 $backendUser->uc['moduleData']['system_config'] = $moduleState;
227 $backendUser->writeUC();
228
229 // Render main body
230 $view = GeneralUtility::makeInstance(StandaloneView::class);
231 $view->getRequest()->setControllerExtensionName('lowlevel');
232 $view->setTemplatePathAndFilename(GeneralUtility::getFileAbsFileName(
233 'EXT:lowlevel/Resources/Private/Templates/Backend/Configuration.html'
234 ));
235 $view->assignMultiple([
236 'treeName' => $selectedTreeDetails['label'],
237 'searchString' => $searchString,
238 'regexSearch' => $moduleState['regexSearch'],
239 'tree' => $arrayBrowser->tree($renderArray, ''),
240 ]);
241
242 // Prepare module setup
243 $moduleTemplate = GeneralUtility::makeInstance(ModuleTemplate::class);
244 $moduleTemplate->setContent($view->render());
245 $moduleTemplate->getPageRenderer()->loadRequireJsModule('TYPO3/CMS/Lowlevel/ConfigurationView');
246
247 // Shortcut in doc header
248 $shortcutButton = $moduleTemplate->getDocHeaderComponent()->getButtonBar()->makeShortcutButton();
249 $shortcutButton->setModuleName('system_config')
250 ->setDisplayName($languageService->sL(
251 'LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:' . $selectedTreeDetails['label']
252 ))
253 ->setSetVariables(['tree']);
254 $moduleTemplate->getDocHeaderComponent()->getButtonBar()->addButton($shortcutButton);
255
256 // Main drop down in doc header
257 $menu = $moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->makeMenu();
258 $menu->setIdentifier('tree');
259 foreach ($this->treeSetup as $treeKey => $treeDetails) {
260 $menuItem = $menu->makeMenuItem();
261 /** @var \TYPO3\CMS\Backend\Routing\UriBuilder $uriBuilder */
262 $uriBuilder = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Routing\UriBuilder::class);
263 $menuItem->setHref((string)$uriBuilder->buildUriFromRoute('system_config', ['tree' => $treeKey]))
264 ->setTitle($languageService->sL(
265 'LLL:EXT:lowlevel/Resources/Private/Language/locallang.xlf:' . $treeDetails['label']
266 ));
267 if ($selectedTreeKey === $treeKey) {
268 $menuItem->setActive(true);
269 }
270 $menu->addMenuItem($menuItem);
271 }
272 $moduleTemplate->getDocHeaderComponent()->getMenuRegistry()->addMenu($menu);
273
274 $response->getBody()->write($moduleTemplate->renderContent());
275 return $response;
276 }
277
278 /**
279 * Returns the Backend User
280 * @return BackendUserAuthentication
281 */
282 protected function getBackendUser()
283 {
284 return $GLOBALS['BE_USER'];
285 }
286
287 /**
288 * @return LanguageService
289 */
290 protected function getLanguageService()
291 {
292 return $GLOBALS['LANG'];
293 }
294 }