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