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