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