[TASK] Remove unneeded parenthesis on array-access
[Packages/TYPO3.CMS.git] / typo3 / sysext / saltedpasswords / Classes / Utility / ExtensionManagerConfigurationUtility.php
1 <?php
2 namespace TYPO3\CMS\Saltedpasswords\Utility;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Messaging\FlashMessage;
18 use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
19 use TYPO3\CMS\Core\Utility\GeneralUtility;
20
21 /**
22 * class providing configuration checks for saltedpasswords.
23 * @since 2009-09-04
24 */
25 class ExtensionManagerConfigurationUtility
26 {
27 /**
28 * @var int
29 */
30 protected $errorType = FlashMessage::OK;
31
32 /**
33 * @var string
34 */
35 protected $header;
36
37 /**
38 * @var string
39 */
40 protected $preText;
41
42 /**
43 * @var array
44 */
45 protected $problems = array();
46
47 /**
48 * @var array
49 */
50 protected $extConf = array();
51
52 /**
53 * Set the error level if no higher level
54 * is set already
55 *
56 * @param string $level One out of error, ok, warning, info
57 * @return void
58 */
59 protected function setErrorLevel($level)
60 {
61 $lang = $this->getLanguageService();
62 switch ($level) {
63 case 'error':
64 $this->errorType = FlashMessage::ERROR;
65 $this->header = $lang->getLL('ext.saltedpasswords.configuration.header.errorsFound');
66 $this->preText = $lang->getLL('ext.saltedpasswords.configuration.message.errorsFound') . '<br />';
67 break;
68 case 'warning':
69 if ($this->errorType < FlashMessage::ERROR) {
70 $this->errorType = FlashMessage::WARNING;
71 $this->header = $lang->getLL('ext.saltedpasswords.configuration.header.warningsFound');
72 $this->preText = $lang->getLL('ext.saltedpasswords.configuration.message.warningsFound') . '<br />';
73 }
74 break;
75 case 'info':
76 if ($this->errorType < FlashMessage::WARNING) {
77 $this->errorType = FlashMessage::INFO;
78 $this->header = $lang->getLL('ext.saltedpasswords.configuration.header.additionalInformation');
79 $this->preText = '<br />';
80 }
81 break;
82 case 'ok':
83 // @todo Remove INFO condition as it has lower importance
84 if ($this->errorType < FlashMessage::WARNING && $this->errorType != FlashMessage::INFO) {
85 $this->errorType = FlashMessage::OK;
86 $this->header = $lang->getLL('ext.saltedpasswords.configuration.header.noErrorsFound');
87 $this->preText = $lang->getLL('ext.saltedpasswords.configuration.message.noErrorsFound') . '<br />';
88 }
89 break;
90 default:
91 }
92 }
93
94 /**
95 * Renders the messages if problems have been found.
96 *
97 * @return array an array with errorType and html code
98 */
99 protected function renderMessage()
100 {
101 $message = '';
102 // If there are problems, render them into an unordered list
103 if (!empty($this->problems)) {
104 $message = '<ul><li>###PROBLEMS###</li></ul>';
105 $message = str_replace('###PROBLEMS###', implode('<br />&nbsp;</li><li>', $this->problems), $message);
106 if ($this->errorType > FlashMessage::OK) {
107 $message .= '<br />' .
108 $this->getLanguageService()->getLL('ext.saltedpasswords.configuration.message.securityWarning');
109 }
110 }
111 if (empty($message)) {
112 $this->setErrorLevel('ok');
113 }
114 $message = $this->preText . $message;
115
116 $class = 'default';
117 switch ($this->errorType) {
118 case FlashMessage::NOTICE:
119 $class = 'notice';
120 break;
121 case FlashMessage::INFO:
122 $class = 'info';
123 break;
124 case FlashMessage::OK:
125 $class = 'success';
126 break;
127 case FlashMessage::WARNING;
128 $class = 'warning';
129 break;
130 case FlashMessage::ERROR:
131 $class = 'danger';
132 break;
133 default:
134 }
135 $html = '<div class="panel panel-' . $class . '">' .
136 '<div class="panel-heading">' . $this->header . '</div>' .
137 '<div class="panel-body">' . $message . '</div>' .
138 '</div>';
139 return array(
140 'errorType' => $this->errorType,
141 'html' => $html
142 );
143 }
144
145 /**
146 * Initializes this object.
147 *
148 * @return void
149 */
150 private function init()
151 {
152 $requestSetup = $this->processPostData((array) $_REQUEST['data']);
153 $extConf = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['saltedpasswords']);
154 $this->extConf['BE'] = array_merge((array)$extConf['BE.'], (array)$requestSetup['BE.']);
155 $this->extConf['FE'] = array_merge((array)$extConf['FE.'], (array)$requestSetup['FE.']);
156 $this->getLanguageService()->includeLLFile('EXT:saltedpasswords/Resources/Private/Language/locallang.xlf');
157 }
158
159 /**
160 * Checks the backend configuration and shows a message if necessary.
161 * The method returns an array or the HTML code depends on
162 * $params['propertyName'] is set or not.
163 *
164 * @param array $params Field information to be rendered
165 * @param \TYPO3\CMS\Core\TypoScript\ConfigurationForm $pObj The calling parent object.
166 * @return array|string array with errorType and HTML or only the HTML as string
167 */
168 public function checkConfigurationBackend(array $params, $pObj)
169 {
170 $this->init();
171 $extConf = $this->extConf['BE'];
172 // The backend is called over SSL
173 $isBackendCalledOverSsl = (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSL'] > 0;
174 $rsaAuthLoaded = ExtensionManagementUtility::isLoaded('rsaauth');
175 // SSL configured?
176 $lang = $this->getLanguageService();
177 if ($isBackendCalledOverSsl) {
178 $this->setErrorLevel('ok');
179 $problems[] = $lang->getLL('ext.saltedpasswords.configuration.message.backendSsl');
180 } elseif ($rsaAuthLoaded) {
181 $loginSecurityLevel = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel']) ?: 'normal';
182 if ($loginSecurityLevel === 'rsa') {
183 if ($this->isRsaAuthBackendAvailable()) {
184 $this->setErrorLevel('ok');
185 $problems[] = $lang->getLL('ext.saltedpasswords.configuration.message.backendRsa');
186 } else {
187 // This means that login would fail because rsaauth is not working properly
188 $this->setErrorLevel('error');
189 $problems[] = '<strong>' .
190 $lang->getLL('ext.saltedpasswords.configuration.message.openSslMissing') .
191 '<a href="http://php.net/manual/en/openssl.installation.php" target="_blank">PHP.net</a></strong>.';
192 }
193 } else {
194 // This means that rsaauth is enabled but not used
195 $this->setErrorLevel('warning');
196 $problems[] = $lang->getLL('ext.saltedpasswords.configuration.message.backendSecurityLevelNotRsa');
197 }
198 } else {
199 // This means that we don't use any encryption method
200 $this->setErrorLevel('warning');
201 $problems[] = $lang->getLL('ext.saltedpasswords.configuration.message.rsaInstructionsIntro') . '<br />
202 <ul>
203 <li>' . $lang->getLL('ext.saltedpasswords.configuration.message.rsaInstructionsFirstItem') . '</li>
204
205 <li>' . nl2br($lang->getLL('ext.saltedpasswords.configuration.message.rsaInstructionsSecondItem')) .
206 '</li>
207 </ul>
208 <br />
209 ' . $lang->getLL('ext.saltedpasswords.configuration.message.rsaInstructionsFootnote');
210 }
211 // Only saltedpasswords as authsservice
212 if ($extConf['onlyAuthService']) {
213 // Warn user that the combination with "forceSalted" may lock him
214 // out from Backend
215 if ($extConf['forceSalted']) {
216 $this->setErrorLevel('warning');
217 $problems[] = $lang->getLL('ext.saltedpasswords.configuration.message.warningForceSalted') . '<br />
218 <strong><i>' . $lang->getLL('ext.saltedpasswords.configuration.label.warning') . '</i></strong> ' .
219 $lang->getLL('ext.saltedpasswords.configuration.message.warningForceSaltedNoteForBackend');
220 } else {
221 // Inform the user that things like openid won't work anymore
222 $this->setErrorLevel('info');
223 $problems[] = $lang->getLL('ext.saltedpasswords.configuration.message.infoOnlyBackendAuthService');
224 }
225 }
226 // forceSalted is set
227 if ($extConf['forceSalted'] && !$extConf['onlyAuthService']) {
228 $this->setErrorLevel('info');
229 $problems[] = $lang->getLL('ext.saltedpasswords.configuration.message.infoForceSalted') .
230 ' <br /> ' . $lang->getLL('ext.saltedpasswords.configuration.message.infoForceSaltedNote');
231 }
232 // updatePasswd wont work with "forceSalted"
233 if ($extConf['updatePasswd'] && $extConf['forceSalted']) {
234 $this->setErrorLevel('error');
235 $problems[] = $lang->getLL('ext.saltedpasswords.configuration.message.errorForceSaltedAndUpdatePassword') .
236 '<br /> ' .
237 $lang->getLL('ext.saltedpasswords.configuration.message.errorForceSaltedAndUpdatePasswordReason');
238 }
239 // Check if the configured hash-method is available on system
240 $instance = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance(null, 'BE');
241 if ($instance === null || !$instance->isAvailable()) {
242 $this->setErrorLevel('error');
243 $problems[] = $lang->getLL('ext.saltedpasswords.configuration.message.errorHashMethodNotAvailable');
244 }
245 $this->problems = $problems;
246 $result = $this->renderMessage();
247 if (!empty($params['propertyName'])) {
248 return $result['html'];
249 }
250 return $result;
251 }
252
253 /**
254 * Checks if rsaauth is able to obtain a backend
255 *
256 * @return bool
257 */
258 protected function isRsaAuthBackendAvailable()
259 {
260 // Try to instantiate an RSAauth backend. If this does not work,
261 // it means that OpenSSL is not usable
262 /** @var \TYPO3\CMS\Rsaauth\Backend\BackendFactory $rsaauthBackendFactory */
263 $rsaauthBackendFactory = GeneralUtility::makeInstance(\TYPO3\CMS\Rsaauth\Backend\BackendFactory::class);
264 $backend = $rsaauthBackendFactory->getBackend();
265 return $backend !== null;
266 }
267
268 /**
269 * Checks the frontend configuration and shows a message if necessary.
270 * The method returns an array or the HTML code depends on
271 * $params['propertyName'] is set or not.
272 *
273 * @param array $params Field information to be rendered
274 * @param \TYPO3\CMS\Core\TypoScript\ConfigurationForm $pObj The calling parent object.
275 * @return array|string array with errorType and HTML or only the HTML as string
276 */
277 public function checkConfigurationFrontend(array $params, $pObj)
278 {
279 $this->init();
280 $extConf = $this->extConf['FE'];
281 $problems = array();
282 $lang = $this->getLanguageService();
283 if ($extConf['enabled']) {
284 $loginSecurityLevel = trim($GLOBALS['TYPO3_CONF_VARS']['FE']['loginSecurityLevel']) ?: 'normal';
285 if (!GeneralUtility::inList('normal,rsa', $loginSecurityLevel)) {
286 $this->setErrorLevel('info');
287 $problems[] = '<strong>' . $lang->getLL('ext.saltedpasswords.configuration.label.important') .
288 '</strong><br /> ' .
289 $lang->getLL('ext.saltedpasswords.configuration.message.infoLoginSecurityLevelDifferent') .
290 '<br />
291 <ul>
292 <li>' .
293 $lang->getLL('ext.saltedpasswords.configuration.message.infoLoginSecurityLevelDifferentFirstItem') .
294 '</li>
295
296 <li>' .
297 $lang->getLL('ext.saltedpasswords.configuration.message.infoLoginSecurityLevelDifferentSecondItem') .
298 '</li>
299 </ul>
300 <br />
301 ' . $lang->getLL('ext.saltedpasswords.configuration.message.infoLoginSecurityLevelDifferentNote');
302 } elseif ($loginSecurityLevel === 'rsa') {
303 if (ExtensionManagementUtility::isLoaded('rsaauth')) {
304 if ($this->isRsaAuthBackendAvailable()) {
305 $this->setErrorLevel('ok');
306 $problems[] = $lang->getLL('ext.saltedpasswords.configuration.message.okFeRsaauthLoaded');
307 } else {
308 // This means that login would fail because rsaauth is not working properly
309 $this->setErrorLevel('error');
310 $problems[] = '<strong>' . $lang->getLL('ext.saltedpasswords.configuration.message.openSslMissing') .
311 ' <a href="http://php.net/manual/en/openssl.installation.php" target="_blank">PHP.net</a></strong>.';
312 }
313 } else {
314 // Rsaauth is not installed but configured to be used
315 $this->setErrorLevel('warning');
316 $problems[] = $lang->getLL('ext.saltedpasswords.configuration.message.warningRsaauthNotInstalledButConfigured');
317 }
318 }
319 // Only saltedpasswords as authsservice
320 if ($extConf['onlyAuthService']) {
321 // Warn user that the combination with "forceSalted" may lock
322 // him out from frontend
323 if ($extConf['forceSalted']) {
324 $this->setErrorLevel('warning');
325 $problems[] = nl2br($lang->getLL('ext.saltedpasswords.configuration.message.infoForceSalted')) .
326 '<strong><i>' . $lang->getLL('ext.saltedpasswords.configuration.label.important') .
327 '</i></strong> ' . $lang->getLL('ext.saltedpasswords.configuration.message.warningForceSaltedNoteForFrontend');
328 } else {
329 // Inform the user that things like openid won't work anymore
330 $this->setErrorLevel('info');
331 $problems[] = $lang->getLL('ext.saltedpasswords.configuration.message.infoOnlyFrontendAuthService');
332 }
333 }
334 // forceSalted is set
335 if ($extConf['forceSalted'] && !$extConf['onlyAuthService']) {
336 $this->setErrorLevel('warning');
337 $problems[] = nl2br($lang->getLL('ext.saltedpasswords.configuration.message.infoForceSalted')) .
338 '<strong><i>' . $lang->getLL('ext.saltedpasswords.configuration.label.important') .
339 '</i></strong> ' . $lang->getLL('ext.saltedpasswords.configuration.message.warningForceSaltedNote2');
340 }
341 // updatePasswd wont work with "forceSalted"
342 if ($extConf['updatePasswd'] && $extConf['forceSalted']) {
343 $this->setErrorLevel('error');
344 $problems[] = nl2br($lang->getLL('ext.saltedpasswords.configuration.message.errorForceSaltedAndUpdatePassword'));
345 }
346 } else {
347 // Not enabled warning
348 $this->setErrorLevel('info');
349 $problems[] = $lang->getLL('ext.saltedpasswords.configuration.message.infoSaltedpasswordsFrontendDisabled');
350 }
351 $this->problems = $problems;
352 $result = $this->renderMessage();
353 if (!empty($params['propertyName'])) {
354 return $result['html'];
355 }
356 return $result;
357 }
358
359 /**
360 * Renders a selector element that allows to select the hash method to be used.
361 *
362 * @param array $params Field information to be rendered
363 * @param \TYPO3\CMS\Core\TypoScript\ConfigurationForm $pObj The calling parent object.
364 * @param string $disposal The configuration disposal ('FE' or 'BE')
365 * @return string The HTML selector
366 */
367 protected function buildHashMethodSelector(array $params, $pObj, $disposal)
368 {
369 $this->init();
370 $propertyName = $params['propertyName'];
371 $unknownVariablePleaseRenameMe = '\'' . substr(md5($propertyName), 0, 10) . '\'';
372 $pField = '';
373 $registeredMethods = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getRegisteredSaltedHashingMethods();
374 foreach ($registeredMethods as $class => $reference) {
375 $classInstance = GeneralUtility::getUserObj($reference);
376 if ($classInstance instanceof \TYPO3\CMS\Saltedpasswords\Salt\SaltInterface && $classInstance->isAvailable()) {
377 $sel = $this->extConf[$disposal]['saltedPWHashingMethod'] == $class ? ' selected="selected" ' : '';
378 $label = 'ext.saltedpasswords.title.' . strtolower(end(explode('\\', $class)));
379 $pField .= '<option value="' . htmlspecialchars($class) . '"' . $sel . '>' . $GLOBALS['LANG']->getLL($label) . '</option>';
380 }
381 }
382 $pField = '<select id="' . $propertyName . '" name="' . $params['fieldName'] .
383 '" onChange="uFormUrl(' . $unknownVariablePleaseRenameMe . ')">' . $pField . '</select>';
384 return $pField;
385 }
386
387 /**
388 * Renders a selector element that allows to select the hash method to be
389 * used (frontend disposal).
390 *
391 * @param array $params Field information to be rendered
392 * @param \TYPO3\CMS\Core\TypoScript\ConfigurationForm $pObj The calling parent object.
393 * @return string The HTML selector
394 */
395 public function buildHashMethodSelectorFE(array $params, $pObj)
396 {
397 return $this->buildHashMethodSelector($params, $pObj, 'FE');
398 }
399
400 /**
401 * Renders a selector element that allows to select the hash method to
402 * be used (backend disposal)
403 *
404 * @param array $params Field information to be rendered
405 * @param \TYPO3\CMS\Core\TypoScript\ConfigurationForm $pObj The calling parent object.
406 * @return string The HTML selector
407 */
408 public function buildHashMethodSelectorBE(array $params, $pObj)
409 {
410 return $this->buildHashMethodSelector($params, $pObj, 'BE');
411 }
412
413 /**
414 * Processes the information submitted by the user using a POST request and
415 * transforms it to a TypoScript node notation.
416 *
417 * @param array $postArray Incoming POST information
418 * @return array Processed and transformed POST information
419 */
420 protected function processPostData(array $postArray = array())
421 {
422 foreach ($postArray as $key => $value) {
423 // @todo Explain
424 $parts = explode('.', $key, 2);
425 if (count($parts) == 2) {
426 // @todo Explain
427 $value = $this->processPostData(array($parts[1] => $value));
428 $postArray[$parts[0] . '.'] = array_merge((array)$postArray[$parts[0] . '.'], $value);
429 } else {
430 // @todo Explain
431 $postArray[$parts[0]] = $value;
432 }
433 }
434 return $postArray;
435 }
436
437 /**
438 * @return \TYPO3\CMS\Lang\LanguageService
439 */
440 protected function getLanguageService()
441 {
442 return $GLOBALS['LANG'];
443 }
444 }