dbaca5da8c49c85650a41dddc69dcd86020a1184
[Packages/TYPO3.CMS.git] / typo3 / sysext / backend / Classes / FrontendBackendUserAuthentication.php
1 <?php
2 namespace TYPO3\CMS\Backend;
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\Backend\Utility\BackendUtility;
18 use TYPO3\CMS\Core\Authentication\BackendUserAuthentication;
19 use TYPO3\CMS\Core\Context\Context;
20 use TYPO3\CMS\Core\Context\LanguageAspect;
21 use TYPO3\CMS\Core\Database\ConnectionPool;
22 use TYPO3\CMS\Core\Database\Query\QueryBuilder;
23 use TYPO3\CMS\Core\Database\Query\QueryHelper;
24 use TYPO3\CMS\Core\Database\Query\Restriction\DeletedRestriction;
25 use TYPO3\CMS\Core\Localization\LanguageService;
26 use TYPO3\CMS\Core\Type\Bitmask\Permission;
27 use TYPO3\CMS\Core\Utility\GeneralUtility;
28
29 /**
30 * TYPO3 backend user authentication in the TSFE frontend.
31 * This includes mainly functions related to the Admin Panel
32 */
33 class FrontendBackendUserAuthentication extends BackendUserAuthentication
34 {
35
36 /**
37 * Form field with login name.
38 *
39 * @var string
40 */
41 public $formfield_uname = '';
42
43 /**
44 * Form field with password.
45 *
46 * @var string
47 */
48 public $formfield_uident = '';
49
50 /**
51 * Formfield_status should be set to "". The value this->formfield_status is set to empty in order to
52 * disable login-attempts to the backend account through this script
53 *
54 * @var string
55 */
56 public $formfield_status = '';
57
58 /**
59 * Decides if the writelog() function is called at login and logout.
60 *
61 * @var bool
62 */
63 public $writeStdLog = false;
64
65 /**
66 * If the writelog() functions is called if a login-attempt has be tried without success.
67 *
68 * @var bool
69 */
70 public $writeAttemptLog = false;
71
72 /**
73 * General flag which is set if the adminpanel is enabled at all.
74 *
75 * @var bool
76 * @deprecated since TYPO3 v9, property will be removed in TYPO3 v10 - see extension "adminpanel" for new API
77 */
78 public $extAdmEnabled = false;
79
80 /**
81 * @var \TYPO3\CMS\Adminpanel\View\AdminPanelView Instance of admin panel
82 * @deprecated since TYPO3 v9, property will be removed in TYPO3 v10 - see extension "adminpanel" for new API
83 */
84 public $adminPanel;
85
86 /**
87 * @var \TYPO3\CMS\Core\FrontendEditing\FrontendEditingController
88 * @deprecated since TYPO3 v9, property will be removed in TYPO3 v10 - see extension "feedit" how the functionality could be used.
89 */
90 public $frontendEdit;
91
92 /**
93 * @var array
94 * @deprecated since TYPO3 v9, property will be removed in TYPO3 v10 - see extension "adminpanel" for new API
95 */
96 public $extAdminConfig = [];
97
98 /**
99 * Initializes the admin panel.
100 *
101 * @deprecated since TYPO3 v9 - rewritten as middleware
102 */
103 public function initializeAdminPanel()
104 {
105 trigger_error('Method will be removed in TYPO3 v10 - initialization is done via middleware.', E_USER_DEPRECATED);
106 }
107
108 /**
109 * Initializes frontend editing.
110 *
111 * @deprecated since TYPO3 v9 - rewritten as middleware
112 */
113 public function initializeFrontendEdit()
114 {
115 trigger_error('Method will be removed in TYPO3 v10 - initialization is done via middleware.', E_USER_DEPRECATED);
116 }
117
118 /**
119 * Determines whether frontend editing is currently active.
120 *
121 * @deprecated since TYPO3 v9 - see ext "feedit" for API
122 * @return bool Whether frontend editing is active
123 */
124 public function isFrontendEditingActive()
125 {
126 trigger_error('Method will be removed in TYPO3 v10 - use underlying TSFE directly.', E_USER_DEPRECATED);
127 return $this->extAdmEnabled && (
128 $this->adminPanel->isAdminModuleEnabled('edit') ||
129 (int)$GLOBALS['TSFE']->displayEditIcons === 1 ||
130 (int)$GLOBALS['TSFE']->displayFieldEditIcons === 1
131 );
132 }
133
134 /**
135 * Delegates to the appropriate view and renders the admin panel content.
136 *
137 * @deprecated since TYPO3 v9 - see ext "adminpanel" for new API
138 * @return string.
139 */
140 public function displayAdminPanel()
141 {
142 trigger_error('Method will be removed in TYPO3 v10 - use MainController of adminpanel extension.', E_USER_DEPRECATED);
143 return $this->adminPanel->display();
144 }
145
146 /**
147 * Determines whether the admin panel is enabled and visible.
148 *
149 * @deprecated since TYPO3 v9 - see ext "adminpanel" for new API
150 * @return bool true if the admin panel is enabled and visible
151 */
152 public function isAdminPanelVisible()
153 {
154 trigger_error('Method will be removed in TYPO3 v10 - use new adminpanel API instead.', E_USER_DEPRECATED);
155 return $this->extAdmEnabled && !$this->extAdminConfig['hide'] && $GLOBALS['TSFE']->config['config']['admPanel'];
156 }
157
158 /*****************************************************
159 *
160 * TSFE BE user Access Functions
161 *
162 ****************************************************/
163 /**
164 * Implementing the access checks that the TYPO3 CMS bootstrap script does before a user is ever logged in.
165 * Used in the frontend.
166 *
167 * @return bool Returns TRUE if access is OK
168 */
169 public function checkBackendAccessSettingsFromInitPhp()
170 {
171 // Check Hardcoded lock on BE
172 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'] < 0) {
173 return false;
174 }
175 // Check IP
176 if (trim($GLOBALS['TYPO3_CONF_VARS']['BE']['IPmaskList'])) {
177 if (!GeneralUtility::cmpIP(GeneralUtility::getIndpEnv('REMOTE_ADDR'), $GLOBALS['TYPO3_CONF_VARS']['BE']['IPmaskList'])) {
178 return false;
179 }
180 }
181 // Check IP mask based on TSconfig
182 if (!$this->checkLockToIP()) {
183 return false;
184 }
185 // Check SSL (https)
186 if ((bool)$GLOBALS['TYPO3_CONF_VARS']['BE']['lockSSL'] && !GeneralUtility::getIndpEnv('TYPO3_SSL')) {
187 return false;
188 }
189 // Finally a check as in BackendUserAuthentication::backendCheckLogin()
190 return $this->isUserAllowedToLogin();
191 }
192
193 /**
194 * Evaluates if the Backend User has read access to the input page record.
195 * The evaluation is based on both read-permission and whether the page is found in one of the users webmounts.
196 * Only if both conditions match, will the function return TRUE.
197 *
198 * Read access means that previewing is allowed etc.
199 *
200 * Used in \TYPO3\CMS\Frontend\Http\RequestHandler
201 *
202 * @param array $pageRec The page record to evaluate for
203 * @return bool TRUE if read access
204 */
205 public function extPageReadAccess($pageRec)
206 {
207 return $this->isInWebMount($pageRec['uid']) && $this->doesUserHaveAccess($pageRec, Permission::PAGE_SHOW);
208 }
209
210 /*****************************************************
211 *
212 * TSFE BE user Access Functions
213 *
214 ****************************************************/
215 /**
216 * Generates a list of Page-uid's from $id. List does not include $id itself
217 * The only pages excluded from the list are deleted pages.
218 *
219 * @param int $id Start page id
220 * @param int $depth Depth to traverse down the page tree.
221 * @param int $begin Is an optional integer that determines at which level in the tree to start collecting uid's. Zero means 'start right away', 1 = 'next level and out'
222 * @param string $perms_clause Perms clause
223 * @return string Returns the list with a comma in the end (if any pages selected!)
224 */
225 public function extGetTreeList($id, $depth, $begin = 0, $perms_clause)
226 {
227 /** @var QueryBuilder $queryBuilder */
228 $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
229 ->getQueryBuilderForTable('pages');
230
231 $queryBuilder->getRestrictions()
232 ->removeAll()
233 ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
234
235 $depth = (int)$depth;
236 $begin = (int)$begin;
237 $id = (int)$id;
238 $theList = '';
239 if ($id && $depth > 0) {
240 $result = $queryBuilder
241 ->select('uid', 'title')
242 ->from('pages')
243 ->where(
244 $queryBuilder->expr()->eq('pid', $queryBuilder->createNamedParameter($id, \PDO::PARAM_INT)),
245 QueryHelper::stripLogicalOperatorPrefix($perms_clause)
246 )
247 ->execute();
248 while ($row = $result->fetch()) {
249 if ($begin <= 0) {
250 $theList .= $row['uid'] . ',';
251 }
252 if ($depth > 1) {
253 $theList .= $this->extGetTreeList($row['uid'], $depth - 1, $begin - 1, $perms_clause);
254 }
255 }
256 }
257 return $theList;
258 }
259
260 /**
261 * Edit Access
262 */
263
264 /**
265 * Checks whether the user has access to edit the language for the
266 * requested record.
267 *
268 * @param string $table The name of the table.
269 * @param array $currentRecord The record.
270 * @return bool
271 */
272 public function allowedToEditLanguage($table, array $currentRecord): bool
273 {
274 // If no access right to record languages, return immediately
275 /** @var LanguageAspect $languageAspect */
276 $languageAspect = GeneralUtility::makeInstance(Context::class)->getAspect('language');
277 if ($table === 'pages') {
278 $languageId = $languageAspect->getId();
279 } elseif ($table === 'tt_content') {
280 $languageId = $languageAspect->getContentId();
281 } elseif ($GLOBALS['TCA'][$table]['ctrl']['languageField']) {
282 $languageId = $currentRecord[$GLOBALS['TCA'][$table]['ctrl']['languageField']];
283 } else {
284 $languageId = -1;
285 }
286 return $this->checkLanguageAccess($languageId);
287 }
288
289 /**
290 * Checks whether the user is allowed to edit the requested table.
291 *
292 * @param string $table The name of the table.
293 * @param array $dataArray The data array.
294 * @param array $conf The configuration array for the edit panel.
295 * @param bool $checkEditAccessInternals Boolean indicating whether recordEditAccessInternals should not be checked. Defaults
296 * @return bool
297 */
298 public function allowedToEdit(string $table, array $dataArray, array $conf, bool $checkEditAccessInternals): bool
299 {
300 // Unless permissions specifically allow it, editing is not allowed.
301 $mayEdit = false;
302 if ($checkEditAccessInternals) {
303 $editAccessInternals = $this->recordEditAccessInternals($table, $dataArray, false, false);
304 } else {
305 $editAccessInternals = true;
306 }
307 if ($editAccessInternals) {
308 if ($table === 'pages') {
309 if ($this->isAdmin() || $this->doesUserHaveAccess($dataArray, Permission::PAGE_EDIT)) {
310 $mayEdit = true;
311 }
312 } else {
313 if ($this->isAdmin() || $this->doesUserHaveAccess(BackendUtility::getRecord('pages', $dataArray['pid']), Permission::CONTENT_EDIT)) {
314 $mayEdit = true;
315 }
316 }
317 if (!$conf['onlyCurrentPid'] || $dataArray['pid'] == $GLOBALS['TSFE']->id) {
318 // Permissions:
319 $perms = $this->calcPerms($GLOBALS['TSFE']->page);
320 if ($table === 'pages') {
321 $allow = $this->getAllowedEditActions($table, $conf, $dataArray['pid']);
322 // Can only display editbox if there are options in the menu
323 if (!empty($allow)) {
324 $mayEdit = true;
325 }
326 } else {
327 $types = GeneralUtility::trimExplode(',', strtolower($conf['allow']), true);
328 $allow = array_flip($types);
329 $mayEdit = !empty($allow) && $perms & Permission::CONTENT_EDIT;
330 }
331 }
332 }
333 return $mayEdit;
334 }
335
336 /**
337 * Takes an array of generally allowed actions and filters that list based on page and content permissions.
338 *
339 * @param string $table The name of the table.
340 * @param array $conf The configuration array.
341 * @param int $pid The PID where editing will occur.
342 * @return array
343 */
344 public function getAllowedEditActions($table, array $conf, $pid): array
345 {
346 $types = GeneralUtility::trimExplode(',', strtolower($conf['allow']), true);
347 $allow = array_flip($types);
348 if (!$conf['onlyCurrentPid'] || $pid == $GLOBALS['TSFE']->id) {
349 // Permissions
350 $types = GeneralUtility::trimExplode(',', strtolower($conf['allow']), true);
351 $allow = array_flip($types);
352 $perms = $this->calcPerms($GLOBALS['TSFE']->page);
353 if ($table === 'pages') {
354 // Rootpage
355 if (count($GLOBALS['TSFE']->config['rootLine']) === 1) {
356 unset($allow['move']);
357 unset($allow['hide']);
358 unset($allow['delete']);
359 }
360 if (!($perms & Permission::PAGE_EDIT) || !$this->checkLanguageAccess(0)) {
361 unset($allow['edit']);
362 unset($allow['move']);
363 unset($allow['hide']);
364 }
365 if (!($perms & Permission::PAGE_DELETE)) {
366 unset($allow['delete']);
367 }
368 if (!($perms & Permission::PAGE_NEW)) {
369 unset($allow['new']);
370 }
371 }
372 }
373 return $allow;
374 }
375
376 /*****************************************************
377 *
378 * Localization handling
379 *
380 ****************************************************/
381 /**
382 * Returns the label for key. If a translation for the language set in $this->uc['lang']
383 * is found that is returned, otherwise the default value.
384 * If the global variable $LOCAL_LANG is NOT an array (yet) then this function loads
385 * the global $LOCAL_LANG array with the content of "EXT:core/Resources/Private/Language/locallang_tsfe.xlf"
386 * such that the values therein can be used for labels in the Admin Panel
387 *
388 * @param string $key Key for a label in the $GLOBALS['LOCAL_LANG'] array of "EXT:core/Resources/Private/Language/locallang_tsfe.xlf
389 * @return string The value for the $key
390 */
391 public function extGetLL($key)
392 {
393 if (!is_array($GLOBALS['LOCAL_LANG'])) {
394 $this->getLanguageService()->includeLLFile('EXT:core/Resources/Private/Language/locallang_tsfe.xlf');
395 if (!is_array($GLOBALS['LOCAL_LANG'])) {
396 $GLOBALS['LOCAL_LANG'] = [];
397 }
398 }
399 return htmlspecialchars($this->getLanguageService()->getLL($key));
400 }
401
402 /**
403 * @return LanguageService
404 */
405 protected function getLanguageService()
406 {
407 return $GLOBALS['LANG'];
408 }
409 }