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