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