[BUGFIX] Display relations' titles when TCA label field is type inline
[Packages/TYPO3.CMS.git] / typo3 / sysext / core / Classes / Authentication / BackendUserAuthentication.php
1 <?php
2 namespace TYPO3\CMS\Core\Authentication;
3
4 /***************************************************************
5 * Copyright notice
6 *
7 * (c) 1999-2013 Kasper Skårhøj (kasperYYYY@typo3.com)
8 * All rights reserved
9 *
10 * This script is part of the TYPO3 project. The TYPO3 project is
11 * free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * The GNU General Public License can be found at
17 * http://www.gnu.org/copyleft/gpl.html.
18 * A copy is found in the textfile GPL.txt and important notices to the license
19 * from the author is found in LICENSE.txt distributed with these scripts.
20 *
21 *
22 * This script is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
26 *
27 * This copyright notice MUST APPEAR in all copies of the script!
28 ***************************************************************/
29
30 use TYPO3\CMS\Core\Utility\GeneralUtility;
31 use TYPO3\CMS\Backend\Utility\BackendUtility;
32
33 /**
34 * TYPO3 backend user authentication
35 * Contains most of the functions used for checking permissions, authenticating users,
36 * setting up the user, and API for user from outside.
37 * This class contains the configuration of the database fields used plus some
38 * functions for the authentication process of backend users.
39 *
40 * @author Kasper Skårhøj <kasperYYYY@typo3.com>
41 * @internal
42 */
43 class BackendUserAuthentication extends \TYPO3\CMS\Core\Authentication\AbstractUserAuthentication {
44
45 // Should be set to the usergroup-column (id-list) in the user-record
46 /**
47 * @todo Define visibility
48 */
49 public $usergroup_column = 'usergroup';
50
51 // The name of the group-table
52 /**
53 * @todo Define visibility
54 */
55 public $usergroup_table = 'be_groups';
56
57 // @internal array holds lists of eg. tables, fields and other values related to the permission-system. See fetchGroupData
58 /**
59 * @todo Define visibility
60 */
61 public $groupData = array(
62 'filemounts' => array()
63 );
64
65 // This array will hold the groups that the user is a member of
66 /**
67 * @todo Define visibility
68 */
69 public $userGroups = array();
70
71 // This array holds the uid's of the groups in the listed order
72 /**
73 * @todo Define visibility
74 */
75 public $userGroupsUID = array();
76
77 // This is $this->userGroupsUID imploded to a comma list... Will correspond to the 'usergroup_cached_list'
78 /**
79 * @todo Define visibility
80 */
81 public $groupList = '';
82
83 // User workspace. -99 is ERROR (none available), -1 is offline, 0 is online, >0 is custom workspaces.
84 /**
85 * @todo Define visibility
86 */
87 public $workspace = -99;
88
89 // Custom workspace record if any
90 /**
91 * @todo Define visibility
92 */
93 public $workspaceRec = array();
94
95 // Used internally to accumulate data for the user-group. DONT USE THIS EXTERNALLY! Use $this->groupData instead
96 /**
97 * @todo Define visibility
98 */
99 public $dataLists = array(
100 'webmount_list' => '',
101 'filemount_list' => '',
102 'fileoper_perms' => 0,
103 'modList' => '',
104 'tables_select' => '',
105 'tables_modify' => '',
106 'pagetypes_select' => '',
107 'non_exclude_fields' => '',
108 'explicit_allowdeny' => '',
109 'allowed_languages' => '',
110 'workspace_perms' => '',
111 'custom_options' => ''
112 );
113
114 // For debugging/display of order in which subgroups are included.
115 /**
116 * @todo Define visibility
117 */
118 public $includeHierarchy = array();
119
120 // List of group_id's in the order they are processed.
121 /**
122 * @todo Define visibility
123 */
124 public $includeGroupArray = array();
125
126 // Set to 'WIN', if windows
127 /**
128 * @todo Define visibility
129 */
130 public $OS = '';
131
132 // Used to accumulate the TSconfig data of the user
133 /**
134 * @todo Define visibility
135 */
136 public $TSdataArray = array();
137
138 // Contains the non-parsed user TSconfig
139 /**
140 * @todo Define visibility
141 */
142 public $userTS_text = '';
143
144 // Contains the parsed user TSconfig
145 /**
146 * @todo Define visibility
147 */
148 public $userTS = array();
149
150 // Set internally if the user TSconfig was parsed and needs to be cached.
151 /**
152 * @todo Define visibility
153 */
154 public $userTSUpdated = 0;
155
156 // Set this from outside if you want the user TSconfig to ALWAYS be parsed and not fetched from cache.
157 /**
158 * @todo Define visibility
159 */
160 public $userTS_dontGetCached = 0;
161
162 // RTE availability errors collected.
163 /**
164 * @todo Define visibility
165 */
166 public $RTE_errors = array();
167
168 // Contains last error message
169 /**
170 * @todo Define visibility
171 */
172 public $errorMsg = '';
173
174 // Cache for checkWorkspaceCurrent()
175 /**
176 * @todo Define visibility
177 */
178 public $checkWorkspaceCurrent_cache = NULL;
179
180 /**
181 * @var \TYPO3\CMS\Core\Resource\ResourceStorage[]
182 */
183 protected $fileStorages;
184
185 /**
186 * @var array
187 */
188 protected $filePermissions;
189
190 // Table to use for session data
191 /**
192 * @todo Define visibility
193 */
194 public $session_table = 'be_sessions';
195
196 // Table in database with userdata
197 /**
198 * @todo Define visibility
199 */
200 public $user_table = 'be_users';
201
202 // Column for login-name
203 /**
204 * @todo Define visibility
205 */
206 public $username_column = 'username';
207
208 // Column for password
209 /**
210 * @todo Define visibility
211 */
212 public $userident_column = 'password';
213
214 // Column for user-id
215 /**
216 * @todo Define visibility
217 */
218 public $userid_column = 'uid';
219
220 /**
221 * @todo Define visibility
222 */
223 public $lastLogin_column = 'lastlogin';
224
225 /**
226 * @todo Define visibility
227 */
228 public $enablecolumns = array(
229 'rootLevel' => 1,
230 'deleted' => 'deleted',
231 'disabled' => 'disable',
232 'starttime' => 'starttime',
233 'endtime' => 'endtime'
234 );
235
236 // Formfield with login-name
237 /**
238 * @todo Define visibility
239 */
240 public $formfield_uname = 'username';
241
242 // Formfield with password
243 /**
244 * @todo Define visibility
245 */
246 public $formfield_uident = 'userident';
247
248 // Formfield with a unique value which is used to encrypt the password and username
249 /**
250 * @todo Define visibility
251 */
252 public $formfield_chalvalue = 'challenge';
253
254 // Formfield with status: *'login', 'logout'
255 /**
256 * @todo Define visibility
257 */
258 public $formfield_status = 'login_status';
259
260 // Decides if the writelog() function is called at login and logout
261 /**
262 * @todo Define visibility
263 */
264 public $writeStdLog = 1;
265
266 // If the writelog() functions is called if a login-attempt has be tried without success
267 /**
268 * @todo Define visibility
269 */
270 public $writeAttemptLog = 1;
271
272 // if > 0 : session-timeout in seconds. if FALSE/<0 : no timeout. If string: The string is fieldname from the usertable where the timeout can be found.
273 /**
274 * @todo Define visibility
275 */
276 public $auth_timeout_field = 6000;
277
278 // 0 = Session-cookies. If session-cookies, the browser will stop session when the browser is closed. Else it keeps the session for $lifetime seconds.
279 /**
280 * @todo Define visibility
281 */
282 public $lifetime = 0;
283
284 /**
285 * @todo Define visibility
286 */
287 public $challengeStoredInCookie = TRUE;
288
289 // User Config:
290 /**
291 * @todo Define visibility
292 */
293 public $uc;
294
295 // User Config Default values:
296 // The array may contain other fields for configuration. For this, see "setup" extension and "TSConfig" document (User TSconfig, "setup.[xxx]....")
297 // Reserved keys for other storage of session data:
298 // moduleData
299 // moduleSessionID
300 /**
301 * @todo Define visibility
302 */
303 public $uc_default = array(
304 'interfaceSetup' => '',
305 // serialized content that is used to store interface pane and menu positions. Set by the logout.php-script
306 'moduleData' => array(),
307 // user-data for the modules
308 'thumbnailsByDefault' => 1,
309 'emailMeAtLogin' => 0,
310 'condensedMode' => 0,
311 'noMenuMode' => 0,
312 'startModule' => 'help_aboutmodules',
313 'hideSubmoduleIcons' => 0,
314 'helpText' => 1,
315 'titleLen' => 50,
316 'edit_wideDocument' => '0',
317 'edit_showFieldHelp' => 'icon',
318 'edit_RTE' => '1',
319 'edit_docModuleUpload' => '1',
320 'navFrameWidth' => '',
321 // Default is 245 pixels
322 'navFrameResizable' => 0,
323 'resizeTextareas' => 1,
324 'resizeTextareas_MaxHeight' => 500,
325 'resizeTextareas_Flexible' => 0
326 );
327
328 /**
329 * Constructor
330 */
331 public function __construct() {
332 $this->name = self::getCookieName();
333 $this->loginType = 'BE';
334 }
335
336 /**
337 * Sets the security level for the Backend login
338 *
339 * @return void
340 * @todo Define visibility
341 */
342 public function start() {
343 $securityLevel = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['loginSecurityLevel']);
344 $standardSecurityLevels = array('normal', 'challenged', 'superchallenged');
345 // The TYPO3 standard login service relies on $this->security_level being set
346 // to 'superchallenged' because of the password in the database is stored as md5 hash.
347 // @deprecated since 4.7
348 // These lines are here for compatibility purpose only, can be removed in 6.1.
349 // @see \TYPO3\CMS\Sv\AuthenticationService::processLoginData()
350 if (!empty($securityLevel) && !in_array($securityLevel, $standardSecurityLevels)) {
351 $this->security_level = $securityLevel;
352 } else {
353 $this->security_level = 'superchallenged';
354 }
355 parent::start();
356 }
357
358 /**
359 * Returns TRUE if user is admin
360 * Basically this function evaluates if the ->user[admin] field has bit 0 set. If so, user is admin.
361 *
362 * @return boolean
363 * @todo Define visibility
364 */
365 public function isAdmin() {
366 return ($this->user['admin'] & 1) == 1;
367 }
368
369 /**
370 * Returns TRUE if the current user is a member of group $groupId
371 * $groupId must be set. $this->groupList must contain groups
372 * Will return TRUE also if the user is a member of a group through subgroups.
373 *
374 * @param integer $groupId Group ID to look for in $this->groupList
375 * @return boolean
376 * @todo Define visibility
377 */
378 public function isMemberOfGroup($groupId) {
379 $groupId = intval($groupId);
380 if ($this->groupList && $groupId) {
381 return GeneralUtility::inList($this->groupList, $groupId);
382 }
383 }
384
385 /**
386 * Checks if the permissions is granted based on a page-record ($row) and $perms (binary and'ed)
387 *
388 * Bits for permissions, see $perms variable:
389 *
390 * 1 - Show: See/Copy page and the pagecontent.
391 * 16- Edit pagecontent: Change/Add/Delete/Move pagecontent.
392 * 2- Edit page: Change/Move the page, eg. change title, startdate, hidden.
393 * 4- Delete page: Delete the page and pagecontent.
394 * 8- New pages: Create new pages under the page.
395 *
396 * @param array $row Is the pagerow for which the permissions is checked
397 * @param integer $perms Is the binary representation of the permission we are going to check. Every bit in this number represents a permission that must be set. See function explanation.
398 * @return boolean
399 * @todo Define visibility
400 */
401 public function doesUserHaveAccess($row, $perms) {
402 $userPerms = $this->calcPerms($row);
403 return ($userPerms & $perms) == $perms;
404 }
405
406 /**
407 * Checks if the page id, $id, is found within the webmounts set up for the user.
408 * This should ALWAYS be checked for any page id a user works with, whether it's about reading, writing or whatever.
409 * The point is that this will add the security that a user can NEVER touch parts outside his mounted pages in the page tree. This is otherwise possible if the raw page permissions allows for it. So this security check just makes it easier to make safe user configurations.
410 * If the user is admin OR if this feature is disabled (fx. by setting TYPO3_CONF_VARS['BE']['lockBeUserToDBmounts']=0) then it returns "1" right away
411 * Otherwise the function will return the uid of the webmount which was first found in the rootline of the input page $id
412 *
413 * @param integer $id Page ID to check
414 * @param string $readPerms Content of "->getPagePermsClause(1)" (read-permissions). If not set, they will be internally calculated (but if you have the correct value right away you can save that database lookup!)
415 * @param boolean $exitOnError If set, then the function will exit with an error message.
416 * @return integer The page UID of a page in the rootline that matched a mount point
417 * @todo Define visibility
418 */
419 public function isInWebMount($id, $readPerms = '', $exitOnError = 0) {
420 if (!$GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts'] || $this->isAdmin()) {
421 return 1;
422 }
423 $id = intval($id);
424 // Check if input id is an offline version page in which case we will map id to the online version:
425 $checkRec = BackendUtility::getRecord('pages', $id, 'pid,t3ver_oid');
426 if ($checkRec['pid'] == -1) {
427 $id = intval($checkRec['t3ver_oid']);
428 }
429 if (!$readPerms) {
430 $readPerms = $this->getPagePermsClause(1);
431 }
432 if ($id > 0) {
433 $wM = $this->returnWebmounts();
434 $rL = BackendUtility::BEgetRootLine($id, ' AND ' . $readPerms);
435 foreach ($rL as $v) {
436 if ($v['uid'] && in_array($v['uid'], $wM)) {
437 return $v['uid'];
438 }
439 }
440 }
441 if ($exitOnError) {
442 throw new \RuntimeException('Access Error: This page is not within your DB-mounts', 1294586445);
443 }
444 }
445
446 /**
447 * Checks access to a backend module with the $MCONF passed as first argument
448 *
449 * @param array $conf $MCONF array of a backend module!
450 * @param boolean $exitOnError If set, an array will issue an error message and exit.
451 * @return boolean Will return TRUE if $MCONF['access'] is not set at all, if the BE_USER is admin or if the module is enabled in the be_users/be_groups records of the user (specifically enabled). Will return FALSE if the module name is not even found in $TBE_MODULES
452 * @todo Define visibility
453 */
454 public function modAccess($conf, $exitOnError) {
455 if (!BackendUtility::isModuleSetInTBE_MODULES($conf['name'])) {
456 if ($exitOnError) {
457 throw new \RuntimeException('Fatal Error: This module "' . $conf['name'] . '" is not enabled in TBE_MODULES', 1294586446);
458 }
459 return FALSE;
460 }
461 // Workspaces check:
462 if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces') && $conf['workspaces']) {
463 if ($this->workspace === 0 && GeneralUtility::inList($conf['workspaces'], 'online') || $this->workspace === -1 && GeneralUtility::inList($conf['workspaces'], 'offline') || $this->workspace > 0 && GeneralUtility::inList($conf['workspaces'], 'custom')) {
464
465 } else {
466 if ($exitOnError) {
467 throw new \RuntimeException('Workspace Error: This module "' . $conf['name'] . '" is not available under the current workspace', 1294586447);
468 }
469 return FALSE;
470 }
471 }
472 // Returns TRUE if conf[access] is not set at all or if the user is admin
473 if (!$conf['access'] || $this->isAdmin()) {
474 return TRUE;
475 }
476 // If $conf['access'] is set but not with 'admin' then we return TRUE, if the module is found in the modList
477 if (!strstr($conf['access'], 'admin') && $conf['name']) {
478 $acs = $this->check('modules', $conf['name']);
479 }
480 if (!$acs && $exitOnError) {
481 throw new \RuntimeException('Access Error: You don\'t have access to this module.', 1294586448);
482 } else {
483 return $acs;
484 }
485 }
486
487 /**
488 * Returns a WHERE-clause for the pages-table where user permissions according to input argument, $perms, is validated.
489 * $perms is the "mask" used to select. Fx. if $perms is 1 then you'll get all pages that a user can actually see!
490 * 2^0 = show (1)
491 * 2^1 = edit (2)
492 * 2^2 = delete (4)
493 * 2^3 = new (8)
494 * If the user is 'admin' " 1=1" is returned (no effect)
495 * If the user is not set at all (->user is not an array), then " 1=0" is returned (will cause no selection results at all)
496 * The 95% use of this function is "->getPagePermsClause(1)" which will return WHERE clauses for *selecting* pages in backend listings - in other words this will check read permissions.
497 *
498 * @param integer $perms Permission mask to use, see function description
499 * @return string Part of where clause. Prefix " AND " to this.
500 * @todo Define visibility
501 */
502 public function getPagePermsClause($perms) {
503 if (is_array($this->user)) {
504 if ($this->isAdmin()) {
505 return ' 1=1';
506 }
507 $perms = intval($perms);
508 // Make sure it's integer.
509 $str = ' (' . '(pages.perms_everybody & ' . $perms . ' = ' . $perms . ')' . ' OR (pages.perms_userid = ' . $this->user['uid'] . ' AND pages.perms_user & ' . $perms . ' = ' . $perms . ')';
510 // User
511 if ($this->groupList) {
512 // Group (if any is set)
513 $str .= ' OR (pages.perms_groupid in (' . $this->groupList . ') AND pages.perms_group & ' . $perms . ' = ' . $perms . ')';
514 }
515 $str .= ')';
516 // ****************
517 // getPagePermsClause-HOOK
518 // ****************
519 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getPagePermsClause'])) {
520 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getPagePermsClause'] as $_funcRef) {
521 $_params = array('currentClause' => $str, 'perms' => $perms);
522 $str = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
523 }
524 }
525 return $str;
526 } else {
527 return ' 1=0';
528 }
529 }
530
531 /**
532 * Returns a combined binary representation of the current users permissions for the page-record, $row.
533 * The perms for user, group and everybody is OR'ed together (provided that the page-owner is the user and for the groups that the user is a member of the group
534 * If the user is admin, 31 is returned (full permissions for all five flags)
535 *
536 * @param array $row Input page row with all perms_* fields available.
537 * @return integer Bitwise representation of the users permissions in relation to input page row, $row
538 * @todo Define visibility
539 */
540 public function calcPerms($row) {
541 // Return 31 for admin users.
542 if ($this->isAdmin()) {
543 return 31;
544 }
545 $out = 0;
546 if (isset($row['perms_userid']) && isset($row['perms_user']) && isset($row['perms_groupid']) && isset($row['perms_group']) && isset($row['perms_everybody']) && isset($this->groupList)) {
547 if ($this->user['uid'] == $row['perms_userid']) {
548 $out |= $row['perms_user'];
549 }
550 if ($this->isMemberOfGroup($row['perms_groupid'])) {
551 $out |= $row['perms_group'];
552 }
553 $out |= $row['perms_everybody'];
554 }
555 // ****************
556 // CALCPERMS hook
557 // ****************
558 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['calcPerms'])) {
559 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['calcPerms'] as $_funcRef) {
560 $_params = array(
561 'row' => $row,
562 'outputPermissions' => $out
563 );
564 $out = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
565 }
566 }
567 return $out;
568 }
569
570 /**
571 * Returns TRUE if the RTE (Rich Text Editor) can be enabled for the user
572 * Strictly this is not permissions being checked but rather a series of settings like a loaded extension, browser/client type and a configuration option in ->uc[edit_RTE]
573 * The reasons for a FALSE return can be found in $this->RTE_errors
574 *
575 * @return boolean
576 * @todo Define visibility
577 */
578 public function isRTE() {
579 // Start:
580 $this->RTE_errors = array();
581 if (!$this->uc['edit_RTE']) {
582 $this->RTE_errors[] = 'RTE is not enabled for user!';
583 }
584 if (!$GLOBALS['TYPO3_CONF_VARS']['BE']['RTEenabled']) {
585 $this->RTE_errors[] = 'RTE is not enabled in $TYPO3_CONF_VARS["BE"]["RTEenabled"]';
586 }
587 // Acquire RTE object:
588 $RTE = BackendUtility::RTEgetObj();
589 if (!is_object($RTE)) {
590 $this->RTE_errors = array_merge($this->RTE_errors, $RTE);
591 }
592 if (!count($this->RTE_errors)) {
593 return TRUE;
594 } else {
595 return FALSE;
596 }
597 }
598
599 /**
600 * Returns TRUE if the $value is found in the list in a $this->groupData[] index pointed to by $type (array key).
601 * Can thus be users to check for modules, exclude-fields, select/modify permissions for tables etc.
602 * If user is admin TRUE is also returned
603 * Please see the document Inside TYPO3 for examples.
604 *
605 * @param string $type The type value; "webmounts", "filemounts", "pagetypes_select", "tables_select", "tables_modify", "non_exclude_fields", "modules
606 * @param string $value String to search for in the groupData-list
607 * @return boolean TRUE if permission is granted (that is, the value was found in the groupData list - or the BE_USER is "admin")
608 * @todo Define visibility
609 */
610 public function check($type, $value) {
611 if (isset($this->groupData[$type])) {
612 if ($this->isAdmin() || GeneralUtility::inList($this->groupData[$type], $value)) {
613 return TRUE;
614 }
615 }
616 return FALSE;
617 }
618
619 /**
620 * Checking the authMode of a select field with authMode set
621 *
622 * @param string $table Table name
623 * @param string $field Field name (must be configured in TCA and of type "select" with authMode set!)
624 * @param string $value Value to evaluation (single value, must not contain any of the chars ":,|")
625 * @param string $authMode Auth mode keyword (explicitAllow, explicitDeny, individual)
626 * @return boolean Whether access is granted or not
627 * @todo Define visibility
628 */
629 public function checkAuthMode($table, $field, $value, $authMode) {
630 // Admin users can do anything:
631 if ($this->isAdmin()) {
632 return TRUE;
633 }
634 // Allow all blank values:
635 if (!strcmp($value, '')) {
636 return TRUE;
637 }
638 // Certain characters are not allowed in the value
639 if (preg_match('/[:|,]/', $value)) {
640 return FALSE;
641 }
642 // Initialize:
643 $testValue = $table . ':' . $field . ':' . $value;
644 $out = TRUE;
645 // Checking value:
646 switch ((string) $authMode) {
647 case 'explicitAllow':
648 if (!GeneralUtility::inList($this->groupData['explicit_allowdeny'], ($testValue . ':ALLOW'))) {
649 $out = FALSE;
650 }
651 break;
652 case 'explicitDeny':
653 if (GeneralUtility::inList($this->groupData['explicit_allowdeny'], $testValue . ':DENY')) {
654 $out = FALSE;
655 }
656 break;
657 case 'individual':
658 if (is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['columns'][$field])) {
659 $items = $GLOBALS['TCA'][$table]['columns'][$field]['config']['items'];
660 if (is_array($items)) {
661 foreach ($items as $iCfg) {
662 if (!strcmp($iCfg[1], $value) && $iCfg[4]) {
663 switch ((string) $iCfg[4]) {
664 case 'EXPL_ALLOW':
665 if (!GeneralUtility::inList($this->groupData['explicit_allowdeny'], ($testValue . ':ALLOW'))) {
666 $out = FALSE;
667 }
668 break;
669 case 'EXPL_DENY':
670 if (GeneralUtility::inList($this->groupData['explicit_allowdeny'], $testValue . ':DENY')) {
671 $out = FALSE;
672 }
673 break;
674 }
675 break;
676 }
677 }
678 }
679 }
680 break;
681 }
682 return $out;
683 }
684
685 /**
686 * Checking if a language value (-1, 0 and >0 for sys_language records) is allowed to be edited by the user.
687 *
688 * @param integer $langValue Language value to evaluate
689 * @return boolean Returns TRUE if the language value is allowed, otherwise FALSE.
690 * @todo Define visibility
691 */
692 public function checkLanguageAccess($langValue) {
693 // The users language list must be non-blank - otherwise all languages are allowed.
694 if (strcmp(trim($this->groupData['allowed_languages']), '')) {
695 $langValue = intval($langValue);
696 // Language must either be explicitly allowed OR the lang Value be "-1" (all languages)
697 if ($langValue != -1 && !$this->check('allowed_languages', $langValue)) {
698 return FALSE;
699 }
700 }
701 return TRUE;
702 }
703
704 /**
705 * Check if user has access to all existing localizations for a certain record
706 *
707 * @param string $table The table
708 * @param array $record The current record
709 * @return boolean
710 * @todo Define visibility
711 */
712 public function checkFullLanguagesAccess($table, $record) {
713 $recordLocalizationAccess = $this->checkLanguageAccess(0);
714 if ($recordLocalizationAccess && (BackendUtility::isTableLocalizable($table) || isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']))) {
715 if (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])) {
716 $l10nTable = $GLOBALS['TCA'][$table]['ctrl']['transForeignTable'];
717 $pointerField = $GLOBALS['TCA'][$l10nTable]['ctrl']['transOrigPointerField'];
718 $pointerValue = $record['uid'];
719 } else {
720 $l10nTable = $table;
721 $pointerField = $GLOBALS['TCA'][$l10nTable]['ctrl']['transOrigPointerField'];
722 $pointerValue = $record[$pointerField] > 0 ? $record[$pointerField] : $record['uid'];
723 }
724 $recordLocalizations = BackendUtility::getRecordsByField($l10nTable, $pointerField, $pointerValue, '', '', '', '1');
725 if (is_array($recordLocalizations)) {
726 foreach ($recordLocalizations as $localization) {
727 $recordLocalizationAccess = $recordLocalizationAccess && $this->checkLanguageAccess($localization[$GLOBALS['TCA'][$l10nTable]['ctrl']['languageField']]);
728 if (!$recordLocalizationAccess) {
729 break;
730 }
731 }
732 }
733 }
734 return $recordLocalizationAccess;
735 }
736
737 /**
738 * Checking if a user has editing access to a record from a $GLOBALS['TCA'] table.
739 * The checks does not take page permissions and other "environmental" things into account. It only deal with record internals; If any values in the record fields disallows it.
740 * For instance languages settings, authMode selector boxes are evaluated (and maybe more in the future).
741 * It will check for workspace dependent access.
742 * The function takes an ID (integer) or row (array) as second argument.
743 *
744 * @param string $table Table name
745 * @param mixed $idOrRow If integer, then this is the ID of the record. If Array this just represents fields in the record.
746 * @param boolean $newRecord Set, if testing a new (non-existing) record array. Will disable certain checks that doesn't make much sense in that context.
747 * @param boolean $deletedRecord Set, if testing a deleted record array.
748 * @param boolean $checkFullLanguageAccess Set, whenever access to all translations of the record is required
749 * @return boolean TRUE if OK, otherwise FALSE
750 * @todo Define visibility
751 */
752 public function recordEditAccessInternals($table, $idOrRow, $newRecord = FALSE, $deletedRecord = FALSE, $checkFullLanguageAccess = FALSE) {
753 if (isset($GLOBALS['TCA'][$table])) {
754 // Always return TRUE for Admin users.
755 if ($this->isAdmin()) {
756 return TRUE;
757 }
758 // Fetching the record if the $idOrRow variable was not an array on input:
759 if (!is_array($idOrRow)) {
760 if ($deletedRecord) {
761 $idOrRow = BackendUtility::getRecord($table, $idOrRow, '*', '', FALSE);
762 } else {
763 $idOrRow = BackendUtility::getRecord($table, $idOrRow);
764 }
765 if (!is_array($idOrRow)) {
766 $this->errorMsg = 'ERROR: Record could not be fetched.';
767 return FALSE;
768 }
769 }
770 // Checking languages:
771 if ($GLOBALS['TCA'][$table]['ctrl']['languageField']) {
772 // Language field must be found in input row - otherwise it does not make sense.
773 if (isset($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) {
774 if (!$this->checkLanguageAccess($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) {
775 $this->errorMsg = 'ERROR: Language was not allowed.';
776 return FALSE;
777 } elseif ($checkFullLanguageAccess && $idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']] == 0 && !$this->checkFullLanguagesAccess($table, $idOrRow)) {
778 $this->errorMsg = 'ERROR: Related/affected language was not allowed.';
779 return FALSE;
780 }
781 } else {
782 $this->errorMsg = 'ERROR: The "languageField" field named "' . $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '" was not found in testing record!';
783 return FALSE;
784 }
785 } elseif (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) && $checkFullLanguageAccess && !$this->checkFullLanguagesAccess($table, $idOrRow)) {
786 return FALSE;
787 }
788 // Checking authMode fields:
789 if (is_array($GLOBALS['TCA'][$table]['columns'])) {
790 foreach ($GLOBALS['TCA'][$table]['columns'] as $fieldName => $fieldValue) {
791 if (isset($idOrRow[$fieldName])) {
792 if ($fieldValue['config']['type'] == 'select' && $fieldValue['config']['authMode'] && !strcmp($fieldValue['config']['authMode_enforce'], 'strict')) {
793 if (!$this->checkAuthMode($table, $fieldName, $idOrRow[$fieldName], $fieldValue['config']['authMode'])) {
794 $this->errorMsg = 'ERROR: authMode "' . $fieldValue['config']['authMode'] . '" failed for field "' . $fieldName . '" with value "' . $idOrRow[$fieldName] . '" evaluated';
795 return FALSE;
796 }
797 }
798 }
799 }
800 }
801 // Checking "editlock" feature (doesn't apply to new records)
802 if (!$newRecord && $GLOBALS['TCA'][$table]['ctrl']['editlock']) {
803 if (isset($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['editlock']])) {
804 if ($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['editlock']]) {
805 $this->errorMsg = 'ERROR: Record was locked for editing. Only admin users can change this state.';
806 return FALSE;
807 }
808 } else {
809 $this->errorMsg = 'ERROR: The "editLock" field named "' . $GLOBALS['TCA'][$table]['ctrl']['editlock'] . '" was not found in testing record!';
810 return FALSE;
811 }
812 }
813 // Checking record permissions
814 // THIS is where we can include a check for "perms_" fields for other records than pages...
815 // Process any hooks
816 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['recordEditAccessInternals'])) {
817 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['recordEditAccessInternals'] as $funcRef) {
818 $params = array(
819 'table' => $table,
820 'idOrRow' => $idOrRow,
821 'newRecord' => $newRecord
822 );
823 if (!GeneralUtility::callUserFunction($funcRef, $params, $this)) {
824 return FALSE;
825 }
826 }
827 }
828 // Finally, return TRUE if all is well.
829 return TRUE;
830 }
831 }
832
833 /**
834 * Checks a type of permission against the compiled permission integer, $compiledPermissions, and in relation to table, $tableName
835 *
836 * @param integer $compiledPermissions Could typically be the "compiled permissions" integer returned by ->calcPerms
837 * @param string $tableName Is the tablename to check: If "pages" table then edit,new,delete and editcontent permissions can be checked. Other tables will be checked for "editcontent" only (and $type will be ignored)
838 * @param string $actionType For $tableName='pages' this can be 'edit' (2), 'new' (8 or 16), 'delete' (4), 'editcontent' (16). For all other tables this is ignored. (16 is used)
839 * @return boolean
840 * @access public (used by typo3/alt_clickmenu.php)
841 */
842 public function isPSet($compiledPermissions, $tableName, $actionType = '') {
843 if ($this->isAdmin()) {
844 $result = TRUE;
845 } elseif ($tableName == 'pages') {
846 switch ($actionType) {
847 case 'edit':
848 $result = ($compiledPermissions & 2) !== 0;
849 break;
850 case 'new':
851 // Create new page OR page content
852 $result = ($compiledPermissions & 8 + 16) !== 0;
853 break;
854 case 'delete':
855 $result = ($compiledPermissions & 4) !== 0;
856 break;
857 case 'editcontent':
858 $result = ($compiledPermissions & 16) !== 0;
859 break;
860 default:
861 $result = FALSE;
862 }
863 } else {
864 $result = ($compiledPermissions & 16) !== 0;
865 }
866 return $result;
867 }
868
869 /**
870 * Returns TRUE if the BE_USER is allowed to *create* shortcuts in the backend modules
871 *
872 * @return boolean
873 * @todo Define visibility
874 */
875 public function mayMakeShortcut() {
876 return $this->getTSConfigVal('options.enableBookmarks') && !$this->getTSConfigVal('options.mayNotCreateEditBookmarks');
877 }
878
879 /**
880 * Checking if editing of an existing record is allowed in current workspace if that is offline.
881 * Rules for editing in offline mode:
882 * - record supports versioning and is an offline version from workspace and has the corrent stage
883 * - or record (any) is in a branch where there is a page which is a version from the workspace and where the stage is not preventing records
884 *
885 * @param string $table Table of record
886 * @param array $recData Integer (record uid) or array where fields are at least: pid, t3ver_wsid, t3ver_stage (if versioningWS is set)
887 * @return string String error code, telling the failure state. FALSE=All ok
888 * @todo Define visibility
889 */
890 public function workspaceCannotEditRecord($table, $recData) {
891 // Only test offline spaces:
892 if ($this->workspace !== 0) {
893 if (!is_array($recData)) {
894 $recData = BackendUtility::getRecord($table, $recData, 'pid' . ($GLOBALS['TCA'][$table]['ctrl']['versioningWS'] ? ',t3ver_wsid,t3ver_stage' : ''));
895 }
896 if (is_array($recData)) {
897 // We are testing a "version" (identified by a pid of -1): it can be edited provided that workspace matches and versioning is enabled for the table.
898 if ((int) $recData['pid'] === -1) {
899 // No versioning, basic error, inconsistency even! Such records should not have a pid of -1!
900 if (!$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
901 return 'Versioning disabled for table';
902 } elseif ((int) $recData['t3ver_wsid'] !== $this->workspace) {
903 // So does workspace match?
904 return 'Workspace ID of record didn\'t match current workspace';
905 } else {
906 // So is the user allowed to "use" the edit stage within the workspace?
907 return $this->workspaceCheckStageForCurrent(0) ? FALSE : 'User\'s access level did not allow for editing';
908 }
909 } else {
910 // We are testing a "live" record:
911 // For "Live" records, check that PID for table allows editing
912 if ($res = $this->workspaceAllowLiveRecordsInPID($recData['pid'], $table)) {
913 // Live records are OK in this branch, but what about the stage of branch point, if any:
914 // OK
915 return $res > 0 ? FALSE : 'Stage for versioning root point and users access level did not allow for editing';
916 } else {
917 // If not offline and not in versionized branch, output error:
918 return 'Online record was not in versionized branch!';
919 }
920 }
921 } else {
922 return 'No record';
923 }
924 } else {
925 // OK because workspace is 0
926 return FALSE;
927 }
928 }
929
930 /**
931 * Evaluates if a user is allowed to edit the offline version
932 *
933 * @param string $table Table of record
934 * @param array $recData Integer (record uid) or array where fields are at least: pid, t3ver_wsid, t3ver_stage (if versioningWS is set)
935 * @return string String error code, telling the failure state. FALSE=All ok
936 * @see workspaceCannotEditRecord()
937 * @todo Define visibility
938 */
939 public function workspaceCannotEditOfflineVersion($table, $recData) {
940 if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
941 if (!is_array($recData)) {
942 $recData = BackendUtility::getRecord($table, $recData, 'uid,pid,t3ver_wsid,t3ver_stage');
943 }
944 if (is_array($recData)) {
945 if ((int) $recData['pid'] === -1) {
946 return $this->workspaceCannotEditRecord($table, $recData);
947 } else {
948 return 'Not an offline version';
949 }
950 } else {
951 return 'No record';
952 }
953 } else {
954 return 'Table does not support versioning.';
955 }
956 }
957
958 /**
959 * Check if "live" records from $table may be created or edited in this PID.
960 * If the answer is FALSE it means the only valid way to create or edit records in the PID is by versioning
961 * If the answer is 1 or 2 it means it is OK to create a record, if -1 it means that it is OK in terms of versioning because the element was within a versionized branch but NOT ok in terms of the state the root point had!
962 *
963 * @param integer $pid PID value to check for.
964 * @param string $table Table name
965 * @return mixed Returns FALSE if a live record cannot be created and must be versionized in order to do so. 2 means a) Workspace is "Live" or workspace allows "live edit" of records from non-versionized tables (and the $table is not versionizable). 1 and -1 means the pid is inside a versionized branch where -1 means that the branch-point did NOT allow a new record according to its state.
966 * @todo Define visibility
967 */
968 public function workspaceAllowLiveRecordsInPID($pid, $table) {
969 // Always for Live workspace AND if live-edit is enabled and tables are completely without versioning it is ok as well.
970 if ($this->workspace === 0 || $this->workspaceRec['live_edit'] && !$GLOBALS['TCA'][$table]['ctrl']['versioningWS'] || $GLOBALS['TCA'][$table]['ctrl']['versioningWS_alwaysAllowLiveEdit']) {
971 // OK to create for this table.
972 return 2;
973 } else {
974 // If the answer is FALSE it means the only valid way to create or edit records in the PID is by versioning
975 return FALSE;
976 }
977 }
978
979 /**
980 * Evaluates if a record from $table can be created in $pid
981 *
982 * @param integer $pid Page id. This value must be the _ORIG_uid if available: So when you have pages versionized as "page" or "element" you must supply the id of the page version in the workspace!
983 * @param string $table Table name
984 * @return boolean TRUE if OK.
985 * @todo Define visibility
986 */
987 public function workspaceCreateNewRecord($pid, $table) {
988 if ($res = $this->workspaceAllowLiveRecordsInPID($pid, $table)) {
989 // If LIVE records cannot be created in the current PID due to workspace restrictions, prepare creation of placeholder-record
990 if ($res < 0) {
991 // Stage for versioning root point and users access level did not allow for editing
992 return FALSE;
993 }
994 } elseif (!$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
995 // So, if no live records were allowed, we have to create a new version of this record:
996 return FALSE;
997 }
998 return TRUE;
999 }
1000
1001 /**
1002 * Evaluates if auto creation of a version of a record is allowed.
1003 *
1004 * @param string $table Table of the record
1005 * @param integer $id UID of record
1006 * @param integer $recpid PID of record
1007 * @return boolean TRUE if ok.
1008 * @todo Define visibility
1009 */
1010 public function workspaceAllowAutoCreation($table, $id, $recpid) {
1011 // Auto-creation of version: In offline workspace, test if versioning is
1012 // enabled and look for workspace version of input record.
1013 // If there is no versionized record found we will create one and save to that.
1014 if ($this->workspace !== 0 && !$this->workspaceRec['disable_autocreate'] && $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] && $recpid >= 0 && !BackendUtility::getWorkspaceVersionOfRecord($this->workspace, $table, $id, 'uid')) {
1015 // There must be no existing version of this record in workspace.
1016 return TRUE;
1017 } elseif ($this->workspaceRec['disable_autocreate']) {
1018 GeneralUtility::deprecationLog('Usage of disable_autocreate feature is deprecated since 4.5.');
1019 }
1020 }
1021
1022 /**
1023 * Checks if an element stage allows access for the user in the current workspace
1024 * In live workspace (= 0) access is always granted for any stage.
1025 * Admins are always allowed.
1026 * An option for custom workspaces allows members to also edit when the stage is "Review"
1027 *
1028 * @param integer $stage Stage id from an element: -1,0 = editing, 1 = reviewer, >1 = owner
1029 * @return boolean TRUE if user is allowed access
1030 * @todo Define visibility
1031 */
1032 public function workspaceCheckStageForCurrent($stage) {
1033 // Always allow for admins
1034 if ($this->isAdmin()) {
1035 return TRUE;
1036 }
1037 if ($this->workspace !== 0 && \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
1038 $stage = intval($stage);
1039 $stat = $this->checkWorkspaceCurrent();
1040 // Check if custom staging is activated
1041 $workspaceRec = BackendUtility::getRecord('sys_workspace', $stat['uid']);
1042 if ($workspaceRec['custom_stages'] > 0 && $stage !== 0 && $stage !== -10) {
1043 // Get custom stage record
1044 $workspaceStageRec = BackendUtility::getRecord('sys_workspace_stage', $stage);
1045 // Check if the user is responsible for the current stage
1046 if (GeneralUtility::inList($workspaceStageRec['responsible_persons'], 'be_users_' . $this->user['uid']) && $stat['_ACCESS'] === 'member' || $stat['_ACCESS'] === 'owner') {
1047 return TRUE;
1048 }
1049 // Check if the user is in a group which is responsible for the current stage
1050 foreach ($this->userGroupsUID as $groupUid) {
1051 if (GeneralUtility::inList($workspaceStageRec['responsible_persons'], 'be_groups_' . $groupUid) && $stat['_ACCESS'] === 'member' || $stat['_ACCESS'] === 'owner') {
1052 return TRUE;
1053 }
1054 }
1055 } elseif ($stage == -10 || $stage == -20) {
1056 if ($stat['_ACCESS'] === 'owner') {
1057 return TRUE;
1058 } else {
1059 return FALSE;
1060 }
1061 } else {
1062 $memberStageLimit = $this->workspaceRec['review_stage_edit'] ? 1 : 0;
1063 if ($stage <= $memberStageLimit && $stat['_ACCESS'] === 'member' || $stage <= 1 && $stat['_ACCESS'] === 'reviewer' || $stat['_ACCESS'] === 'owner') {
1064 return TRUE;
1065 }
1066 }
1067 } else {
1068 // Always OK for live workspace.
1069 return TRUE;
1070 }
1071 }
1072
1073 /**
1074 * Returns TRUE if the user has access to publish content from the workspace ID given.
1075 * Admin-users are always granted access to do this
1076 * If the workspace ID is 0 (live) all users have access also
1077 * For custom workspaces it depends on whether the user is owner OR like with draft workspace if the user has access to Live workspace.
1078 *
1079 * @param integer $wsid Workspace UID; 0,1+
1080 * @return boolean Returns TRUE if the user has access to publish content from the workspace ID given.
1081 * @todo Define visibility
1082 */
1083 public function workspacePublishAccess($wsid) {
1084 if ($this->isAdmin()) {
1085 return TRUE;
1086 }
1087 // If no access to workspace, of course you cannot publish!
1088 $retVal = FALSE;
1089 $wsAccess = $this->checkWorkspace($wsid);
1090 if ($wsAccess) {
1091 switch ($wsAccess['uid']) {
1092 case 0:
1093 // Live workspace
1094 // If access to Live workspace, no problem.
1095 $retVal = TRUE;
1096 break;
1097 default:
1098 // Custom workspace
1099 $retVal = $wsAccess['_ACCESS'] === 'owner' || $this->checkWorkspace(0) && !($wsAccess['publish_access'] & 2);
1100 // Either be an adminuser OR have access to online workspace which is OK as well as long as publishing access is not limited by workspace option.
1101 }
1102 }
1103 return $retVal;
1104 }
1105
1106 /**
1107 * Workspace swap-mode access?
1108 *
1109 * @return boolean Returns TRUE if records can be swapped in the current workspace, otherwise FALSE
1110 * @todo Define visibility
1111 */
1112 public function workspaceSwapAccess() {
1113 if ($this->workspace > 0 && (int) $this->workspaceRec['swap_modes'] === 2) {
1114 return FALSE;
1115 } else {
1116 return TRUE;
1117 }
1118 }
1119
1120 /**
1121 * Returns the value/properties of a TS-object as given by $objectString, eg. 'options.dontMountAdminMounts'
1122 * Nice (general!) function for returning a part of a TypoScript array!
1123 *
1124 * @param string $objectString Pointer to an "object" in the TypoScript array, fx. 'options.dontMountAdminMounts'
1125 * @param array $config Optional TSconfig array: If array, then this is used and not $this->userTS. If not array, $this->userTS is used.
1126 * @return array An array with two keys, "value" and "properties" where "value" is a string with the value of the objectsting and "properties" is an array with the properties of the objectstring.
1127 * @todo Define visibility
1128 */
1129 public function getTSConfig($objectString, $config = '') {
1130 if (!is_array($config)) {
1131 // Getting Root-ts if not sent
1132 $config = $this->userTS;
1133 }
1134 $TSConf = array('value' => NULL, 'properties' => NULL);
1135 $parts = GeneralUtility::trimExplode('.', $objectString, TRUE, 2);
1136 $key = $parts[0];
1137 if (strlen($key) > 0) {
1138 if (count($parts) > 1 && strlen($parts[1]) > 0) {
1139 // Go on, get the next level
1140 if (is_array($config[$key . '.'])) {
1141 $TSConf = $this->getTSConfig($parts[1], $config[$key . '.']);
1142 }
1143 } else {
1144 $TSConf['value'] = $config[$key];
1145 $TSConf['properties'] = $config[$key . '.'];
1146 }
1147 }
1148 return $TSConf;
1149 }
1150
1151 /**
1152 * Returns the "value" of the $objectString from the BE_USERS "User TSconfig" array
1153 *
1154 * @param string $objectString Object string, eg. "somestring.someproperty.somesubproperty
1155 * @return string The value for that object string (object path)
1156 * @see getTSConfig()
1157 * @todo Define visibility
1158 */
1159 public function getTSConfigVal($objectString) {
1160 $TSConf = $this->getTSConfig($objectString);
1161 return $TSConf['value'];
1162 }
1163
1164 /**
1165 * Returns the "properties" of the $objectString from the BE_USERS "User TSconfig" array
1166 *
1167 * @param string $objectString Object string, eg. "somestring.someproperty.somesubproperty
1168 * @return array The properties for that object string (object path) - if any
1169 * @see getTSConfig()
1170 * @todo Define visibility
1171 */
1172 public function getTSConfigProp($objectString) {
1173 $TSConf = $this->getTSConfig($objectString);
1174 return $TSConf['properties'];
1175 }
1176
1177 /**
1178 * Returns an array with the webmounts.
1179 * If no webmounts, and empty array is returned.
1180 * NOTICE: Deleted pages WILL NOT be filtered out! So if a mounted page has been deleted it is STILL coming out as a webmount. This is not checked due to performance.
1181 *
1182 * @return array
1183 * @todo Define visibility
1184 */
1185 public function returnWebmounts() {
1186 return (string) $this->groupData['webmounts'] != '' ? explode(',', $this->groupData['webmounts']) : array();
1187 }
1188
1189 /**
1190 * Returns TRUE or FALSE, depending if an alert popup (a javascript confirmation) should be shown
1191 * call like $GLOBALS['BE_USER']->jsConfirmation($BITMASK)
1192 *
1193 * 1 - typeChange
1194 * 2 - copy/move/paste
1195 * 4 - delete
1196 * 8 - frontend editing
1197 * 128 - other (not used yet)
1198 *
1199 * @param integer $bitmask Bitmask
1200 * @return boolean TRUE if the confirmation should be shown
1201 * @todo Define visibility
1202 */
1203 public function jsConfirmation($bitmask) {
1204 $alertPopup = $GLOBALS['BE_USER']->getTSConfig('options.alertPopups');
1205 if (empty($alertPopup['value'])) {
1206 // Default: show all warnings
1207 $alertPopup = 255;
1208 } else {
1209 $alertPopup = (int) $alertPopup['value'];
1210 }
1211 // Show confirmation
1212 if (($alertPopup & $bitmask) == $bitmask) {
1213 return 1;
1214 } else {
1215 // don't show confirmation
1216 return 0;
1217 }
1218 }
1219
1220 /**
1221 * Initializes a lot of stuff like the access-lists, database-mountpoints and filemountpoints
1222 * This method is called by ->backendCheckLogin() (from extending \TYPO3\CMS\Core\Authentication\BackendUserAuthentication) if the backend user login has verified OK.
1223 * Generally this is required initialization of a backend user.
1224 *
1225 * @return void
1226 * @access private
1227 * @see \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser
1228 * @todo Define visibility
1229 */
1230 public function fetchGroupData() {
1231 if ($this->user['uid']) {
1232 // Get lists for the be_user record and set them as default/primary values.
1233 // Enabled Backend Modules
1234 $this->dataLists['modList'] = $this->user['userMods'];
1235 // Add Allowed Languages
1236 $this->dataLists['allowed_languages'] = $this->user['allowed_languages'];
1237 // Set user value for workspace permissions.
1238 $this->dataLists['workspace_perms'] = $this->user['workspace_perms'];
1239 // Database mountpoints
1240 $this->dataLists['webmount_list'] = $this->user['db_mountpoints'];
1241 // File mountpoints
1242 $this->dataLists['filemount_list'] = $this->user['file_mountpoints'];
1243 // Fileoperation permissions
1244 $this->dataLists['fileoper_perms'] = (int) $this->user['fileoper_perms'];
1245 // Setting default User TSconfig:
1246 $this->TSdataArray[] = $this->addTScomment('From $GLOBALS["TYPO3_CONF_VARS"]["BE"]["defaultUserTSconfig"]:') . $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultUserTSconfig'];
1247 // Default TSconfig for admin-users
1248 if ($this->isAdmin()) {
1249 $this->TSdataArray[] = $this->addTScomment('"admin" user presets:') . '
1250 admPanel.enable.all = 1
1251 ';
1252 if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('sys_note')) {
1253 $this->TSdataArray[] = '
1254 // Setting defaults for sys_note author / email...
1255 TCAdefaults.sys_note.author = ' . $this->user['realName'] . '
1256 TCAdefaults.sys_note.email = ' . $this->user['email'] . '
1257 ';
1258 }
1259 }
1260 // BE_GROUPS:
1261 // Get the groups...
1262 // 240203: Since the group-field never contains any references to groups with a prepended table name
1263 // we think it's safe to just intExplode and re-implode - which should be much faster than the other function call.
1264 $grList = $GLOBALS['TYPO3_DB']->cleanIntList($this->user[$this->usergroup_column]);
1265 if ($grList) {
1266 // Fetch groups will add a lot of information to the internal arrays: modules, accesslists, TSconfig etc. Refer to fetchGroups() function.
1267 $this->fetchGroups($grList);
1268 }
1269 // Add the TSconfig for this specific user:
1270 $this->TSdataArray[] = $this->addTScomment('USER TSconfig field') . $this->user['TSconfig'];
1271 // Check include lines.
1272 $this->TSdataArray = \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::checkIncludeLines_array($this->TSdataArray);
1273 // Imploding with "[global]" will make sure that non-ended confinements with braces are ignored.
1274 $this->userTS_text = implode(LF . '[GLOBAL]' . LF, $this->TSdataArray);
1275 if (!$this->userTS_dontGetCached) {
1276 // Perform TS-Config parsing with condition matching
1277 $parseObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Backend\\Configuration\\TsConfigParser');
1278 $res = $parseObj->parseTSconfig($this->userTS_text, 'userTS');
1279 if ($res) {
1280 $this->userTS = $res['TSconfig'];
1281 $this->userTSUpdated = $res['cached'] ? 0 : 1;
1282 }
1283 } else {
1284 // Parsing the user TSconfig (or getting from cache)
1285 $hash = md5('userTS:' . $this->userTS_text);
1286 $cachedContent = BackendUtility::getHash($hash);
1287 if (isset($cachedContent) && !$this->userTS_dontGetCached) {
1288 $this->userTS = unserialize($cachedContent);
1289 } else {
1290 $parseObj = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\TypoScript\\Parser\\TypoScriptParser');
1291 $parseObj->parse($this->userTS_text);
1292 $this->userTS = $parseObj->setup;
1293 BackendUtility::storeHash($hash, serialize($this->userTS), 'BE_USER_TSconfig');
1294 // Update UC:
1295 $this->userTSUpdated = 1;
1296 }
1297 }
1298 // Processing webmounts
1299 // Admin's always have the root mounted
1300 if ($this->isAdmin() && !$this->getTSConfigVal('options.dontMountAdminMounts')) {
1301 $this->dataLists['webmount_list'] = '0,' . $this->dataLists['webmount_list'];
1302 }
1303 // The lists are cleaned for duplicates
1304 $this->groupData['webmounts'] = GeneralUtility::uniqueList($this->dataLists['webmount_list']);
1305 $this->groupData['pagetypes_select'] = GeneralUtility::uniqueList($this->dataLists['pagetypes_select']);
1306 $this->groupData['tables_select'] = GeneralUtility::uniqueList($this->dataLists['tables_modify'] . ',' . $this->dataLists['tables_select']);
1307 $this->groupData['tables_modify'] = GeneralUtility::uniqueList($this->dataLists['tables_modify']);
1308 $this->groupData['non_exclude_fields'] = GeneralUtility::uniqueList($this->dataLists['non_exclude_fields']);
1309 $this->groupData['explicit_allowdeny'] = GeneralUtility::uniqueList($this->dataLists['explicit_allowdeny']);
1310 $this->groupData['allowed_languages'] = GeneralUtility::uniqueList($this->dataLists['allowed_languages']);
1311 $this->groupData['custom_options'] = GeneralUtility::uniqueList($this->dataLists['custom_options']);
1312 $this->groupData['modules'] = GeneralUtility::uniqueList($this->dataLists['modList']);
1313 $this->groupData['fileoper_perms'] = $this->dataLists['fileoper_perms'];
1314 $this->groupData['workspace_perms'] = $this->dataLists['workspace_perms'];
1315 // Populating the $this->userGroupsUID -array with the groups in the order in which they were LAST included.!!
1316 $this->userGroupsUID = array_reverse(array_unique(array_reverse($this->includeGroupArray)));
1317 // Finally this is the list of group_uid's in the order they are parsed (including subgroups!) and without duplicates (duplicates are presented with their last entrance in the list, which thus reflects the order of the TypoScript in TSconfig)
1318 $this->groupList = implode(',', $this->userGroupsUID);
1319 $this->setCachedList($this->groupList);
1320 // Checking read access to webmounts:
1321 if (trim($this->groupData['webmounts']) !== '') {
1322 $webmounts = explode(',', $this->groupData['webmounts']);
1323 // Explode mounts
1324 // Selecting all webmounts with permission clause for reading
1325 $MProws = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid', 'pages', 'deleted=0 AND uid IN (' . $this->groupData['webmounts'] . ') AND ' . $this->getPagePermsClause(1), '', '', '', 'uid');
1326 foreach ($webmounts as $idx => $mountPointUid) {
1327 // If the mount ID is NOT found among selected pages, unset it:
1328 if ($mountPointUid > 0 && !isset($MProws[$mountPointUid])) {
1329 unset($webmounts[$idx]);
1330 }
1331 }
1332 // Implode mounts in the end.
1333 $this->groupData['webmounts'] = implode(',', $webmounts);
1334 }
1335 // Setting up workspace situation (after webmounts are processed!):
1336 $this->workspaceInit();
1337 }
1338 }
1339
1340 /**
1341 * Fetches the group records, subgroups and fills internal arrays.
1342 * Function is called recursively to fetch subgroups
1343 *
1344 * @param string $grList Commalist of be_groups uid numbers
1345 * @param string $idList List of already processed be_groups-uids so the function will not fall into a eternal recursion.
1346 * @return void
1347 * @access private
1348 * @todo Define visibility
1349 */
1350 public function fetchGroups($grList, $idList = '') {
1351 // Fetching records of the groups in $grList (which are not blocked by lockedToDomain either):
1352 $lockToDomain_SQL = ' AND (lockToDomain=\'\' OR lockToDomain IS NULL OR lockToDomain=\'' . GeneralUtility::getIndpEnv('HTTP_HOST') . '\')';
1353 $whereSQL = 'deleted=0 AND hidden=0 AND pid=0 AND uid IN (' . $grList . ')' . $lockToDomain_SQL;
1354 // Hook for manipulation of the WHERE sql sentence which controls which BE-groups are included
1355 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroupQuery'])) {
1356 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroupQuery'] as $classRef) {
1357 $hookObj = GeneralUtility::getUserObj($classRef);
1358 if (method_exists($hookObj, 'fetchGroupQuery_processQuery')) {
1359 $whereSQL = $hookObj->fetchGroupQuery_processQuery($this, $grList, $idList, $whereSQL);
1360 }
1361 }
1362 }
1363 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', $this->usergroup_table, $whereSQL);
1364 // The userGroups array is filled
1365 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
1366 $this->userGroups[$row['uid']] = $row;
1367 }
1368 $GLOBALS['TYPO3_DB']->sql_free_result($res);
1369 // Traversing records in the correct order
1370 $include_staticArr = GeneralUtility::intExplode(',', $grList);
1371 // Traversing list
1372 foreach ($include_staticArr as $key => $uid) {
1373 // Get row:
1374 $row = $this->userGroups[$uid];
1375 // Must be an array and $uid should not be in the idList, because then it is somewhere previously in the grouplist
1376 if (is_array($row) && !GeneralUtility::inList($idList, $uid)) {
1377 // Include sub groups
1378 if (trim($row['subgroup'])) {
1379 // Make integer list
1380 $theList = implode(',', GeneralUtility::intExplode(',', $row['subgroup']));
1381 // Call recursively, pass along list of already processed groups so they are not recursed again.
1382 $this->fetchGroups($theList, $idList . ',' . $uid);
1383 }
1384 // Add the group uid, current list, TSconfig to the internal arrays.
1385 $this->includeGroupArray[] = $uid;
1386 $this->includeHierarchy[] = $idList;
1387 $this->TSdataArray[] = $this->addTScomment(('Group "' . $row['title'] . '" [' . $row['uid'] . '] TSconfig field:')) . $row['TSconfig'];
1388 // Mount group database-mounts
1389 if (($this->user['options'] & 1) == 1) {
1390 $this->dataLists['webmount_list'] .= ',' . $row['db_mountpoints'];
1391 }
1392 // Mount group file-mounts
1393 if (($this->user['options'] & 2) == 2) {
1394 $this->dataLists['filemount_list'] .= ',' . $row['file_mountpoints'];
1395 }
1396 // The lists are made: groupMods, tables_select, tables_modify, pagetypes_select, non_exclude_fields, explicit_allowdeny, allowed_languages, custom_options
1397 if ($row['inc_access_lists'] == 1) {
1398 $this->dataLists['modList'] .= ',' . $row['groupMods'];
1399 $this->dataLists['tables_select'] .= ',' . $row['tables_select'];
1400 $this->dataLists['tables_modify'] .= ',' . $row['tables_modify'];
1401 $this->dataLists['pagetypes_select'] .= ',' . $row['pagetypes_select'];
1402 $this->dataLists['non_exclude_fields'] .= ',' . $row['non_exclude_fields'];
1403 $this->dataLists['explicit_allowdeny'] .= ',' . $row['explicit_allowdeny'];
1404 $this->dataLists['allowed_languages'] .= ',' . $row['allowed_languages'];
1405 $this->dataLists['custom_options'] .= ',' . $row['custom_options'];
1406 }
1407 // Setting fileoperation permissions
1408 $this->dataLists['fileoper_perms'] |= (int) $row['fileoper_perms'];
1409 // Setting workspace permissions:
1410 $this->dataLists['workspace_perms'] |= $row['workspace_perms'];
1411 // If this function is processing the users OWN group-list (not subgroups) AND if the ->firstMainGroup is not set, then the ->firstMainGroup will be set.
1412 if (!strcmp($idList, '') && !$this->firstMainGroup) {
1413 $this->firstMainGroup = $uid;
1414 }
1415 }
1416 }
1417 // HOOK: fetchGroups_postProcessing
1418 if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroups_postProcessing'])) {
1419 foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroups_postProcessing'] as $_funcRef) {
1420 $_params = array();
1421 GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1422 }
1423 }
1424 }
1425
1426 /**
1427 * Updates the field be_users.usergroup_cached_list if the groupList of the user has changed/is different from the current list.
1428 * The field "usergroup_cached_list" contains the list of groups which the user is a member of. After authentication (where these functions are called...) one can depend on this list being a representation of the exact groups/subgroups which the BE_USER has membership with.
1429 *
1430 * @param string $cList The newly compiled group-list which must be compared with the current list in the user record and possibly stored if a difference is detected.
1431 * @return void
1432 * @access private
1433 * @todo Define visibility
1434 */
1435 public function setCachedList($cList) {
1436 if ((string) $cList != (string) $this->user['usergroup_cached_list']) {
1437 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('be_users', 'uid=' . intval($this->user['uid']), array('usergroup_cached_list' => $cList));
1438 }
1439 }
1440
1441 /**
1442 * Sets up all file storages for a user.
1443 * Needs to be called AFTER the groups have been loaded.
1444 *
1445 * @return void
1446 */
1447 protected function initializeFileStorages() {
1448 $this->fileStorages = array();
1449 /** @var $storageRepository \TYPO3\CMS\Core\Resource\StorageRepository */
1450 $storageRepository = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\StorageRepository');
1451 // Admin users have all file storages visible, without any filters
1452 if ($this->isAdmin()) {
1453 $storageObjects = $storageRepository->findAll();
1454 foreach ($storageObjects as $storageObject) {
1455 $this->fileStorages[$storageObject->getUid()] = $storageObject;
1456 }
1457 } else {
1458 // Regular users only have storages that are defined in their filemounts
1459 // Permissions and file mounts for the storage are added in StoragePermissionAspect
1460 foreach ($this->getFileMountRecords() as $row) {
1461 if (!array_key_exists(intval($row['base']), $this->fileStorages)) {
1462 $storageObject = $storageRepository->findByUid($row['base']);
1463 $this->fileStorages[$storageObject->getUid()] = $storageObject;
1464 }
1465 }
1466 }
1467
1468 // This has to be called always in order to set certain filters
1469 $this->evaluateUserSpecificFileFilterSettings();
1470 }
1471
1472 /**
1473 * Returns an array of file mount records, taking workspaces and user home and group home directories into account
1474 * Needs to be called AFTER the groups have been loaded.
1475 *
1476 * @return array
1477 * @internal
1478 */
1479 public function getFileMountRecords() {
1480 static $fileMountRecords = array();
1481
1482 if (empty($fileMountRecords)) {
1483 // Processing filemounts (both from the user and the groups)
1484 $fileMounts = array_unique(GeneralUtility::intExplode(',', $this->dataLists['filemount_list'], TRUE));
1485
1486 // Limit filemounts if set in workspace record
1487 if ($this->workspace > 0 && !empty($this->workspaceRec['file_mountpoints'])) {
1488 $workspaceFileMounts = GeneralUtility::intExplode(',', $this->workspaceRec['file_mountpoints'], TRUE);
1489 $fileMounts = array_intersect($fileMounts, $workspaceFileMounts);
1490 }
1491
1492 if (!empty($fileMounts)) {
1493 $orderBy = $GLOBALS['TCA']['sys_filemounts']['ctrl']['default_sortby'] ? $GLOBALS['TYPO3_DB']->stripOrderBy($GLOBALS['TCA']['sys_filemounts']['ctrl']['default_sortby']) : 'sorting';
1494 $fileMountRecords = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
1495 '*',
1496 'sys_filemounts',
1497 'deleted=0 AND hidden=0 AND pid=0 AND uid IN (' . implode(',', $fileMounts) . ')',
1498 '',
1499 $orderBy
1500 );
1501 }
1502
1503 // Personal or Group filemounts are not accessible if file mount list is set in workspace record
1504 if ($this->workspace <= 0 || empty($this->workspaceRec['file_mountpoints'])) {
1505 // If userHomePath is set, we attempt to mount it
1506 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['userHomePath']) {
1507 list($userHomeStorageUid, $userHomeFilter) = explode(':', $GLOBALS['TYPO3_CONF_VARS']['BE']['userHomePath'], 2);
1508 $userHomeStorageUid = intval($userHomeStorageUid);
1509 $userHomeFilter = '/' . ltrim($userHomeFilter, '/');
1510 if ($userHomeStorageUid > 0) {
1511 // Try and mount with [uid]_[username]
1512 $fileMountRecords[] = array(
1513 'base' => $userHomeStorageUid,
1514 'title' => $this->user['username'],
1515 'path' => $userHomeFilter . $this->user['uid'] . '_' . $this->user['username'] . $GLOBALS['TYPO3_CONF_VARS']['BE']['userUploadDir']
1516 );
1517 // Try and mount with only [uid]
1518 $fileMountRecords[] = array(
1519 'base' => $userHomeStorageUid,
1520 'title' => $this->user['username'],
1521 'path' => $userHomeFilter . $this->user['uid'] . $GLOBALS['TYPO3_CONF_VARS']['BE']['userUploadDir']
1522 );
1523 }
1524 }
1525
1526 // Mount group home-dirs
1527 if (($this->user['options'] & 2) == 2 && $GLOBALS['TYPO3_CONF_VARS']['BE']['groupHomePath'] != '') {
1528 // If groupHomePath is set, we attempt to mount it
1529 list($groupHomeStorageUid, $groupHomeFilter) = explode(':', $GLOBALS['TYPO3_CONF_VARS']['BE']['groupHomePath'], 2);
1530 $groupHomeStorageUid = intval($groupHomeStorageUid);
1531 $groupHomeFilter = '/' . ltrim($groupHomeFilter, '/');
1532 if ($groupHomeStorageUid > 0) {
1533 foreach ($this->userGroups as $groupData) {
1534 $fileMountRecords[] = array(
1535 'base' => $groupHomeStorageUid,
1536 'title' => $groupData['title'],
1537 'path' => $groupHomeFilter . $groupData['uid']
1538 );
1539 }
1540 }
1541 }
1542 }
1543 }
1544
1545 return $fileMountRecords;
1546 }
1547
1548 /**
1549 * Returns an array with the filemounts for the user.
1550 * Each filemount is represented with an array of a "name", "path" and "type".
1551 * If no filemounts an empty array is returned.
1552 *
1553 * @api
1554 * @return \TYPO3\CMS\Core\Resource\ResourceStorage[]
1555 */
1556 public function getFileStorages() {
1557 // Initializing file mounts after the groups are fetched
1558 if ($this->fileStorages === NULL) {
1559 $this->initializeFileStorages();
1560 }
1561 return $this->fileStorages;
1562 }
1563
1564 /**
1565 * Adds filters based on what the user has set
1566 * this should be done in this place, and called whenever needed,
1567 * but only when needed
1568 *
1569 * @return void
1570 */
1571 public function evaluateUserSpecificFileFilterSettings() {
1572 // Add the option for also displaying the non-hidden files
1573 if ($this->uc['showHiddenFilesAndFolders']) {
1574 \TYPO3\CMS\Core\Resource\Filter\FileNameFilter::setShowHiddenFilesAndFolders(TRUE);
1575 }
1576 }
1577
1578 /**
1579 * Returns the information about file permissions
1580 * previously, this was stored in the DB fields (fileoper_perms)
1581 * but is now handled via userTSconfig
1582 *
1583 * permissions.file.default {
1584 * addFile = 1
1585 * readFile = 1
1586 * writeFile = 1
1587 * copyFile = 1
1588 * moveFile = 1
1589 * renameFile = 1
1590 * unzipFile = 1
1591 * deleteFile = 1
1592 *
1593 * addFolder = 1
1594 * readFolder = 1
1595 * writeFolder = 1
1596 * copyFolder = 1
1597 * moveFolder = 1
1598 * renameFolder = 1
1599 * deleteFolder = 1
1600 * recursivedeleteFolder = 1
1601 * }
1602 *
1603 * # overwrite settings for a specific storageObject
1604 * permissions.file.storage.StorageUid {
1605 * readFile = 1
1606 * recursivedeleteFolder = 0
1607 * }
1608 *
1609 * Please note that these permissions only apply, if the storage has the
1610 * capabilities (browseable, writable), and if the driver allows for writing etc
1611 *
1612 * @api
1613 * @return array
1614 */
1615 public function getFilePermissions() {
1616 if (!isset($this->filePermissions)) {
1617 $defaultOptions = array(
1618 // File permissions
1619 'addFile' => TRUE,
1620 'readFile' => TRUE,
1621 'writeFile' => TRUE,
1622 'copyFile' => TRUE,
1623 'moveFile' => TRUE,
1624 'renameFile' => TRUE,
1625 'unzipFile' => TRUE,
1626 'deleteFile' => TRUE,
1627 // Folder permissions
1628 'addFolder' => TRUE,
1629 'readFolder' => TRUE,
1630 'writeFolder' => TRUE,
1631 'copyFolder' => TRUE,
1632 'moveFolder' => TRUE,
1633 'renameFolder' => TRUE,
1634 'deleteFolder' => TRUE,
1635 'recursivedeleteFolder' => TRUE
1636 );
1637 if (!$this->isAdmin()) {
1638 $defaultPermissionsTsConfig = $this->getTSConfigProp('permissions.file.default');
1639 if (!empty($defaultPermissionsTsConfig)) {
1640 $defaultOptions = $defaultPermissionsTsConfig;
1641 } else {
1642 $oldFileOperationPermissions = $this->getFileoperationPermissions();
1643 // Lower permissions if the old file operation permissions are not set
1644 if (!($oldFileOperationPermissions & 1)) {
1645 $defaultOptions['addFile'] = FALSE;
1646 $defaultOptions['writeFile'] = FALSE;
1647 $defaultOptions['copyFile'] = FALSE;
1648 $defaultOptions['moveFile'] = FALSE;
1649 $defaultOptions['renameFile'] = FALSE;
1650 $defaultOptions['deleteFile'] = FALSE;
1651 }
1652 if (!($oldFileOperationPermissions & 2)) {
1653 $defaultOptions['unzipFile'] = FALSE;
1654 }
1655 if (!($oldFileOperationPermissions & 4)) {
1656 $defaultOptions['addFolder'] = FALSE;
1657 $defaultOptions['writeFolder'] = FALSE;
1658 $defaultOptions['moveFolder'] = FALSE;
1659 $defaultOptions['renameFolder'] = FALSE;
1660 $defaultOptions['deleteFolder'] = FALSE;
1661 }
1662 if (!($oldFileOperationPermissions & 8)) {
1663 $defaultOptions['copyFolder'] = FALSE;
1664 }
1665 if (!($oldFileOperationPermissions & 16)) {
1666 $defaultOptions['recursivedeleteFolder'] = FALSE;
1667 }
1668 }
1669 }
1670 $this->filePermissions = $defaultOptions;
1671 }
1672 return $this->filePermissions;
1673 }
1674
1675 /**
1676 * Gets the file permissions for a storage
1677 * by merging any storage-specific permissions for a
1678 * storage with the default settings.
1679 * Admin users will always get the default settings.
1680 *
1681 * @api
1682 * @param \TYPO3\CMS\Core\Resource\ResourceStorage $storageObject
1683 * @return array
1684 */
1685 public function getFilePermissionsForStorage(\TYPO3\CMS\Core\Resource\ResourceStorage $storageObject) {
1686 $finalUserPermissions = $this->getFilePermissions();
1687 if (!$this->isAdmin()) {
1688 $storageFilePermissions = $this->getTSConfigProp('permissions.file.storage.' . $storageObject->getUid());
1689 if (!empty($storageFilePermissions)) {
1690 $finalUserPermissions = array_merge($finalUserPermissions, $storageFilePermissions);
1691 }
1692 }
1693 return $finalUserPermissions;
1694 }
1695
1696 /**
1697 * Returns a \TYPO3\CMS\Core\Resource\Folder object that is used for uploading
1698 * files by default.
1699 * This is used for RTE and its magic images, as well as uploads
1700 * in the TCEforms fields, unless otherwise configured (will be added in the future)
1701 *
1702 * the default upload folder for a user is the defaultFolder on the first
1703 * filestorage/filemount that the user can access
1704 * however, you can set the users' upload folder like this:
1705 *
1706 * options.defaultUploadFolder = 3:myfolder/yourfolder/
1707 *
1708 * @return \TYPO3\CMS\Core\Resource\Folder|boolean The default upload folder for this user
1709 */
1710 public function getDefaultUploadFolder() {
1711 $uploadFolder = $this->getTSConfigVal('options.defaultUploadFolder');
1712 if ($uploadFolder) {
1713 $uploadFolder = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier($uploadFolder);
1714 } else {
1715 $storages = $this->getFileStorages();
1716 if (count($storages) > 0) {
1717 /** @var $firstStorage \TYPO3\CMS\Core\Resource\ResourceStorage */
1718 $firstStorage = reset($storages);
1719 $uploadFolder = $firstStorage->getDefaultFolder();
1720 }
1721 }
1722 if ($uploadFolder instanceof \TYPO3\CMS\Core\Resource\Folder) {
1723 return $uploadFolder;
1724 } else {
1725 return FALSE;
1726 }
1727 }
1728
1729 /**
1730 * Returns an integer bitmask that represents the permissions for file operations.
1731 * Permissions of the user and groups the user is a member of were combined by a logical OR.
1732 *
1733 * Meaning of each bit:
1734 * 1 - Files: Upload,Copy,Move,Delete,Rename
1735 * 2 - Files: Unzip
1736 * 4 - Directory: Move,Delete,Rename,New
1737 * 8 - Directory: Copy
1738 * 16 - Directory: Delete recursively (rm -Rf)
1739 *
1740 * @return integer File operation permission bitmask
1741 * @deprecated since TYPO3 6.0, use the TSconfig settings instead
1742 */
1743 public function getFileoperationPermissions() {
1744 GeneralUtility::logDeprecatedFunction();
1745 if ($this->isAdmin()) {
1746 return 31;
1747 } else {
1748 return $this->groupData['fileoper_perms'];
1749 }
1750 }
1751
1752 /**
1753 * Adds a filemount to the users array of filemounts, $this->groupData['filemounts'][hash_key] = Array ('name'=>$name, 'path'=>$path, 'type'=>$type);
1754 * Is a part of the authentication proces of the user.
1755 * A final requirement for a path being mounted is that a) it MUST return TRUE on is_dir(), b) must contain either PATH_site+'fileadminDir' OR 'lockRootPath' - if lockRootPath is set - as first part of string!
1756 * Paths in the mounted information will always be absolute and have a trailing slash.
1757 *
1758 * @param string $title Will be the (root)name of the filemount in the folder tree
1759 * @param string $altTitle Will be the (root)name of the filemount IF $title is not TRUE (blank or zero)
1760 * @param string $path Is the path which should be mounted. Will accept backslash in paths on windows servers (will substituted with forward slash). The path should be 1) relative to TYPO3_CONF_VARS[BE][fileadminDir] if $webspace is set, otherwise absolute.
1761 * @param boolean $webspace If $webspace is set, the $path is relative to 'fileadminDir' in TYPO3_CONF_VARS, otherwise $path is absolute. 'fileadminDir' must be set to allow mounting of relative paths.
1762 * @param string $type Type of filemount; Can be blank (regular) or "user" / "group" (for user and group filemounts presumably). Probably sets the icon first and foremost.
1763 * @return boolean Returns "1" if the requested filemount was mounted, otherwise no return value.
1764 * @deprecated since TYPO3 6.0, will be removed in TYPO3 6.1, all data is stored in $this->fileStorages now, see initializeFileStorages()
1765 * @access private
1766 * @todo Define visibility
1767 */
1768 public function addFileMount($title, $altTitle, $path, $webspace, $type) {
1769 GeneralUtility::logDeprecatedFunction();
1770 // Return FALSE if fileadminDir is not set and we try to mount a relative path
1771 if ($webspace && !$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']) {
1772 return FALSE;
1773 }
1774 // Trimming and pre-processing
1775 $path = trim($path);
1776 // with WINDOWS convert backslash to slash!!
1777 if ($this->OS == 'WIN') {
1778 $path = str_replace('\\', '/', $path);
1779 }
1780 // If the path is TRUE and validates as a valid path string:
1781 if ($path && GeneralUtility::validPathStr($path)) {
1782 // normalize path: remove leading '/' and './', and trailing '/' and '/.'
1783 $path = trim($path);
1784 $path = preg_replace('#^\\.?/|/\\.?$#', '', $path);
1785 // There must be some chars in the path
1786 if ($path) {
1787 // Fileadmin dir, absolute
1788 $fdir = PATH_site . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'];
1789 if ($webspace) {
1790 $path = $fdir . $path;
1791 } else {
1792 // With WINDOWS no prepending!!
1793 if ($this->OS != 'WIN') {
1794 // With WINDOWS no prepending!!
1795 $path = '/' . $path;
1796 }
1797 }
1798 $path .= '/';
1799 // We now have a path with slash after and slash before (if unix)
1800 if (@is_dir($path) && ($GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath'] && GeneralUtility::isFirstPartOfStr($path, $GLOBALS['TYPO3_CONF_VARS']['BE']['lockRootPath']) || GeneralUtility::isFirstPartOfStr($path, $fdir))) {
1801 // Alternative title?
1802 $name = $title ? $title : $altTitle;
1803 // Adds the filemount. The same filemount with same name, type and path cannot be set up twice because of the hash string used as key.
1804 $this->groupData['filemounts'][md5($name . '|' . $path . '|' . $type)] = array('name' => $name, 'path' => $path, 'type' => $type);
1805 // Return TRUE - went well, success!
1806 return 1;
1807 }
1808 }
1809 }
1810 }
1811
1812 /**
1813 * Returns an array with the filemounts for the user. Each filemount is represented with an array of a "name", "path" and "type".
1814 * If no filemounts an empty array is returned.
1815 *
1816 * @return array
1817 * @deprecated since TYPO3 6.0, will be removed in TYPO3 6.1 as getFileStorages() should be the one to be used
1818 * @todo Define visibility
1819 */
1820 public function returnFilemounts() {
1821 GeneralUtility::logDeprecatedFunction();
1822 // initialize the file storages in order to set some default settings in any time
1823 $this->getFileStorages();
1824 return $this->groupData['filemounts'];
1825 }
1826
1827 /**
1828 * Creates a TypoScript comment with the string text inside.
1829 *
1830 * @param string $str The text to wrap in comment prefixes and delimiters.
1831 * @return string TypoScript comment with the string text inside.
1832 * @todo Define visibility
1833 */
1834 public function addTScomment($str) {
1835 $delimiter = '# ***********************************************';
1836 $out = $delimiter . LF;
1837 $lines = GeneralUtility::trimExplode(LF, $str);
1838 foreach ($lines as $v) {
1839 $out .= '# ' . $v . LF;
1840 }
1841 $out .= $delimiter . LF;
1842 return $out;
1843 }
1844
1845 /**
1846 * Initializing workspace.
1847 * Called from within this function, see fetchGroupData()
1848 *
1849 * @return void
1850 * @see fetchGroupData()
1851 * @todo Define visibility
1852 */
1853 public function workspaceInit() {
1854 // Initializing workspace by evaluating and setting the workspace, possibly updating it in the user record!
1855 $this->setWorkspace($this->user['workspace_id']);
1856 // Limiting the DB mountpoints if there any selected in the workspace record
1857 $this->initializeDbMountpointsInWorkspace();
1858 if ($allowed_languages = $this->getTSConfigVal('options.workspaces.allowed_languages.' . $this->workspace)) {
1859 $this->groupData['allowed_languages'] = $allowed_languages;
1860 $this->groupData['allowed_languages'] = GeneralUtility::uniqueList($this->groupData['allowed_languages']);
1861 }
1862 }
1863
1864 /**
1865 * Limiting the DB mountpoints if there any selected in the workspace record
1866 *
1867 * @return void
1868 */
1869 protected function initializeDbMountpointsInWorkspace() {
1870 $dbMountpoints = trim($this->workspaceRec['db_mountpoints']);
1871 if ($this->workspace > 0 && $dbMountpoints != '') {
1872 $filteredDbMountpoints = array();
1873 // Notice: We cannot call $this->getPagePermsClause(1); as usual because the group-list is not available at this point. But bypassing is fine because all we want here is check if the workspace mounts are inside the current webmounts rootline.
1874 // The actual permission checking on page level is done elsewhere as usual anyway before the page tree is rendered.
1875 $readPerms = '1=1';
1876 // Traverse mount points of the
1877 $dbMountpoints = GeneralUtility::intExplode(',', $dbMountpoints);
1878 foreach ($dbMountpoints as $mpId) {
1879 if ($this->isInWebMount($mpId, $readPerms)) {
1880 $filteredDbMountpoints[] = $mpId;
1881 }
1882 }
1883 // Re-insert webmounts:
1884 $filteredDbMountpoints = array_unique($filteredDbMountpoints);
1885 $this->groupData['webmounts'] = implode(',', $filteredDbMountpoints);
1886 }
1887 }
1888
1889 /**
1890 * Checking if a workspace is allowed for backend user
1891 *
1892 * @param mixed $wsRec If integer, workspace record is looked up, if array it is seen as a Workspace record with at least uid, title, members and adminusers columns. Can be faked for workspaces uid 0 and -1 (online and offline)
1893 * @param string $fields List of fields to select. Default fields are: uid,title,adminusers,members,reviewers,publish_access,stagechg_notification
1894 * @return array TRUE if access. Output will also show how access was granted. Admin users will have a true output regardless of input.
1895 * @todo Define visibility
1896 */
1897 public function checkWorkspace($wsRec, $fields = 'uid,title,adminusers,members,reviewers,publish_access,stagechg_notification') {
1898 $retVal = FALSE;
1899 // If not array, look up workspace record:
1900 if (!is_array($wsRec)) {
1901 switch ((string) $wsRec) {
1902 case '0':
1903 $wsRec = array('uid' => $wsRec);
1904 break;
1905 default:
1906 if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
1907 $wsRec = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow($fields, 'sys_workspace', 'pid=0 AND uid=' . intval($wsRec) . BackendUtility::deleteClause('sys_workspace'), '', 'title');
1908 }
1909 }
1910 }
1911 // If wsRec is set to an array, evaluate it:
1912 if (is_array($wsRec)) {
1913 if ($this->isAdmin()) {
1914 return array_merge($wsRec, array('_ACCESS' => 'admin'));
1915 } else {
1916 switch ((string) $wsRec['uid']) {
1917 case '0':
1918 $retVal = $this->groupData['workspace_perms'] & 1 ? array_merge($wsRec, array('_ACCESS' => 'online')) : FALSE;
1919 break;
1920 default:
1921 // Checking if the guy is admin:
1922 if (GeneralUtility::inList($wsRec['adminusers'], 'be_users_' . $this->user['uid'])) {
1923 return array_merge($wsRec, array('_ACCESS' => 'owner'));
1924 }
1925 // Checking if he is owner through a user group of his:
1926 foreach ($this->userGroupsUID as $groupUid) {
1927 if (GeneralUtility::inList($wsRec['adminusers'], 'be_groups_' . $groupUid)) {
1928 return array_merge($wsRec, array('_ACCESS' => 'owner'));
1929 }
1930 }
1931 // Checking if he is reviewer user:
1932 if (GeneralUtility::inList($wsRec['reviewers'], 'be_users_' . $this->user['uid'])) {
1933 return array_merge($wsRec, array('_ACCESS' => 'reviewer'));
1934 }
1935 // Checking if he is reviewer through a user group of his:
1936 foreach ($this->userGroupsUID as $groupUid) {
1937 if (GeneralUtility::inList($wsRec['reviewers'], 'be_groups_' . $groupUid)) {
1938 return array_merge($wsRec, array('_ACCESS' => 'reviewer'));
1939 }
1940 }
1941 // Checking if he is member as user:
1942 if (GeneralUtility::inList($wsRec['members'], 'be_users_' . $this->user['uid'])) {
1943 return array_merge($wsRec, array('_ACCESS' => 'member'));
1944 }
1945 // Checking if he is member through a user group of his:
1946 foreach ($this->userGroupsUID as $groupUid) {
1947 if (GeneralUtility::inList($wsRec['members'], 'be_groups_' . $groupUid)) {
1948 return array_merge($wsRec, array('_ACCESS' => 'member'));
1949 }
1950 }
1951 }
1952 }
1953 }
1954 return $retVal;
1955 }
1956
1957 /**
1958 * Uses checkWorkspace() to check if current workspace is available for user. This function caches the result and so can be called many times with no performance loss.
1959 *
1960 * @return array See checkWorkspace()
1961 * @see checkWorkspace()
1962 * @todo Define visibility
1963 */
1964 public function checkWorkspaceCurrent() {
1965 if (!isset($this->checkWorkspaceCurrent_cache)) {
1966 $this->checkWorkspaceCurrent_cache = $this->checkWorkspace($this->workspace);
1967 }
1968 return $this->checkWorkspaceCurrent_cache;
1969 }
1970
1971 /**
1972 * Setting workspace ID
1973 *
1974 * @param integer $workspaceId ID of workspace to set for backend user. If not valid the default workspace for BE user is found and set.
1975 * @return void
1976 * @todo Define visibility
1977 */
1978 public function setWorkspace($workspaceId) {
1979 // Check workspace validity and if not found, revert to default workspace.
1980 if ($this->workspaceRec = $this->checkWorkspace($workspaceId, '*')) {
1981 // Set workspace ID internally
1982 $this->workspace = (int) $workspaceId;
1983 } else {
1984 $this->workspace = (int) $this->getDefaultWorkspace();
1985 $this->workspaceRec = $this->checkWorkspace($this->workspace, '*');
1986 }
1987 // Unset access cache:
1988 unset($this->checkWorkspaceCurrent_cache);
1989 // If ID is different from the stored one, change it:
1990 if (strcmp($this->workspace, $this->user['workspace_id'])) {
1991 $this->user['workspace_id'] = $this->workspace;
1992 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('be_users', 'uid=' . intval($this->user['uid']), array('workspace_id' => $this->user['workspace_id']));
1993 $this->simplelog('User changed workspace to "' . $this->workspace . '"');
1994 }
1995 }
1996
1997 /**
1998 * Setting workspace preview state for user:
1999 *
2000 * @param boolean $previewState State of user preview.
2001 * @return void
2002 * @todo Define visibility
2003 */
2004 public function setWorkspacePreview($previewState) {
2005 $this->user['workspace_preview'] = $previewState;
2006 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('be_users', 'uid=' . intval($this->user['uid']), array('workspace_preview' => $this->user['workspace_preview']));
2007 }
2008
2009 /**
2010 * Return default workspace ID for user,
2011 * If EXT:workspaces is not installed the user will be pushed the the
2012 * Live workspace
2013 *
2014 * @return integer Default workspace id. If no workspace is available it will be "-99
2015 * @todo Define visibility
2016 */
2017 public function getDefaultWorkspace() {
2018 $defaultWorkspace = -99;
2019 if (!\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces') || $this->checkWorkspace(0)) {
2020 // Check online
2021 $defaultWorkspace = 0;
2022 } elseif ($this->checkWorkspace(-1)) {
2023 // Check offline
2024 $defaultWorkspace = -1;
2025 } elseif (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
2026 // Traverse custom workspaces:
2027 $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,title,adminusers,members,reviewers', 'sys_workspace', 'pid=0' . BackendUtility::deleteClause('sys_workspace'), '', 'title');
2028 foreach ($workspaces as $rec) {
2029 if ($this->checkWorkspace($rec)) {
2030 $defaultWorkspace = $rec['uid'];
2031 break;
2032 }
2033 }
2034 }
2035 return $defaultWorkspace;
2036 }
2037
2038 /**
2039 * Writes an entry in the logfile/table
2040 * Documentation in "TYPO3 Core API"
2041 *
2042 * @param integer $type Denotes which module that has submitted the entry. See "TYPO3 Core API". Use "4" for extensions.
2043 * @param integer $action Denotes which specific operation that wrote the entry. Use "0" when no sub-categorizing applies
2044 * @param integer $error Flag. 0 = message, 1 = error (user problem), 2 = System Error (which should not happen), 3 = security notice (admin)
2045 * @param integer $details_nr The message number. Specific for each $type and $action. This will make it possible to translate errormessages to other languages
2046 * @param string $details Default text that follows the message (in english!). Possibly translated by identification through type/action/details_nr
2047 * @param array $data Data that follows the log. Might be used to carry special information. If an array the first 5 entries (0-4) will be sprintf'ed with the details-text
2048 * @param string $tablename Table name. Special field used by tce_main.php.
2049 * @param integer $recuid Record UID. Special field used by tce_main.php.
2050 * @param integer $recpid Record PID. Special field used by tce_main.php. OBSOLETE
2051 * @param integer $event_pid The page_uid (pid) where the event occurred. Used to select log-content for specific pages.
2052 * @param string $NEWid Special field used by tce_main.php. NEWid string of newly created records.
2053 * @param integer $userId Alternative Backend User ID (used for logging login actions where this is not yet known).
2054 * @return integer Log entry ID.
2055 * @todo Define visibility
2056 */
2057 public function writelog($type, $action, $error, $details_nr, $details, $data, $tablename = '', $recuid = '', $recpid = '', $event_pid = -1, $NEWid = '', $userId = 0) {
2058 $fields_values = array(
2059 'userid' => $userId ? $userId : intval($this->user['uid']),
2060 'type' => intval($type),
2061 'action' => intval($action),
2062 'error' => intval($error),
2063 'details_nr' => intval($details_nr),
2064 'details' => $details,
2065 'log_data' => serialize($data),
2066 'tablename' => $tablename,
2067 'recuid' => intval($recuid),
2068 'IP' => GeneralUtility::getIndpEnv('REMOTE_ADDR'),
2069 'tstamp' => $GLOBALS['EXEC_TIME'],
2070 'event_pid' => intval($event_pid),
2071 'NEWid' => $NEWid,
2072 'workspace' => $this->workspace
2073 );
2074 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_log', $fields_values);
2075 return $GLOBALS['TYPO3_DB']->sql_insert_id();
2076 }
2077
2078 /**
2079 * Simple logging function
2080 *
2081 * @param string $message Log message
2082 * @param string $extKey Option extension key / module name
2083 * @param integer $error Error level. 0 = message, 1 = error (user problem), 2 = System Error (which should not happen), 3 = security notice (admin)
2084 * @return integer Log entry UID
2085 * @todo Define visibility
2086 */
2087 public function simplelog($message, $extKey = '', $error = 0) {
2088 return $this->writelog(4, 0, $error, 0, ($extKey ? '[' . $extKey . '] ' : '') . $message, array());
2089 }
2090
2091 /**
2092 * Sends a warning to $email if there has been a certain amount of failed logins during a period.
2093 * If a login fails, this function is called. It will look up the sys_log to see if there has been more than $max failed logins the last $secondsBack seconds (default 3600). If so, an email with a warning is sent to $email.
2094 *
2095 * @param string $email Email address
2096 * @param integer $secondsBack Number of sections back in time to check. This is a kind of limit for how many failures an hour for instance.
2097 * @param integer $max Max allowed failures before a warning mail is sent
2098 * @return void
2099 * @access private
2100 * @todo Define visibility
2101 */
2102 public function checkLogFailures($email, $secondsBack = 3600, $max = 3) {
2103 if ($email) {
2104 // Get last flag set in the log for sending
2105 $theTimeBack = $GLOBALS['EXEC_TIME'] - $secondsBack;
2106 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('tstamp', 'sys_log', 'type=255 AND action=4 AND tstamp>' . intval($theTimeBack), '', 'tstamp DESC', '1');
2107 if ($testRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
2108 $theTimeBack = $testRow['tstamp'];
2109 }
2110 $GLOBALS['TYPO3_DB']->sql_free_result($res);
2111 // Check for more than $max number of error failures with the last period.
2112 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'sys_log', 'type=255 AND action=3 AND error<>0 AND tstamp>' . intval($theTimeBack), '', 'tstamp');
2113 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res) > $max) {
2114 // OK, so there were more than the max allowed number of login failures - so we will send an email then.
2115 $subject = 'TYPO3 Login Failure Warning (at ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ')';
2116 $email_body = 'There have been some attempts (' . $GLOBALS['TYPO3_DB']->sql_num_rows($res) . ') to login at the TYPO3
2117 site "' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '" (' . GeneralUtility::getIndpEnv('HTTP_HOST') . ').
2118
2119 This is a dump of the failures:
2120
2121 ';
2122 while ($testRows = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
2123 $theData = unserialize($testRows['log_data']);
2124 $email_body .= date(($GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm']), $testRows['tstamp']) . ': ' . @sprintf($testRows['details'], ('' . $theData[0]), ('' . $theData[1]), ('' . $theData[2]));
2125 $email_body .= LF;
2126 }
2127 $from = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFrom();
2128 /** @var $mail \TYPO3\CMS\Core\Mail\MailMessage */
2129 $mail = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\MailMessage');
2130 $mail->setTo($email)->setFrom($from)->setSubject($subject)->setBody($email_body);
2131 $mail->send();
2132 // Logout written to log
2133 $this->writelog(255, 4, 0, 3, 'Failure warning (%s failures within %s seconds) sent by email to %s', array($GLOBALS['TYPO3_DB']->sql_num_rows($res), $secondsBack, $email));
2134 $GLOBALS['TYPO3_DB']->sql_free_result($res);
2135 }
2136 }
2137 }
2138
2139 /**
2140 * Getter for the cookie name
2141 *
2142 * @static
2143 * @return string returns the configured cookie name
2144 */
2145 static public function getCookieName() {
2146 $configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['cookieName']);
2147 if (empty($configuredCookieName)) {
2148 $configuredCookieName = 'be_typo_user';
2149 }
2150 return $configuredCookieName;
2151 }
2152
2153 /**
2154 * If TYPO3_CONF_VARS['BE']['enabledBeUserIPLock'] is enabled and
2155 * an IP-list is found in the User TSconfig objString "options.lockToIP",
2156 * then make an IP comparison with REMOTE_ADDR and return the outcome (TRUE/FALSE)
2157 *
2158 * @return boolean TRUE, if IP address validates OK (or no check is done at all)
2159 * @todo Define visibility
2160 */
2161 public function checkLockToIP() {
2162 $out = 1;
2163 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['enabledBeUserIPLock']) {
2164 $IPList = $this->getTSConfigVal('options.lockToIP');
2165 if (trim($IPList)) {
2166 $baseIP = GeneralUtility::getIndpEnv('REMOTE_ADDR');
2167 $out = GeneralUtility::cmpIP($baseIP, $IPList);
2168 }
2169 }
2170 return $out;
2171 }
2172
2173 /**
2174 * Check if user is logged in and if so, call ->fetchGroupData() to load group information and access lists of all kind, further check IP, set the ->uc array and send login-notification email if required.
2175 * If no user is logged in the default behaviour is to exit with an error message, but this will happen ONLY if the constant TYPO3_PROCEED_IF_NO_USER is set TRUE.
2176 * This function is called right after ->start() in fx. init.php
2177 *
2178 * @return void
2179 * @todo Define visibility
2180 */
2181 public function backendCheckLogin() {
2182 if (!$this->user['uid']) {
2183 if (!defined('TYPO3_PROCEED_IF_NO_USER') || !TYPO3_PROCEED_IF_NO_USER) {
2184 \TYPO3\CMS\Core\Utility\HttpUtility::redirect($GLOBALS['BACK_PATH']);
2185 }
2186 } else {
2187 // ...and if that's the case, call these functions
2188 $this->fetchGroupData();
2189 // The groups are fetched and ready for permission checking in this initialization.
2190 // Tables.php must be read before this because stuff like the modules has impact in this
2191 if ($this->checkLockToIP()) {
2192 if ($this->isUserAllowedToLogin()) {
2193 // Setting the UC array. It's needed with fetchGroupData first, due to default/overriding of values.
2194 $this->backendSetUC();
2195 // Email at login - if option set.
2196 $this->emailAtLogin();
2197 } else {
2198 throw new \RuntimeException('Login Error: TYPO3 is in maintenance mode at the moment. Only administrators are allowed access.', 1294585860);
2199 }
2200 } else {
2201 throw new \RuntimeException('Login Error: IP locking prevented you from being authorized. Can\'t proceed, sorry.', 1294585861);
2202 }
2203 }
2204 }
2205
2206 /**
2207 * If the backend script is in CLI mode, it will try to load a backend user named by the CLI module name (in lowercase)
2208 *
2209 * @return boolean Returns TRUE if a CLI user was loaded, otherwise FALSE!
2210 * @todo Define visibility
2211 */
2212 public function checkCLIuser() {
2213 // First, check if cliMode is enabled:
2214 if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) {
2215 if (!$this->user['uid']) {
2216 if (substr($GLOBALS['MCONF']['name'], 0, 5) == '_CLI_') {
2217 $userName = strtolower($GLOBALS['MCONF']['name']);
2218 $this->setBeUserByName($userName);
2219 if ($this->user['uid']) {
2220 if (!$this->isAdmin()) {
2221 return TRUE;
2222 } else {
2223 fwrite(STDERR, 'ERROR: CLI backend user "' . $userName . '" was ADMIN which is not allowed!' . LF . LF);
2224 die(3);
2225 }
2226 } else {
2227 fwrite(STDERR, 'ERROR: No backend user named "' . $userName . '" was found!' . LF . LF);
2228 die(3);
2229 }
2230 } else {
2231 fwrite(STDERR, 'ERROR: Module name, "' . $GLOBALS['MCONF']['name'] . '", was not prefixed with "_CLI_"' . LF . LF);
2232 die(3);
2233 }
2234 } else {
2235 fwrite(STDERR, 'ERROR: Another user was already loaded which is impossible in CLI mode!' . LF . LF);
2236 die(3);
2237 }
2238 }
2239 }
2240
2241 /**
2242 * Initialize the internal ->uc array for the backend user
2243 * Will make the overrides if necessary, and write the UC back to the be_users record if changes has happend
2244 *
2245 * @return void
2246 * @internal
2247 * @todo Define visibility
2248 */
2249 public function backendSetUC() {
2250 // UC - user configuration is a serialized array inside the userobject
2251 // If there is a saved uc we implement that instead of the default one.
2252 $temp_theSavedUC = unserialize($this->user['uc']);
2253 if (is_array($temp_theSavedUC)) {
2254 $this->unpack_uc($temp_theSavedUC);
2255 }
2256 // Setting defaults if uc is empty
2257 if (!is_array($this->uc)) {
2258 $this->uc = array_merge($this->uc_default, (array) $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultUC'], GeneralUtility::removeDotsFromTS((array) $this->getTSConfigProp('setup.default')));
2259 $this->overrideUC();
2260 $U = 1;
2261 }
2262 // If TSconfig is updated, update the defaultUC.
2263 if ($this->userTSUpdated) {
2264 $this->overrideUC();
2265 $U = 1;
2266 }
2267 // Setting default lang from be_user record.
2268 if (!isset($this->uc['lang'])) {
2269 $this->uc['lang'] = $this->user['lang'];
2270 $U = 1;
2271 }
2272 // Setting the time of the first login:
2273 if (!isset($this->uc['firstLoginTimeStamp'])) {
2274 $this->uc['firstLoginTimeStamp'] = $GLOBALS['EXEC_TIME'];
2275 $U = TRUE;
2276 }
2277 // Saving if updated.
2278 if ($U) {
2279 $this->writeUC();
2280 }
2281 }
2282
2283 /**
2284 * Override: Call this function every time the uc is updated.
2285 * That is 1) by reverting to default values, 2) in the setup-module, 3) userTS changes (userauthgroup)
2286 *
2287 * @return void
2288 * @internal
2289 * @todo Define visibility
2290 */
2291 public function overrideUC() {
2292 $this->uc = array_merge((array) $this->uc, (array) $this->getTSConfigProp('setup.override'));
2293 }
2294
2295 /**
2296 * Clears the user[uc] and ->uc to blank strings. Then calls ->backendSetUC() to fill it again with reset contents
2297 *
2298 * @return void
2299 * @internal
2300 * @todo Define visibility
2301 */
2302 public function resetUC() {
2303 $this->user['uc'] = '';
2304 $this->uc = '';
2305 $this->backendSetUC();
2306 }
2307
2308 /**
2309 * Will send an email notification to warning_email_address/the login users email address when a login session is just started.
2310 * Depends on various parameters whether mails are send and to whom.
2311 *
2312 * @return void
2313 * @access private
2314 */
2315 private function emailAtLogin() {
2316 if ($this->loginSessionStarted) {
2317 // Send notify-mail
2318 $subject = 'At "' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '"' . ' from ' . GeneralUtility::getIndpEnv('REMOTE_ADDR') . (GeneralUtility::getIndpEnv('REMOTE_HOST') ? ' (' . GeneralUtility::getIndpEnv('REMOTE_HOST') . ')' : '');
2319 $msg = sprintf('User "%s" logged in from %s (%s) at "%s" (%s)', $this->user['username'], GeneralUtility::getIndpEnv('REMOTE_ADDR'), GeneralUtility::getIndpEnv('REMOTE_HOST'), $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'], GeneralUtility::getIndpEnv('HTTP_HOST'));
2320 // Warning email address
2321 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['warning_email_addr']) {
2322 $warn = 0;
2323 $prefix = '';
2324 if (intval($GLOBALS['TYPO3_CONF_VARS']['BE']['warning_mode']) & 1) {
2325 // first bit: All logins
2326 $warn = 1;
2327 $prefix = $this->isAdmin() ? '[AdminLoginWarning]' : '[LoginWarning]';
2328 }
2329 if ($this->isAdmin() && intval($GLOBALS['TYPO3_CONF_VARS']['BE']['warning_mode']) & 2) {
2330 // second bit: Only admin-logins
2331 $warn = 1;
2332 $prefix = '[AdminLoginWarning]';
2333 }
2334 if ($warn) {
2335 $from = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFrom();
2336 /** @var $mail \TYPO3\CMS\Core\Mail\MailMessage */
2337 $mail = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\MailMessage');
2338 $mail->setTo($GLOBALS['TYPO3_CONF_VARS']['BE']['warning_email_addr'])->setFrom($from)->setSubject($prefix . ' ' . $subject)->setBody($msg);
2339 $mail->send();
2340 }
2341 }
2342 // If An email should be sent to the current user, do that:
2343 if ($this->uc['emailMeAtLogin'] && strstr($this->user['email'], '@')) {
2344 $from = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFrom();
2345 /** @var $mail \TYPO3\CMS\Core\Mail\MailMessage */
2346 $mail = GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\MailMessage');
2347 $mail->setTo($this->user['email'])->setFrom($from)->setSubject($subject)->setBody($msg);
2348 $mail->send();
2349 }
2350 }
2351 }
2352
2353 /**
2354 * Determines whether a backend user is allowed to access the backend.
2355 *
2356 * The conditions are:
2357 * + backend user is a regular user and adminOnly is not defined
2358 * + backend user is an admin user
2359 * + backend user is used in CLI context and adminOnly is explicitly set to "2"
2360 * + backend user is being controlled by an admin user
2361 *
2362 * @return boolean Whether a backend user is allowed to access the backend
2363 */
2364 protected function isUserAllowedToLogin() {
2365 $isUserAllowedToLogin = FALSE;
2366 $adminOnlyMode = $GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'];
2367 // Backend user is allowed if adminOnly is not set or user is an admin:
2368 if (!$adminOnlyMode || $this->isAdmin()) {
2369 $isUserAllowedToLogin = TRUE;
2370 } elseif ($adminOnlyMode == 2 && TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) {
2371 $isUserAllowedToLogin = TRUE;
2372 } elseif ($this->user['ses_backuserid']) {
2373 $backendUserId = intval($this->user['ses_backuserid']);
2374 $whereAdmin = 'uid=' . $backendUserId . ' AND admin=1' . BackendUtility::BEenableFields('be_users');
2375 if ($GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', 'be_users', $whereAdmin) > 0) {
2376 $isUserAllowedToLogin = TRUE;
2377 }
2378 }
2379 return $isUserAllowedToLogin;
2380 }
2381
2382 /**
2383 * Logs out the current user and clears the form protection tokens.
2384 */
2385 public function logoff() {
2386 if (isset($GLOBALS['BE_USER'])) {
2387 \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get()->clean();
2388 }
2389 parent::logoff();
2390 }
2391
2392 }
2393
2394
2395 ?>