f1c52f112634987370dde8841f0866bdf6e57962
[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 $storageFilePermissions = $this->getTSConfig('permissions.file.storage.' . $storageObject->getUid());
1718 if (is_array($storageFilePermissions) && count($storageFilePermissions)) {
1719 return array_merge($storageFilePermissions, $defaultFilePermissions);
1720 } else {
1721 return $defaultFilePermissions;
1722 }
1723 }
1724
1725 /**
1726 * Returns a \TYPO3\CMS\Core\Resource\Folder object that is used for uploading
1727 * files by default.
1728 * This is used for RTE and its magic images, as well as uploads
1729 * in the TCEforms fields, unless otherwise configured (will be added in the future)
1730 *
1731 * the default upload folder for a user is the defaultFolder on the first
1732 * filestorage/filemount that the user can access
1733 * however, you can set the users' upload folder like this:
1734 *
1735 * options.defaultUploadFolder = 3:myfolder/yourfolder/
1736 *
1737 * @return \TYPO3\CMS\Core\Resource\Folder|boolean The default upload folder for this user
1738 */
1739 public function getDefaultUploadFolder() {
1740 $uploadFolder = $this->getTSConfigVal('options.defaultUploadFolder');
1741 if ($uploadFolder) {
1742 $uploadFolder = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier($uploadFolder);
1743 } else {
1744 $storages = $this->getFileStorages();
1745 if (count($storages) > 0) {
1746 /** @var $firstStorage \TYPO3\CMS\Core\Resource\ResourceStorage */
1747 $firstStorage = reset($storages);
1748 $uploadFolder = $firstStorage->getDefaultFolder();
1749 }
1750 }
1751 if ($uploadFolder instanceof \TYPO3\CMS\Core\Resource\Folder) {
1752 return $uploadFolder;
1753 } else {
1754 return FALSE;
1755 }
1756 }
1757
1758 /**
1759 * Returns an integer bitmask that represents the permissions for file operations.
1760 * Permissions of the user and groups the user is a member of were combined by a logical OR.
1761 *
1762 * Meaning of each bit:
1763 * 1 - Files: Upload,Copy,Move,Delete,Rename
1764 * 2 - Files: Unzip
1765 * 4 - Directory: Move,Delete,Rename,New
1766 * 8 - Directory: Copy
1767 * 16 - Directory: Delete recursively (rm -Rf)
1768 *
1769 * @return integer File operation permission bitmask
1770 * @deprecated since TYPO3 6.0, use the TSconfig settings instead
1771 */
1772 public function getFileoperationPermissions() {
1773 \TYPO3\CMS\Core\Utility\GeneralUtility::logDeprecatedFunction();
1774 if ($this->isAdmin()) {
1775 return 31;
1776 } else {
1777 return $this->groupData['fileoper_perms'];
1778 }
1779 }
1780
1781 /**
1782 * Adds a filemount to the users array of filemounts, $this->groupData['filemounts'][hash_key] = Array ('name'=>$name, 'path'=>$path, 'type'=>$type);
1783 * Is a part of the authentication proces of the user.
1784 * 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!
1785 * Paths in the mounted information will always be absolute and have a trailing slash.
1786 *
1787 * @param string $title Will be the (root)name of the filemount in the folder tree
1788 * @param string $altTitle Will be the (root)name of the filemount IF $title is not TRUE (blank or zero)
1789 * @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.
1790 * @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.
1791 * @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.
1792 * @return boolean Returns "1" if the requested filemount was mounted, otherwise no return value.
1793 * @deprecated since TYPO3 6.0, will be removed in TYPO3 6.1, all data is stored in $this->fileStorages now, see initializeFileStorages()
1794 * @access private
1795 * @todo Define visibility
1796 */
1797 public function addFileMount($title, $altTitle, $path, $webspace, $type) {
1798 \TYPO3\CMS\Core\Utility\GeneralUtility::logDeprecatedFunction();
1799 // Return FALSE if fileadminDir is not set and we try to mount a relative path
1800 if ($webspace && !$GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir']) {
1801 return FALSE;
1802 }
1803 // Trimming and pre-processing
1804 $path = trim($path);
1805 // with WINDOWS convert backslash to slash!!
1806 if ($this->OS == 'WIN') {
1807 $path = str_replace('\\', '/', $path);
1808 }
1809 // If the path is TRUE and validates as a valid path string:
1810 if ($path && \TYPO3\CMS\Core\Utility\GeneralUtility::validPathStr($path)) {
1811 // normalize path: remove leading '/' and './', and trailing '/' and '/.'
1812 $path = trim($path);
1813 $path = preg_replace('#^\\.?/|/\\.?$#', '', $path);
1814 // There must be some chars in the path
1815 if ($path) {
1816 // Fileadmin dir, absolute
1817 $fdir = PATH_site . $GLOBALS['TYPO3_CONF_VARS']['BE']['fileadminDir'];
1818 if ($webspace) {
1819 $path = $fdir . $path;
1820 } else {
1821 // With WINDOWS no prepending!!
1822 if ($this->OS != 'WIN') {
1823 // With WINDOWS no prepending!!
1824 $path = '/' . $path;
1825 }
1826 }
1827 $path .= '/';
1828 // We now have a path with slash after and slash before (if unix)
1829 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))) {
1830 // Alternative title?
1831 $name = $title ? $title : $altTitle;
1832 // 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.
1833 $this->groupData['filemounts'][md5($name . '|' . $path . '|' . $type)] = array('name' => $name, 'path' => $path, 'type' => $type);
1834 // Return TRUE - went well, success!
1835 return 1;
1836 }
1837 }
1838 }
1839 }
1840
1841 /**
1842 * Returns an array with the filemounts for the user. Each filemount is represented with an array of a "name", "path" and "type".
1843 * If no filemounts an empty array is returned.
1844 *
1845 * @return array
1846 * @deprecated since TYPO3 6.0, will be removed in TYPO3 6.1 as getFileStorages() should be the one to be used
1847 * @todo Define visibility
1848 */
1849 public function returnFilemounts() {
1850 \TYPO3\CMS\Core\Utility\GeneralUtility::logDeprecatedFunction();
1851 // initialize the file storages in order to set some default settings in any time
1852 $this->getFileStorages();
1853 return $this->groupData['filemounts'];
1854 }
1855
1856 /**
1857 * Creates a TypoScript comment with the string text inside.
1858 *
1859 * @param string $str The text to wrap in comment prefixes and delimiters.
1860 * @return string TypoScript comment with the string text inside.
1861 * @todo Define visibility
1862 */
1863 public function addTScomment($str) {
1864 $delimiter = '# ***********************************************';
1865 $out = $delimiter . LF;
1866 $lines = \TYPO3\CMS\Core\Utility\GeneralUtility::trimExplode(LF, $str);
1867 foreach ($lines as $v) {
1868 $out .= '# ' . $v . LF;
1869 }
1870 $out .= $delimiter . LF;
1871 return $out;
1872 }
1873
1874 /**
1875 * Initializing workspace.
1876 * Called from within this function, see fetchGroupData()
1877 *
1878 * @return void
1879 * @see fetchGroupData()
1880 * @todo Define visibility
1881 */
1882 public function workspaceInit() {
1883 // Initializing workspace by evaluating and setting the workspace, possibly updating it in the user record!
1884 $this->setWorkspace($this->user['workspace_id']);
1885 // Limiting the DB mountpoints if there any selected in the workspace record
1886 $this->initializeDbMountpointsInWorkspace();
1887 if ($allowed_languages = $this->getTSConfigVal('options.workspaces.allowed_languages.' . $this->workspace)) {
1888 $this->groupData['allowed_languages'] = $allowed_languages;
1889 $this->groupData['allowed_languages'] = \TYPO3\CMS\Core\Utility\GeneralUtility::uniqueList($this->groupData['allowed_languages']);
1890 }
1891 }
1892
1893 /**
1894 * Limiting the DB mountpoints if there any selected in the workspace record
1895 *
1896 * @return void
1897 */
1898 protected function initializeDbMountpointsInWorkspace() {
1899 $dbMountpoints = trim($this->workspaceRec['db_mountpoints']);
1900 if ($this->workspace > 0 && $dbMountpoints != '') {
1901 $filteredDbMountpoints = array();
1902 // 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.
1903 // The actual permission checking on page level is done elsewhere as usual anyway before the page tree is rendered.
1904 $readPerms = '1=1';
1905 // Traverse mount points of the
1906 $dbMountpoints = \TYPO3\CMS\Core\Utility\GeneralUtility::intExplode(',', $dbMountpoints);
1907 foreach ($dbMountpoints as $mpId) {
1908 if ($this->isInWebMount($mpId, $readPerms)) {
1909 $filteredDbMountpoints[] = $mpId;
1910 }
1911 }
1912 // Re-insert webmounts:
1913 $filteredDbMountpoints = array_unique($filteredDbMountpoints);
1914 $this->groupData['webmounts'] = implode(',', $filteredDbMountpoints);
1915 }
1916 }
1917
1918 /**
1919 * Adds more limitations for users who are no admins
1920 * this was previously in workspaceInit but has now been moved to "
1921 *
1922 * @return void
1923 */
1924 protected function initializeFileStoragesForWorkspace() {
1925 // Filtering the file mountpoints
1926 // if there some selected in the workspace record
1927 if ($this->workspace > 0) {
1928 $storageFiltersInWorkspace = trim($this->workspaceRec['file_mountpoints']);
1929 // no custom filemounts that should serve as filter or user is admin
1930 // so all user mountpoints are re-applied
1931 if (!$this->isAdmin() && $storageFiltersInWorkspace !== '') {
1932 // empty the fileStorages (will be re-applied later)
1933 $existingFileStoragesOfUser = $this->fileStorages;
1934 $this->fileStorages = array();
1935 $storageRepository = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Resource\\StorageRepository');
1936 // Fetching all filemounts from the workspace
1937 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'sys_filemounts', 'deleted = 0 AND hidden = 0 AND pid = 0 AND uid IN (' . $GLOBALS['TYPO3_DB']->cleanIntList($storageFiltersInWorkspace) . ')');
1938 // add every filemount of this workspace record
1939 while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
1940 // get the added entry, and check if it was in the users' original filemounts
1941 // if not, remove it from the new filemount list again
1942 // see self::addFileMount
1943 // TODO: check if the filter is narrowing down the existing user
1944 $storageObject = $storageRepository->findByUid($row['base']);
1945 if (isset($existingFileStoragesOfUser[$storageObject->getUid()])) {
1946 $storageObject->injectFileMount($row['path']);
1947 $this->fileStorages[$storageObject->getUid()] = $storageObject;
1948 }
1949 }
1950 $GLOBALS['TYPO3_DB']->sql_free_result($res);
1951 }
1952 }
1953 }
1954
1955 /**
1956 * Checking if a workspace is allowed for backend user
1957 *
1958 * @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)
1959 * @param string $fields List of fields to select. Default fields are: uid,title,adminusers,members,reviewers,publish_access,stagechg_notification
1960 * @return array TRUE if access. Output will also show how access was granted. Admin users will have a true output regardless of input.
1961 * @todo Define visibility
1962 */
1963 public function checkWorkspace($wsRec, $fields = 'uid,title,adminusers,members,reviewers,publish_access,stagechg_notification') {
1964 $retVal = FALSE;
1965 // If not array, look up workspace record:
1966 if (!is_array($wsRec)) {
1967 switch ((string) $wsRec) {
1968 case '0':
1969 $wsRec = array('uid' => $wsRec);
1970 break;
1971 default:
1972 if (\TYPO3\CMS\Core\Extension\ExtensionManager::isLoaded('workspaces')) {
1973 $wsRec = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow($fields, 'sys_workspace', 'pid=0 AND uid=' . intval($wsRec) . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause('sys_workspace'), '', 'title');
1974 }
1975 break;
1976 }
1977 }
1978 // If wsRec is set to an array, evaluate it:
1979 if (is_array($wsRec)) {
1980 if ($this->isAdmin()) {
1981 return array_merge($wsRec, array('_ACCESS' => 'admin'));
1982 } else {
1983 switch ((string) $wsRec['uid']) {
1984 case '0':
1985 $retVal = $this->groupData['workspace_perms'] & 1 ? array_merge($wsRec, array('_ACCESS' => 'online')) : FALSE;
1986 break;
1987 default:
1988 // Checking if the guy is admin:
1989 if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList($wsRec['adminusers'], 'be_users_' . $this->user['uid'])) {
1990 return array_merge($wsRec, array('_ACCESS' => 'owner'));
1991 }
1992 // Checking if he is owner through a user group of his:
1993 foreach ($this->userGroupsUID as $groupUid) {
1994 if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList($wsRec['adminusers'], 'be_groups_' . $groupUid)) {
1995 return array_merge($wsRec, array('_ACCESS' => 'owner'));
1996 }
1997 }
1998 // Checking if he is reviewer user:
1999 if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList($wsRec['reviewers'], 'be_users_' . $this->user['uid'])) {
2000 return array_merge($wsRec, array('_ACCESS' => 'reviewer'));
2001 }
2002 // Checking if he is reviewer through a user group of his:
2003 foreach ($this->userGroupsUID as $groupUid) {
2004 if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList($wsRec['reviewers'], 'be_groups_' . $groupUid)) {
2005 return array_merge($wsRec, array('_ACCESS' => 'reviewer'));
2006 }
2007 }
2008 // Checking if he is member as user:
2009 if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList($wsRec['members'], 'be_users_' . $this->user['uid'])) {
2010 return array_merge($wsRec, array('_ACCESS' => 'member'));
2011 }
2012 // Checking if he is member through a user group of his:
2013 foreach ($this->userGroupsUID as $groupUid) {
2014 if (\TYPO3\CMS\Core\Utility\GeneralUtility::inList($wsRec['members'], 'be_groups_' . $groupUid)) {
2015 return array_merge($wsRec, array('_ACCESS' => 'member'));
2016 }
2017 }
2018 break;
2019 }
2020 }
2021 }
2022 return $retVal;
2023 }
2024
2025 /**
2026 * 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.
2027 *
2028 * @return array See checkWorkspace()
2029 * @see checkWorkspace()
2030 * @todo Define visibility
2031 */
2032 public function checkWorkspaceCurrent() {
2033 if (!isset($this->checkWorkspaceCurrent_cache)) {
2034 $this->checkWorkspaceCurrent_cache = $this->checkWorkspace($this->workspace);
2035 }
2036 return $this->checkWorkspaceCurrent_cache;
2037 }
2038
2039 /**
2040 * Setting workspace ID
2041 *
2042 * @param integer $workspaceId ID of workspace to set for backend user. If not valid the default workspace for BE user is found and set.
2043 * @return void
2044 * @todo Define visibility
2045 */
2046 public function setWorkspace($workspaceId) {
2047 // Check workspace validity and if not found, revert to default workspace.
2048 if ($this->workspaceRec = $this->checkWorkspace($workspaceId, '*')) {
2049 // Set workspace ID internally
2050 $this->workspace = (int) $workspaceId;
2051 } else {
2052 $this->workspace = (int) $this->getDefaultWorkspace();
2053 $this->workspaceRec = $this->checkWorkspace($this->workspace, '*');
2054 }
2055 // Unset access cache:
2056 unset($this->checkWorkspaceCurrent_cache);
2057 // If ID is different from the stored one, change it:
2058 if (strcmp($this->workspace, $this->user['workspace_id'])) {
2059 $this->user['workspace_id'] = $this->workspace;
2060 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('be_users', 'uid=' . intval($this->user['uid']), array('workspace_id' => $this->user['workspace_id']));
2061 $this->simplelog('User changed workspace to "' . $this->workspace . '"');
2062 }
2063 }
2064
2065 /**
2066 * Setting workspace preview state for user:
2067 *
2068 * @param boolean $previewState State of user preview.
2069 * @return void
2070 * @todo Define visibility
2071 */
2072 public function setWorkspacePreview($previewState) {
2073 $this->user['workspace_preview'] = $previewState;
2074 $GLOBALS['TYPO3_DB']->exec_UPDATEquery('be_users', 'uid=' . intval($this->user['uid']), array('workspace_preview' => $this->user['workspace_preview']));
2075 }
2076
2077 /**
2078 * Return default workspace ID for user,
2079 * If EXT:workspaces is not installed the user will be pushed the the
2080 * Live workspace
2081 *
2082 * @return integer Default workspace id. If no workspace is available it will be "-99
2083 * @todo Define visibility
2084 */
2085 public function getDefaultWorkspace() {
2086 $defaultWorkspace = -99;
2087 if (!\TYPO3\CMS\Core\Extension\ExtensionManager::isLoaded('workspaces') || $this->checkWorkspace(0)) {
2088 // Check online
2089 $defaultWorkspace = 0;
2090 } elseif ($this->checkWorkspace(-1)) {
2091 // Check offline
2092 $defaultWorkspace = -1;
2093 } elseif (\TYPO3\CMS\Core\Extension\ExtensionManager::isLoaded('workspaces')) {
2094 // Traverse custom workspaces:
2095 $workspaces = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows('uid,title,adminusers,members,reviewers', 'sys_workspace', 'pid=0' . \TYPO3\CMS\Backend\Utility\BackendUtility::deleteClause('sys_workspace'), '', 'title');
2096 foreach ($workspaces as $rec) {
2097 if ($this->checkWorkspace($rec)) {
2098 $defaultWorkspace = $rec['uid'];
2099 break;
2100 }
2101 }
2102 }
2103 return $defaultWorkspace;
2104 }
2105
2106 /**
2107 * Writes an entry in the logfile/table
2108 * Documentation in "TYPO3 Core API"
2109 *
2110 * @param integer $type Denotes which module that has submitted the entry. See "TYPO3 Core API". Use "4" for extensions.
2111 * @param integer $action Denotes which specific operation that wrote the entry. Use "0" when no sub-categorizing applies
2112 * @param integer $error Flag. 0 = message, 1 = error (user problem), 2 = System Error (which should not happen), 3 = security notice (admin)
2113 * @param integer $details_nr The message number. Specific for each $type and $action. This will make it possible to translate errormessages to other languages
2114 * @param string $details Default text that follows the message (in english!). Possibly translated by identification through type/action/details_nr
2115 * @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
2116 * @param string $tablename Table name. Special field used by tce_main.php.
2117 * @param integer $recuid Record UID. Special field used by tce_main.php.
2118 * @param integer $recpid Record PID. Special field used by tce_main.php. OBSOLETE
2119 * @param integer $event_pid The page_uid (pid) where the event occurred. Used to select log-content for specific pages.
2120 * @param string $NEWid Special field used by tce_main.php. NEWid string of newly created records.
2121 * @param integer $userId Alternative Backend User ID (used for logging login actions where this is not yet known).
2122 * @return integer Log entry ID.
2123 * @todo Define visibility
2124 */
2125 public function writelog($type, $action, $error, $details_nr, $details, $data, $tablename = '', $recuid = '', $recpid = '', $event_pid = -1, $NEWid = '', $userId = 0) {
2126 $fields_values = array(
2127 'userid' => $userId ? $userId : intval($this->user['uid']),
2128 'type' => intval($type),
2129 'action' => intval($action),
2130 'error' => intval($error),
2131 'details_nr' => intval($details_nr),
2132 'details' => $details,
2133 'log_data' => serialize($data),
2134 'tablename' => $tablename,
2135 'recuid' => intval($recuid),
2136 'IP' => \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('REMOTE_ADDR'),
2137 'tstamp' => $GLOBALS['EXEC_TIME'],
2138 'event_pid' => intval($event_pid),
2139 'NEWid' => $NEWid,
2140 'workspace' => $this->workspace
2141 );
2142 $GLOBALS['TYPO3_DB']->exec_INSERTquery('sys_log', $fields_values);
2143 return $GLOBALS['TYPO3_DB']->sql_insert_id();
2144 }
2145
2146 /**
2147 * Simple logging function
2148 *
2149 * @param string $message Log message
2150 * @param string $extKey Option extension key / module name
2151 * @param integer $error Error level. 0 = message, 1 = error (user problem), 2 = System Error (which should not happen), 3 = security notice (admin)
2152 * @return integer Log entry UID
2153 * @todo Define visibility
2154 */
2155 public function simplelog($message, $extKey = '', $error = 0) {
2156 return $this->writelog(4, 0, $error, 0, ($extKey ? '[' . $extKey . '] ' : '') . $message, array());
2157 }
2158
2159 /**
2160 * Sends a warning to $email if there has been a certain amount of failed logins during a period.
2161 * 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.
2162 *
2163 * @param string $email Email address
2164 * @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.
2165 * @param integer $max Max allowed failures before a warning mail is sent
2166 * @return void
2167 * @access private
2168 * @todo Define visibility
2169 */
2170 public function checkLogFailures($email, $secondsBack = 3600, $max = 3) {
2171 if ($email) {
2172 // Get last flag set in the log for sending
2173 $theTimeBack = $GLOBALS['EXEC_TIME'] - $secondsBack;
2174 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('tstamp', 'sys_log', 'type=255 AND action=4 AND tstamp>' . intval($theTimeBack), '', 'tstamp DESC', '1');
2175 if ($testRow = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
2176 $theTimeBack = $testRow['tstamp'];
2177 }
2178 $GLOBALS['TYPO3_DB']->sql_free_result($res);
2179 // Check for more than $max number of error failures with the last period.
2180 $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'sys_log', 'type=255 AND action=3 AND error<>0 AND tstamp>' . intval($theTimeBack), '', 'tstamp');
2181 if ($GLOBALS['TYPO3_DB']->sql_num_rows($res) > $max) {
2182 // OK, so there were more than the max allowed number of login failures - so we will send an email then.
2183 $subject = 'TYPO3 Login Failure Warning (at ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ')';
2184 $email_body = 'There have been some attempts (' . $GLOBALS['TYPO3_DB']->sql_num_rows($res) . ') to login at the TYPO3
2185 site "' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '" (' . \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('HTTP_HOST') . ').
2186
2187 This is a dump of the failures:
2188
2189 ';
2190 while ($testRows = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) {
2191 $theData = unserialize($testRows['log_data']);
2192 $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]));
2193 $email_body .= LF;
2194 }
2195 $GLOBALS['TYPO3_DB']->sql_free_result($res);
2196 $from = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFrom();
2197 /** @var $mail \TYPO3\CMS\Core\Mail\MailMessage */
2198 $mail = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\MailMessage');
2199 $mail->setTo($email)->setFrom($from)->setSubject($subject)->setBody($email_body);
2200 $mail->send();
2201 // Logout written to log
2202 $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));
2203 }
2204 }
2205 }
2206
2207 /**
2208 * Getter for the cookie name
2209 *
2210 * @static
2211 * @return string returns the configured cookie name
2212 */
2213 static public function getCookieName() {
2214 $configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['cookieName']);
2215 if (empty($configuredCookieName)) {
2216 $configuredCookieName = 'be_typo_user';
2217 }
2218 return $configuredCookieName;
2219 }
2220
2221 /**
2222 * If TYPO3_CONF_VARS['BE']['enabledBeUserIPLock'] is enabled and
2223 * an IP-list is found in the User TSconfig objString "options.lockToIP",
2224 * then make an IP comparison with REMOTE_ADDR and return the outcome (TRUE/FALSE)
2225 *
2226 * @return boolean TRUE, if IP address validates OK (or no check is done at all)
2227 * @todo Define visibility
2228 */
2229 public function checkLockToIP() {
2230 $out = 1;
2231 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['enabledBeUserIPLock']) {
2232 $IPList = $this->getTSConfigVal('options.lockToIP');
2233 if (trim($IPList)) {
2234 $baseIP = \TYPO3\CMS\Core\Utility\GeneralUtility::getIndpEnv('REMOTE_ADDR');
2235 $out = \TYPO3\CMS\Core\Utility\GeneralUtility::cmpIP($baseIP, $IPList);
2236 }
2237 }
2238 return $out;
2239 }
2240
2241 /**
2242 * 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.
2243 * 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.
2244 * This function is called right after ->start() in fx. init.php
2245 *
2246 * @return void
2247 * @todo Define visibility
2248 */
2249 public function backendCheckLogin() {
2250 if (!$this->user['uid']) {
2251 if (!defined('TYPO3_PROCEED_IF_NO_USER') || !TYPO3_PROCEED_IF_NO_USER) {
2252 \TYPO3\CMS\Core\Utility\HttpUtility::redirect($GLOBALS['BACK_PATH']);
2253 }
2254 } else {
2255 // ...and if that's the case, call these functions
2256 $this->fetchGroupData();
2257 // The groups are fetched and ready for permission checking in this initialization.
2258 // Tables.php must be read before this because stuff like the modules has impact in this
2259 if ($this->checkLockToIP()) {
2260 if ($this->isUserAllowedToLogin()) {
2261 // Setting the UC array. It's needed with fetchGroupData first, due to default/overriding of values.
2262 $this->backendSetUC();
2263 // Email at login - if option set.
2264 $this->emailAtLogin();
2265 } else {
2266 throw new \RuntimeException('Login Error: TYPO3 is in maintenance mode at the moment. Only administrators are allowed access.', 1294585860);
2267 }
2268 } else {
2269 throw new \RuntimeException('Login Error: IP locking prevented you from being authorized. Can\'t proceed, sorry.', 1294585861);
2270 }
2271 }
2272 }
2273
2274 /**
2275 * If the backend script is in CLI mode, it will try to load a backend user named by the CLI module name (in lowercase)
2276 *
2277 * @return boolean Returns TRUE if a CLI user was loaded, otherwise FALSE!
2278 * @todo Define visibility
2279 */
2280 public function checkCLIuser() {
2281 // First, check if cliMode is enabled:
2282 if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) {
2283 if (!$this->user['uid']) {
2284 if (substr($GLOBALS['MCONF']['name'], 0, 5) == '_CLI_') {
2285 $userName = strtolower($GLOBALS['MCONF']['name']);
2286 $this->setBeUserByName($userName);
2287 if ($this->user['uid']) {
2288 if (!$this->isAdmin()) {
2289 return TRUE;
2290 } else {
2291 fwrite(STDERR, 'ERROR: CLI backend user "' . $userName . '" was ADMIN which is not allowed!' . LF . LF);
2292 die(3);
2293 }
2294 } else {
2295 fwrite(STDERR, 'ERROR: No backend user named "' . $userName . '" was found!' . LF . LF);
2296 die(3);
2297 }
2298 } else {
2299 fwrite(STDERR, 'ERROR: Module name, "' . $GLOBALS['MCONF']['name'] . '", was not prefixed with "_CLI_"' . LF . LF);
2300 die(3);
2301 }
2302 } else {
2303 fwrite(STDERR, 'ERROR: Another user was already loaded which is impossible in CLI mode!' . LF . LF);
2304 die(3);
2305 }
2306 }
2307 }
2308
2309 /**
2310 * Initialize the internal ->uc array for the backend user
2311 * Will make the overrides if necessary, and write the UC back to the be_users record if changes has happend
2312 *
2313 * @return void
2314 * @internal
2315 * @todo Define visibility
2316 */
2317 public function backendSetUC() {
2318 // UC - user configuration is a serialized array inside the userobject
2319 // If there is a saved uc we implement that instead of the default one.
2320 $temp_theSavedUC = unserialize($this->user['uc']);
2321 if (is_array($temp_theSavedUC)) {
2322 $this->unpack_uc($temp_theSavedUC);
2323 }
2324 // Setting defaults if uc is empty
2325 if (!is_array($this->uc)) {
2326 $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')));
2327 $this->overrideUC();
2328 $U = 1;
2329 }
2330 // If TSconfig is updated, update the defaultUC.
2331 if ($this->userTSUpdated) {
2332 $this->overrideUC();
2333 $U = 1;
2334 }
2335 // Setting default lang from be_user record.
2336 if (!isset($this->uc['lang'])) {
2337 $this->uc['lang'] = $this->user['lang'];
2338 $U = 1;
2339 }
2340 // Setting the time of the first login:
2341 if (!isset($this->uc['firstLoginTimeStamp'])) {
2342 $this->uc['firstLoginTimeStamp'] = $GLOBALS['EXEC_TIME'];
2343 $U = TRUE;
2344 }
2345 // Saving if updated.
2346 if ($U) {
2347 // Method from the t3lib_userauth class.
2348 $this->writeUC();
2349 }
2350 }
2351
2352 /**
2353 * Override: Call this function every time the uc is updated.
2354 * That is 1) by reverting to default values, 2) in the setup-module, 3) userTS changes (userauthgroup)
2355 *
2356 * @return void
2357 * @internal
2358 * @todo Define visibility
2359 */
2360 public function overrideUC() {
2361 $this->uc = array_merge((array) $this->uc, (array) $this->getTSConfigProp('setup.override'));
2362 }
2363
2364 /**
2365 * Clears the user[uc] and ->uc to blank strings. Then calls ->backendSetUC() to fill it again with reset contents
2366 *
2367 * @return void
2368 * @internal
2369 * @todo Define visibility
2370 */
2371 public function resetUC() {
2372 $this->user['uc'] = '';
2373 $this->uc = '';
2374 $this->backendSetUC();
2375 }
2376
2377 /**
2378 * Will send an email notification to warning_email_address/the login users email address when a login session is just started.
2379 * Depends on various parameters whether mails are send and to whom.
2380 *
2381 * @return void
2382 * @access private
2383 */
2384 private function emailAtLogin() {
2385 if ($this->loginSessionStarted) {
2386 // Send notify-mail
2387 $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') . ')' : '');
2388 $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'));
2389 // Warning email address
2390 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['warning_email_addr']) {
2391 $warn = 0;
2392 $prefix = '';
2393 if (intval($GLOBALS['TYPO3_CONF_VARS']['BE']['warning_mode']) & 1) {
2394 // first bit: All logins
2395 $warn = 1;
2396 $prefix = $this->isAdmin() ? '[AdminLoginWarning]' : '[LoginWarning]';
2397 }
2398 if ($this->isAdmin() && intval($GLOBALS['TYPO3_CONF_VARS']['BE']['warning_mode']) & 2) {
2399 // second bit: Only admin-logins
2400 $warn = 1;
2401 $prefix = '[AdminLoginWarning]';
2402 }
2403 if ($warn) {
2404 $from = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFrom();
2405 /** @var $mail \TYPO3\CMS\Core\Mail\MailMessage */
2406 $mail = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\MailMessage');
2407 $mail->setTo($GLOBALS['TYPO3_CONF_VARS']['BE']['warning_email_addr'])->setFrom($from)->setSubject($prefix . ' ' . $subject)->setBody($msg);
2408 $mail->send();
2409 }
2410 }
2411 // If An email should be sent to the current user, do that:
2412 if ($this->uc['emailMeAtLogin'] && strstr($this->user['email'], '@')) {
2413 $from = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFrom();
2414 /** @var $mail \TYPO3\CMS\Core\Mail\MailMessage */
2415 $mail = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance('TYPO3\\CMS\\Core\\Mail\\MailMessage');
2416 $mail->setTo($this->user['email'])->setFrom($from)->setSubject($subject)->setBody($msg);
2417 $mail->send();
2418 }
2419 }
2420 }
2421
2422 /**
2423 * Determines whether a backend user is allowed to access the backend.
2424 *
2425 * The conditions are:
2426 * + backend user is a regular user and adminOnly is not defined
2427 * + backend user is an admin user
2428 * + backend user is used in CLI context and adminOnly is explicitly set to "2"
2429 * + backend user is being controlled by an admin user
2430 *
2431 * @return boolean Whether a backend user is allowed to access the backend
2432 */
2433 protected function isUserAllowedToLogin() {
2434 $isUserAllowedToLogin = FALSE;
2435 $adminOnlyMode = $GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'];
2436 // Backend user is allowed if adminOnly is not set or user is an admin:
2437 if (!$adminOnlyMode || $this->isAdmin()) {
2438 $isUserAllowedToLogin = TRUE;
2439 } elseif ($adminOnlyMode == 2 && TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) {
2440 $isUserAllowedToLogin = TRUE;
2441 } elseif ($this->user['ses_backuserid']) {
2442 $backendUserId = intval($this->user['ses_backuserid']);
2443 $whereAdmin = 'uid=' . $backendUserId . ' AND admin=1' . \TYPO3\CMS\Backend\Utility\BackendUtility::BEenableFields('be_users');
2444 if ($GLOBALS['TYPO3_DB']->exec_SELECTcountRows('uid', 'be_users', $whereAdmin) > 0) {
2445 $isUserAllowedToLogin = TRUE;
2446 }
2447 }
2448 return $isUserAllowedToLogin;
2449 }
2450
2451 /**
2452 * Logs out the current user and clears the form protection tokens.
2453 */
2454 public function logoff() {
2455 if (isset($GLOBALS['BE_USER'])) {
2456 \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get()->clean();
2457 }
2458 parent::logoff();
2459 }
2460
2461 }
2462
2463
2464 ?>