TYPO3  7.6
BackendUserAuthentication.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Core\Authentication;
3 
4 /*
5  * This file is part of the TYPO3 CMS project.
6  *
7  * It is free software; you can redistribute it and/or modify it under
8  * the terms of the GNU General Public License, either version 2
9  * of the License, or any later version.
10  *
11  * For the full copyright and license information, please read the
12  * LICENSE.txt file that was distributed with this source code.
13  *
14  * The TYPO3 project - inspiring people to share!
15  */
16 
21 
30 {
35  public $usergroup_column = 'usergroup';
36 
41  public $usergroup_table = 'be_groups';
42 
48  public $groupData = array(
49  'filemounts' => array()
50  );
51 
56  public $userGroups = array();
57 
62  public $userGroupsUID = array();
63 
68  public $groupList = '';
69 
78  public $workspace = -99;
79 
84  public $workspaceRec = array();
85 
93  public $dataLists = array(
94  'webmount_list' => '',
95  'filemount_list' => '',
96  'file_permissions' => '',
97  'modList' => '',
98  'tables_select' => '',
99  'tables_modify' => '',
100  'pagetypes_select' => '',
101  'non_exclude_fields' => '',
102  'explicit_allowdeny' => '',
103  'allowed_languages' => '',
104  'workspace_perms' => '',
105  'custom_options' => ''
106  );
107 
112  public $includeHierarchy = array();
113 
118  public $includeGroupArray = array();
119 
125  public $OS = '';
126 
131  public $TSdataArray = array();
132 
137  public $userTS_text = '';
138 
143  public $userTS = array();
144 
149  public $userTSUpdated = false;
150 
155  public $userTS_dontGetCached = false;
156 
161  public $errorMsg = '';
162 
168 
172  protected $fileStorages;
173 
177  protected $filePermissions;
178 
183  public $session_table = 'be_sessions';
184 
189  public $user_table = 'be_users';
190 
195  public $username_column = 'username';
196 
201  public $userident_column = 'password';
202 
207  public $userid_column = 'uid';
208 
212  public $lastLogin_column = 'lastlogin';
213 
217  public $enablecolumns = array(
218  'rootLevel' => 1,
219  'deleted' => 'deleted',
220  'disabled' => 'disable',
221  'starttime' => 'starttime',
222  'endtime' => 'endtime'
223  );
224 
229  public $formfield_uname = 'username';
230 
235  public $formfield_uident = 'userident';
236 
241  public $formfield_status = 'login_status';
242 
247  public $writeStdLog = true;
248 
253  public $writeAttemptLog = true;
254 
261  public $auth_timeout_field = 6000;
262 
266  public $firstMainGroup = 0;
267 
272  public $uc;
273 
283  public $uc_default = array(
284  'interfaceSetup' => '',
285  // serialized content that is used to store interface pane and menu positions. Set by the logout.php-script
286  'moduleData' => array(),
287  // user-data for the modules
288  'thumbnailsByDefault' => 1,
289  'emailMeAtLogin' => 0,
290  'startModule' => 'help_AboutmodulesAboutmodules',
291  'hideSubmoduleIcons' => 0,
292  'titleLen' => 50,
293  'edit_RTE' => '1',
294  'edit_docModuleUpload' => '1',
295  // Default is 245 pixels
296  'navFrameResizable' => 0,
297  'resizeTextareas' => 1,
298  'resizeTextareas_MaxHeight' => 500,
299  'resizeTextareas_Flexible' => 0
300  );
301 
305  public function __construct()
306  {
307  parent::__construct();
308  $this->name = self::getCookieName();
309  $this->loginType = 'BE';
310  $this->OS = TYPO3_OS;
311  }
312 
319  public function isAdmin()
320  {
321  return is_array($this->user) && ($this->user['admin'] & 1) == 1;
322  }
323 
332  public function isMemberOfGroup($groupId)
333  {
334  $groupId = (int)$groupId;
335  if ($this->groupList && $groupId) {
336  return GeneralUtility::inList($this->groupList, $groupId);
337  }
338  return false;
339  }
340 
356  public function doesUserHaveAccess($row, $perms)
357  {
358  $userPerms = $this->calcPerms($row);
359  return ($userPerms & $perms) == $perms;
360  }
361 
378  public function isInWebMount($id, $readPerms = '', $exitOnError = 0)
379  {
380  if (!$GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts'] || $this->isAdmin()) {
381  return 1;
382  }
383  $id = (int)$id;
384  // Check if input id is an offline version page in which case we will map id to the online version:
385  $checkRec = BackendUtility::getRecord('pages', $id, 'pid,t3ver_oid');
386  if ($checkRec['pid'] == -1) {
387  $id = (int)$checkRec['t3ver_oid'];
388  }
389  if (!$readPerms) {
390  $readPerms = $this->getPagePermsClause(1);
391  }
392  if ($id > 0) {
393  $wM = $this->returnWebmounts();
394  $rL = BackendUtility::BEgetRootLine($id, ' AND ' . $readPerms);
395  foreach ($rL as $v) {
396  if ($v['uid'] && in_array($v['uid'], $wM)) {
397  return $v['uid'];
398  }
399  }
400  }
401  if ($exitOnError) {
402  throw new \RuntimeException('Access Error: This page is not within your DB-mounts', 1294586445);
403  }
404  return null;
405  }
406 
415  public function modAccess($conf, $exitOnError)
416  {
417  if (!BackendUtility::isModuleSetInTBE_MODULES($conf['name'])) {
418  if ($exitOnError) {
419  throw new \RuntimeException('Fatal Error: This module "' . $conf['name'] . '" is not enabled in TBE_MODULES', 1294586446);
420  }
421  return false;
422  }
423  // Workspaces check:
424  if (
425  !empty($conf['workspaces'])
426  && \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')
427  && ($this->workspace !== 0 || !GeneralUtility::inList($conf['workspaces'], 'online'))
428  && ($this->workspace !== -1 || !GeneralUtility::inList($conf['workspaces'], 'offline'))
429  && ($this->workspace <= 0 || !GeneralUtility::inList($conf['workspaces'], 'custom'))
430  ) {
431  if ($exitOnError) {
432  throw new \RuntimeException('Workspace Error: This module "' . $conf['name'] . '" is not available under the current workspace', 1294586447);
433  }
434  return false;
435  }
436  // Returns TRUE if conf[access] is not set at all or if the user is admin
437  if (!$conf['access'] || $this->isAdmin()) {
438  return true;
439  }
440  // If $conf['access'] is set but not with 'admin' then we return TRUE, if the module is found in the modList
441  $acs = false;
442  if (!strstr($conf['access'], 'admin') && $conf['name']) {
443  $acs = $this->check('modules', $conf['name']);
444  }
445  if (!$acs && $exitOnError) {
446  throw new \RuntimeException('Access Error: You don\'t have access to this module.', 1294586448);
447  } else {
448  return $acs;
449  }
450  }
451 
467  public function getPagePermsClause($perms)
468  {
469  if (is_array($this->user)) {
470  if ($this->isAdmin()) {
471  return ' 1=1';
472  }
473  $perms = (int)$perms;
474  // Make sure it's integer.
475  $str = ' (' . '(pages.perms_everybody & ' . $perms . ' = ' . $perms . ')' . ' OR (pages.perms_userid = '
476  . $this->user['uid'] . ' AND pages.perms_user & ' . $perms . ' = ' . $perms . ')';
477  // User
478  if ($this->groupList) {
479  // Group (if any is set)
480  $str .= ' OR (pages.perms_groupid in (' . $this->groupList . ') AND pages.perms_group & '
481  . $perms . ' = ' . $perms . ')';
482  }
483  $str .= ')';
484  // ****************
485  // getPagePermsClause-HOOK
486  // ****************
487  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getPagePermsClause'])) {
488  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getPagePermsClause'] as $_funcRef) {
489  $_params = array('currentClause' => $str, 'perms' => $perms);
490  $str = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
491  }
492  }
493  return $str;
494  } else {
495  return ' 1=0';
496  }
497  }
498 
508  public function calcPerms($row)
509  {
510  // Return 31 for admin users.
511  if ($this->isAdmin()) {
512  return Permission::ALL;
513  }
514  // Return 0 if page is not within the allowed web mount
515  if (!$this->isInWebMount($row['uid'])) {
516  return Permission::NOTHING;
517  }
518  $out = Permission::NOTHING;
519  if (
520  isset($row['perms_userid']) && isset($row['perms_user']) && isset($row['perms_groupid'])
521  && isset($row['perms_group']) && isset($row['perms_everybody']) && isset($this->groupList)
522  ) {
523  if ($this->user['uid'] == $row['perms_userid']) {
524  $out |= $row['perms_user'];
525  }
526  if ($this->isMemberOfGroup($row['perms_groupid'])) {
527  $out |= $row['perms_group'];
528  }
529  $out |= $row['perms_everybody'];
530  }
531  // ****************
532  // CALCPERMS hook
533  // ****************
534  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['calcPerms'])) {
535  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['calcPerms'] as $_funcRef) {
536  $_params = array(
537  'row' => $row,
538  'outputPermissions' => $out
539  );
540  $out = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
541  }
542  }
543  return $out;
544  }
545 
551  public function isRTE()
552  {
553  return (bool)$this->uc['edit_RTE'];
554  }
555 
566  public function check($type, $value)
567  {
568  if (isset($this->groupData[$type])) {
569  if ($this->isAdmin() || GeneralUtility::inList($this->groupData[$type], $value)) {
570  return true;
571  }
572  }
573  return false;
574  }
575 
585  public function checkAuthMode($table, $field, $value, $authMode)
586  {
587  // Admin users can do anything:
588  if ($this->isAdmin()) {
589  return true;
590  }
591  // Allow all blank values:
592  if ((string)$value === '') {
593  return true;
594  }
595  // Certain characters are not allowed in the value
596  if (preg_match('/[:|,]/', $value)) {
597  return false;
598  }
599  // Initialize:
600  $testValue = $table . ':' . $field . ':' . $value;
601  $out = true;
602  // Checking value:
603  switch ((string)$authMode) {
604  case 'explicitAllow':
605  if (!GeneralUtility::inList($this->groupData['explicit_allowdeny'], ($testValue . ':ALLOW'))) {
606  $out = false;
607  }
608  break;
609  case 'explicitDeny':
610  if (GeneralUtility::inList($this->groupData['explicit_allowdeny'], $testValue . ':DENY')) {
611  $out = false;
612  }
613  break;
614  case 'individual':
615  if (is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['columns'][$field])) {
616  $items = $GLOBALS['TCA'][$table]['columns'][$field]['config']['items'];
617  if (is_array($items)) {
618  foreach ($items as $iCfg) {
619  if ((string)$iCfg[1] === (string)$value && $iCfg[4]) {
620  switch ((string)$iCfg[4]) {
621  case 'EXPL_ALLOW':
622  if (!GeneralUtility::inList($this->groupData['explicit_allowdeny'], ($testValue . ':ALLOW'))) {
623  $out = false;
624  }
625  break;
626  case 'EXPL_DENY':
627  if (GeneralUtility::inList($this->groupData['explicit_allowdeny'], $testValue . ':DENY')) {
628  $out = false;
629  }
630  break;
631  }
632  break;
633  }
634  }
635  }
636  }
637  break;
638  }
639  return $out;
640  }
641 
648  public function checkLanguageAccess($langValue)
649  {
650  // The users language list must be non-blank - otherwise all languages are allowed.
651  if (trim($this->groupData['allowed_languages']) !== '') {
652  $langValue = (int)$langValue;
653  // Language must either be explicitly allowed OR the lang Value be "-1" (all languages)
654  if ($langValue != -1 && !$this->check('allowed_languages', $langValue)) {
655  return false;
656  }
657  }
658  return true;
659  }
660 
668  public function checkFullLanguagesAccess($table, $record)
669  {
670  $recordLocalizationAccess = $this->checkLanguageAccess(0);
671  if ($recordLocalizationAccess && (BackendUtility::isTableLocalizable($table) || isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']))) {
672  if (isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable'])) {
673  $l10nTable = $GLOBALS['TCA'][$table]['ctrl']['transForeignTable'];
674  $pointerField = $GLOBALS['TCA'][$l10nTable]['ctrl']['transOrigPointerField'];
675  $pointerValue = $record['uid'];
676  } else {
677  $l10nTable = $table;
678  $pointerField = $GLOBALS['TCA'][$l10nTable]['ctrl']['transOrigPointerField'];
679  $pointerValue = $record[$pointerField] > 0 ? $record[$pointerField] : $record['uid'];
680  }
681  $recordLocalizations = BackendUtility::getRecordsByField($l10nTable, $pointerField, $pointerValue, '', '', '', '1');
682  if (is_array($recordLocalizations)) {
683  foreach ($recordLocalizations as $localization) {
684  $recordLocalizationAccess = $recordLocalizationAccess
685  && $this->checkLanguageAccess($localization[$GLOBALS['TCA'][$l10nTable]['ctrl']['languageField']]);
686  if (!$recordLocalizationAccess) {
687  break;
688  }
689  }
690  }
691  }
692  return $recordLocalizationAccess;
693  }
694 
710  public function recordEditAccessInternals($table, $idOrRow, $newRecord = false, $deletedRecord = false, $checkFullLanguageAccess = false)
711  {
712  if (!isset($GLOBALS['TCA'][$table])) {
713  return false;
714  }
715  // Always return TRUE for Admin users.
716  if ($this->isAdmin()) {
717  return true;
718  }
719  // Fetching the record if the $idOrRow variable was not an array on input:
720  if (!is_array($idOrRow)) {
721  if ($deletedRecord) {
722  $idOrRow = BackendUtility::getRecord($table, $idOrRow, '*', '', false);
723  } else {
724  $idOrRow = BackendUtility::getRecord($table, $idOrRow);
725  }
726  if (!is_array($idOrRow)) {
727  $this->errorMsg = 'ERROR: Record could not be fetched.';
728  return false;
729  }
730  }
731  // Checking languages:
732  if ($GLOBALS['TCA'][$table]['ctrl']['languageField']) {
733  // Language field must be found in input row - otherwise it does not make sense.
734  if (isset($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) {
735  if (!$this->checkLanguageAccess($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) {
736  $this->errorMsg = 'ERROR: Language was not allowed.';
737  return false;
738  } elseif (
739  $checkFullLanguageAccess && $idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']] == 0
740  && !$this->checkFullLanguagesAccess($table, $idOrRow)
741  ) {
742  $this->errorMsg = 'ERROR: Related/affected language was not allowed.';
743  return false;
744  }
745  } else {
746  $this->errorMsg = 'ERROR: The "languageField" field named "'
747  . $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '" was not found in testing record!';
748  return false;
749  }
750  } elseif (
751  isset($GLOBALS['TCA'][$table]['ctrl']['transForeignTable']) && $checkFullLanguageAccess &&
752  !$this->checkFullLanguagesAccess($table, $idOrRow)
753  ) {
754  return false;
755  }
756  // Checking authMode fields:
757  if (is_array($GLOBALS['TCA'][$table]['columns'])) {
758  foreach ($GLOBALS['TCA'][$table]['columns'] as $fieldName => $fieldValue) {
759  if (isset($idOrRow[$fieldName])) {
760  if (
761  $fieldValue['config']['type'] === 'select' && $fieldValue['config']['authMode']
762  && $fieldValue['config']['authMode_enforce'] === 'strict'
763  ) {
764  if (!$this->checkAuthMode($table, $fieldName, $idOrRow[$fieldName], $fieldValue['config']['authMode'])) {
765  $this->errorMsg = 'ERROR: authMode "' . $fieldValue['config']['authMode']
766  . '" failed for field "' . $fieldName . '" with value "'
767  . $idOrRow[$fieldName] . '" evaluated';
768  return false;
769  }
770  }
771  }
772  }
773  }
774  // Checking "editlock" feature (doesn't apply to new records)
775  if (!$newRecord && $GLOBALS['TCA'][$table]['ctrl']['editlock']) {
776  if (isset($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['editlock']])) {
777  if ($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['editlock']]) {
778  $this->errorMsg = 'ERROR: Record was locked for editing. Only admin users can change this state.';
779  return false;
780  }
781  } else {
782  $this->errorMsg = 'ERROR: The "editLock" field named "' . $GLOBALS['TCA'][$table]['ctrl']['editlock']
783  . '" was not found in testing record!';
784  return false;
785  }
786  }
787  // Checking record permissions
788  // THIS is where we can include a check for "perms_" fields for other records than pages...
789  // Process any hooks
790  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['recordEditAccessInternals'])) {
791  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['recordEditAccessInternals'] as $funcRef) {
792  $params = array(
793  'table' => $table,
794  'idOrRow' => $idOrRow,
795  'newRecord' => $newRecord
796  );
797  if (!GeneralUtility::callUserFunction($funcRef, $params, $this)) {
798  return false;
799  }
800  }
801  }
802  // Finally, return TRUE if all is well.
803  return true;
804  }
805 
816  public function isPSet($compiledPermissions, $tableName, $actionType = '')
817  {
818  if ($this->isAdmin()) {
819  $result = true;
820  } elseif ($tableName == 'pages') {
821  switch ($actionType) {
822  case 'edit':
823  $result = ($compiledPermissions & Permission::PAGE_EDIT) !== 0;
824  break;
825  case 'new':
826  // Create new page OR page content
827  $result = ($compiledPermissions & Permission::PAGE_NEW + Permission::CONTENT_EDIT) !== 0;
828  break;
829  case 'delete':
830  $result = ($compiledPermissions & Permission::PAGE_DELETE) !== 0;
831  break;
832  case 'editcontent':
833  $result = ($compiledPermissions & Permission::CONTENT_EDIT) !== 0;
834  break;
835  default:
836  $result = false;
837  }
838  } else {
839  $result = ($compiledPermissions & Permission::CONTENT_EDIT) !== 0;
840  }
841  return $result;
842  }
843 
849  public function mayMakeShortcut()
850  {
851  return $this->getTSConfigVal('options.enableBookmarks')
852  && !$this->getTSConfigVal('options.mayNotCreateEditBookmarks');
853  }
854 
866  public function workspaceCannotEditRecord($table, $recData)
867  {
868  // Only test offline spaces:
869  if ($this->workspace !== 0) {
870  if (!is_array($recData)) {
871  $recData = BackendUtility::getRecord(
872  $table,
873  $recData,
874  'pid' . ($GLOBALS['TCA'][$table]['ctrl']['versioningWS'] ? ',t3ver_wsid,t3ver_stage' : '')
875  );
876  }
877  if (is_array($recData)) {
878  // We are testing a "version" (identified by a pid of -1): it can be edited provided
879  // that workspace matches and versioning is enabled for the table.
880  if ((int)$recData['pid'] === -1) {
881  // No versioning, basic error, inconsistency even! Such records should not have a pid of -1!
882  if (!$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
883  return 'Versioning disabled for table';
884  } elseif ((int)$recData['t3ver_wsid'] !== $this->workspace) {
885  // So does workspace match?
886  return 'Workspace ID of record didn\'t match current workspace';
887  } else {
888  // So is the user allowed to "use" the edit stage within the workspace?
889  return $this->workspaceCheckStageForCurrent(0)
890  ? false
891  : 'User\'s access level did not allow for editing';
892  }
893  } else {
894  // We are testing a "live" record:
895  // For "Live" records, check that PID for table allows editing
896  if ($res = $this->workspaceAllowLiveRecordsInPID($recData['pid'], $table)) {
897  // Live records are OK in this branch, but what about the stage of branch point, if any:
898  // OK
899  return $res > 0
900  ? false
901  : 'Stage for versioning root point and users access level did not allow for editing';
902  } else {
903  // If not offline and not in versionized branch, output error:
904  return 'Online record was not in versionized branch!';
905  }
906  }
907  } else {
908  return 'No record';
909  }
910  } else {
911  // OK because workspace is 0
912  return false;
913  }
914  }
915 
924  public function workspaceCannotEditOfflineVersion($table, $recData)
925  {
926  if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
927  if (!is_array($recData)) {
928  $recData = BackendUtility::getRecord($table, $recData, 'uid,pid,t3ver_wsid,t3ver_stage');
929  }
930  if (is_array($recData)) {
931  if ((int)$recData['pid'] === -1) {
932  return $this->workspaceCannotEditRecord($table, $recData);
933  } else {
934  return 'Not an offline version';
935  }
936  } else {
937  return 'No record';
938  }
939  } else {
940  return 'Table does not support versioning.';
941  }
942  }
943 
955  public function workspaceAllowLiveRecordsInPID($pid, $table)
956  {
957  // Always for Live workspace AND if live-edit is enabled
958  // and tables are completely without versioning it is ok as well.
959  if (
960  $this->workspace === 0
961  || $this->workspaceRec['live_edit'] && !$GLOBALS['TCA'][$table]['ctrl']['versioningWS']
962  || $GLOBALS['TCA'][$table]['ctrl']['versioningWS_alwaysAllowLiveEdit']
963  ) {
964  // OK to create for this table.
965  return 2;
966  } else {
967  // If the answer is FALSE it means the only valid way to create or edit records in the PID is by versioning
968  return false;
969  }
970  }
971 
979  public function workspaceCreateNewRecord($pid, $table)
980  {
981  if ($res = $this->workspaceAllowLiveRecordsInPID($pid, $table)) {
982  // If LIVE records cannot be created in the current PID due to workspace restrictions, prepare creation of placeholder-record
983  if ($res < 0) {
984  // Stage for versioning root point and users access level did not allow for editing
985  return false;
986  }
987  } elseif (!$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
988  // So, if no live records were allowed, we have to create a new version of this record:
989  return false;
990  }
991  return true;
992  }
993 
1002  public function workspaceAllowAutoCreation($table, $id, $recpid)
1003  {
1004  // Auto-creation of version: In offline workspace, test if versioning is
1005  // enabled and look for workspace version of input record.
1006  // If there is no versionized record found we will create one and save to that.
1007  if (
1008  $this->workspace !== 0
1009  && $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] && $recpid >= 0
1010  && !BackendUtility::getWorkspaceVersionOfRecord($this->workspace, $table, $id, 'uid')
1011  ) {
1012  // There must be no existing version of this record in workspace.
1013  return true;
1014  }
1015  return false;
1016  }
1017 
1027  public function workspaceCheckStageForCurrent($stage)
1028  {
1029  // Always allow for admins
1030  if ($this->isAdmin()) {
1031  return true;
1032  }
1033  if ($this->workspace !== 0 && \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
1034  $stage = (int)$stage;
1035  $stat = $this->checkWorkspaceCurrent();
1036  // Check if custom staging is activated
1037  $workspaceRec = BackendUtility::getRecord('sys_workspace', $stat['uid']);
1038  if ($workspaceRec['custom_stages'] > 0 && $stage !== 0 && $stage !== -10) {
1039  // Get custom stage record
1040  $workspaceStageRec = BackendUtility::getRecord('sys_workspace_stage', $stage);
1041  // Check if the user is responsible for the current stage
1042  if (
1043  $stat['_ACCESS'] === 'owner'
1044  || $stat['_ACCESS'] === 'member'
1045  && GeneralUtility::inList($workspaceStageRec['responsible_persons'], 'be_users_' . $this->user['uid'])
1046  ) {
1047  return true;
1048  }
1049  // Check if the user is in a group which is responsible for the current stage
1050  foreach ($this->userGroupsUID as $groupUid) {
1051  if (
1052  $stat['_ACCESS'] === 'owner'
1053  || $stat['_ACCESS'] === 'member'
1054  && GeneralUtility::inList($workspaceStageRec['responsible_persons'], 'be_groups_' . $groupUid)
1055  ) {
1056  return true;
1057  }
1058  }
1059  } elseif ($stage == -10 || $stage == -20) {
1060  if ($stat['_ACCESS'] === 'owner') {
1061  return true;
1062  } else {
1063  return false;
1064  }
1065  } else {
1066  $memberStageLimit = $this->workspaceRec['review_stage_edit'] ? 1 : 0;
1067  if (
1068  $stat['_ACCESS'] === 'owner'
1069  || $stat['_ACCESS'] === 'reviewer' && $stage <= 1
1070  || $stat['_ACCESS'] === 'member' && $stage <= $memberStageLimit
1071  ) {
1072  return true;
1073  }
1074  }
1075  } else {
1076  // Always OK for live workspace.
1077  return true;
1078  }
1079  return false;
1080  }
1081 
1092  public function workspacePublishAccess($wsid)
1093  {
1094  if ($this->isAdmin()) {
1095  return true;
1096  }
1097  // If no access to workspace, of course you cannot publish!
1098  $retVal = false;
1099  $wsAccess = $this->checkWorkspace($wsid);
1100  if ($wsAccess) {
1101  switch ($wsAccess['uid']) {
1102  case 0:
1103  // Live workspace
1104  // If access to Live workspace, no problem.
1105  $retVal = true;
1106  break;
1107  default:
1108  // Custom workspace
1109  $retVal = $wsAccess['_ACCESS'] === 'owner' || $this->checkWorkspace(0) && !($wsAccess['publish_access'] & Permission::PAGE_EDIT);
1110  // Either be an adminuser OR have access to online
1111  // workspace which is OK as well as long as publishing
1112  // access is not limited by workspace option.
1113  }
1114  }
1115  return $retVal;
1116  }
1117 
1123  public function workspaceSwapAccess()
1124  {
1125  if ($this->workspace > 0 && (int)$this->workspaceRec['swap_modes'] === 2) {
1126  return false;
1127  } else {
1128  return true;
1129  }
1130  }
1131 
1140  public function getTSConfig($objectString, $config = '')
1141  {
1142  if (!is_array($config)) {
1143  // Getting Root-ts if not sent
1144  $config = $this->userTS;
1145  }
1146  $TSConf = array('value' => null, 'properties' => null);
1147  $parts = GeneralUtility::trimExplode('.', $objectString, true, 2);
1148  $key = $parts[0];
1149  if ($key !== '') {
1150  if (count($parts) > 1 && $parts[1] !== '') {
1151  // Go on, get the next level
1152  if (is_array($config[$key . '.'])) {
1153  $TSConf = $this->getTSConfig($parts[1], $config[$key . '.']);
1154  }
1155  } else {
1156  $TSConf['value'] = $config[$key];
1157  $TSConf['properties'] = $config[$key . '.'];
1158  }
1159  }
1160  return $TSConf;
1161  }
1162 
1170  public function getTSConfigVal($objectString)
1171  {
1172  $TSConf = $this->getTSConfig($objectString);
1173  return $TSConf['value'];
1174  }
1175 
1183  public function getTSConfigProp($objectString)
1184  {
1185  $TSConf = $this->getTSConfig($objectString);
1186  return $TSConf['properties'];
1187  }
1188 
1197  public function returnWebmounts()
1198  {
1199  return (string)$this->groupData['webmounts'] != '' ? explode(',', $this->groupData['webmounts']) : array();
1200  }
1201 
1209  public function setWebmounts(array $mountPointUids, $append = false)
1210  {
1211  if (empty($mountPointUids)) {
1212  return;
1213  }
1214  if ($append) {
1215  $currentWebMounts = GeneralUtility::intExplode(',', $this->groupData['webmounts']);
1216  $mountPointUids = array_merge($currentWebMounts, $mountPointUids);
1217  }
1218  $this->groupData['webmounts'] = implode(',', array_unique($mountPointUids));
1219  }
1220 
1229  public function jsConfirmation($bitmask)
1230  {
1231  $alertPopup = $this->getTSConfig('options.alertPopups');
1232  if (empty($alertPopup['value'])) {
1233  // Default: show all warnings
1234  $alertPopup = 255;
1235  } else {
1236  $alertPopup = (int)$alertPopup['value'];
1237  }
1238  // Show confirmation
1239  return ($alertPopup & $bitmask) == $bitmask;
1240  }
1241 
1252  public function fetchGroupData()
1253  {
1254  if ($this->user['uid']) {
1255  // Get lists for the be_user record and set them as default/primary values.
1256  // Enabled Backend Modules
1257  $this->dataLists['modList'] = $this->user['userMods'];
1258  // Add Allowed Languages
1259  $this->dataLists['allowed_languages'] = $this->user['allowed_languages'];
1260  // Set user value for workspace permissions.
1261  $this->dataLists['workspace_perms'] = $this->user['workspace_perms'];
1262  // Database mountpoints
1263  $this->dataLists['webmount_list'] = $this->user['db_mountpoints'];
1264  // File mountpoints
1265  $this->dataLists['filemount_list'] = $this->user['file_mountpoints'];
1266  // Fileoperation permissions
1267  $this->dataLists['file_permissions'] = $this->user['file_permissions'];
1268  // Setting default User TSconfig:
1269  $this->TSdataArray[] = $this->addTScomment('From $GLOBALS["TYPO3_CONF_VARS"]["BE"]["defaultUserTSconfig"]:')
1270  . $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultUserTSconfig'];
1271  // Default TSconfig for admin-users
1272  if ($this->isAdmin()) {
1273  $this->TSdataArray[] = $this->addTScomment('"admin" user presets:') . '
1274  admPanel.enable.all = 1
1275  ';
1276  if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('sys_note')) {
1277  $this->TSdataArray[] = '
1278  // Setting defaults for sys_note author / email...
1279  TCAdefaults.sys_note.author = ' . $this->user['realName'] . '
1280  TCAdefaults.sys_note.email = ' . $this->user['email'] . '
1281  ';
1282  }
1283  }
1284  // BE_GROUPS:
1285  // Get the groups...
1286  if (!empty($this->user[$this->usergroup_column])) {
1287  // Fetch groups will add a lot of information to the internal arrays: modules, accesslists, TSconfig etc.
1288  // Refer to fetchGroups() function.
1289  $this->fetchGroups($this->user[$this->usergroup_column]);
1290  }
1291 
1292  // Populating the $this->userGroupsUID -array with the groups in the order in which they were LAST included.!!
1293  $this->userGroupsUID = array_reverse(array_unique(array_reverse($this->includeGroupArray)));
1294  // Finally this is the list of group_uid's in the order they are parsed (including subgroups!)
1295  // and without duplicates (duplicates are presented with their last entrance in the list,
1296  // which thus reflects the order of the TypoScript in TSconfig)
1297  $this->groupList = implode(',', $this->userGroupsUID);
1298  $this->setCachedList($this->groupList);
1299 
1300  // Add the TSconfig for this specific user:
1301  $this->TSdataArray[] = $this->addTScomment('USER TSconfig field') . $this->user['TSconfig'];
1302  // Check include lines.
1303  $this->TSdataArray = \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::checkIncludeLines_array($this->TSdataArray);
1304  // Imploding with "[global]" will make sure that non-ended confinements with braces are ignored.
1305  $this->userTS_text = implode(LF . '[GLOBAL]' . LF, $this->TSdataArray);
1306  if (!$this->userTS_dontGetCached) {
1307  // Perform TS-Config parsing with condition matching
1308  $parseObj = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Configuration\TsConfigParser::class);
1309  $res = $parseObj->parseTSconfig($this->userTS_text, 'userTS');
1310  if ($res) {
1311  $this->userTS = $res['TSconfig'];
1312  $this->userTSUpdated = (bool)$res['cached'];
1313  }
1314  } else {
1315  // Parsing the user TSconfig (or getting from cache)
1316  $hash = md5('userTS:' . $this->userTS_text);
1317  $cachedContent = BackendUtility::getHash($hash);
1318  if (is_array($cachedContent) && !$this->userTS_dontGetCached) {
1319  $this->userTS = $cachedContent;
1320  } else {
1321  $parseObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::class);
1322  $parseObj->parse($this->userTS_text);
1323  $this->userTS = $parseObj->setup;
1324  BackendUtility::storeHash($hash, $this->userTS, 'BE_USER_TSconfig');
1325  // Update UC:
1326  $this->userTSUpdated = true;
1327  }
1328  }
1329  // Processing webmounts
1330  // Admin's always have the root mounted
1331  if ($this->isAdmin() && !$this->getTSConfigVal('options.dontMountAdminMounts')) {
1332  $this->dataLists['webmount_list'] = '0,' . $this->dataLists['webmount_list'];
1333  }
1334  // The lists are cleaned for duplicates
1335  $this->groupData['webmounts'] = GeneralUtility::uniqueList($this->dataLists['webmount_list']);
1336  $this->groupData['pagetypes_select'] = GeneralUtility::uniqueList($this->dataLists['pagetypes_select']);
1337  $this->groupData['tables_select'] = GeneralUtility::uniqueList($this->dataLists['tables_modify'] . ',' . $this->dataLists['tables_select']);
1338  $this->groupData['tables_modify'] = GeneralUtility::uniqueList($this->dataLists['tables_modify']);
1339  $this->groupData['non_exclude_fields'] = GeneralUtility::uniqueList($this->dataLists['non_exclude_fields']);
1340  $this->groupData['explicit_allowdeny'] = GeneralUtility::uniqueList($this->dataLists['explicit_allowdeny']);
1341  $this->groupData['allowed_languages'] = GeneralUtility::uniqueList($this->dataLists['allowed_languages']);
1342  $this->groupData['custom_options'] = GeneralUtility::uniqueList($this->dataLists['custom_options']);
1343  $this->groupData['modules'] = GeneralUtility::uniqueList($this->dataLists['modList']);
1344  $this->groupData['file_permissions'] = GeneralUtility::uniqueList($this->dataLists['file_permissions']);
1345  $this->groupData['workspace_perms'] = $this->dataLists['workspace_perms'];
1346 
1347  // Checking read access to webmounts:
1348  if (trim($this->groupData['webmounts']) !== '') {
1349  $webmounts = explode(',', $this->groupData['webmounts']);
1350  // Explode mounts
1351  // Selecting all webmounts with permission clause for reading
1352  $where = 'deleted=0 AND uid IN (' . $this->groupData['webmounts'] . ') AND ' . $this->getPagePermsClause(1);
1353  $MProws = $this->db->exec_SELECTgetRows('uid', 'pages', $where, '', '', '', 'uid');
1354  foreach ($webmounts as $idx => $mountPointUid) {
1355  // If the mount ID is NOT found among selected pages, unset it:
1356  if ($mountPointUid > 0 && !isset($MProws[$mountPointUid])) {
1357  unset($webmounts[$idx]);
1358  }
1359  }
1360  // Implode mounts in the end.
1361  $this->groupData['webmounts'] = implode(',', $webmounts);
1362  }
1363  // Setting up workspace situation (after webmounts are processed!):
1364  $this->workspaceInit();
1365  }
1366  }
1367 
1377  public function fetchGroups($grList, $idList = '')
1378  {
1379  // Fetching records of the groups in $grList (which are not blocked by lockedToDomain either):
1380  $lockToDomain_SQL = ' AND (lockToDomain=\'\' OR lockToDomain IS NULL OR lockToDomain=' . $this->db->fullQuoteStr(GeneralUtility::getIndpEnv('HTTP_HOST'), $this->usergroup_table) . ')';
1381  $grList = $this->db->cleanIntList($grList);
1382  $whereSQL = 'deleted=0 AND hidden=0 AND pid=0 AND uid IN (' . $grList . ')' . $lockToDomain_SQL;
1383  // Hook for manipulation of the WHERE sql sentence which controls which BE-groups are included
1384  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroupQuery'])) {
1385  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroupQuery'] as $classRef) {
1386  $hookObj = GeneralUtility::getUserObj($classRef);
1387  if (method_exists($hookObj, 'fetchGroupQuery_processQuery')) {
1388  $whereSQL = $hookObj->fetchGroupQuery_processQuery($this, $grList, $idList, $whereSQL);
1389  }
1390  }
1391  }
1392  $res = $this->db->exec_SELECTquery('*', $this->usergroup_table, $whereSQL);
1393  // The userGroups array is filled
1394  while ($row = $this->db->sql_fetch_assoc($res)) {
1395  $this->userGroups[$row['uid']] = $row;
1396  }
1397  $this->db->sql_free_result($res);
1398  // Traversing records in the correct order
1399  foreach (explode(',', $grList) as $uid) {
1400  // Get row:
1401  $row = $this->userGroups[$uid];
1402  // Must be an array and $uid should not be in the idList, because then it is somewhere previously in the grouplist
1403  if (is_array($row) && !GeneralUtility::inList($idList, $uid)) {
1404  // Include sub groups
1405  if (trim($row['subgroup'])) {
1406  // Make integer list
1407  $theList = implode(',', GeneralUtility::intExplode(',', $row['subgroup']));
1408  // Call recursively, pass along list of already processed groups so they are not recursed again.
1409  $this->fetchGroups($theList, $idList . ',' . $uid);
1410  }
1411  // Add the group uid, current list, TSconfig to the internal arrays.
1412  $this->includeGroupArray[] = $uid;
1413  $this->includeHierarchy[] = $idList;
1414  $this->TSdataArray[] = $this->addTScomment('Group "' . $row['title'] . '" [' . $row['uid'] . '] TSconfig field:') . $row['TSconfig'];
1415  // Mount group database-mounts
1416  if (($this->user['options'] & Permission::PAGE_SHOW) == 1) {
1417  $this->dataLists['webmount_list'] .= ',' . $row['db_mountpoints'];
1418  }
1419  // Mount group file-mounts
1420  if (($this->user['options'] & Permission::PAGE_EDIT) == 2) {
1421  $this->dataLists['filemount_list'] .= ',' . $row['file_mountpoints'];
1422  }
1423  // The lists are made: groupMods, tables_select, tables_modify, pagetypes_select, non_exclude_fields, explicit_allowdeny, allowed_languages, custom_options
1424  $this->dataLists['modList'] .= ',' . $row['groupMods'];
1425  $this->dataLists['tables_select'] .= ',' . $row['tables_select'];
1426  $this->dataLists['tables_modify'] .= ',' . $row['tables_modify'];
1427  $this->dataLists['pagetypes_select'] .= ',' . $row['pagetypes_select'];
1428  $this->dataLists['non_exclude_fields'] .= ',' . $row['non_exclude_fields'];
1429  $this->dataLists['explicit_allowdeny'] .= ',' . $row['explicit_allowdeny'];
1430  $this->dataLists['allowed_languages'] .= ',' . $row['allowed_languages'];
1431  $this->dataLists['custom_options'] .= ',' . $row['custom_options'];
1432  $this->dataLists['file_permissions'] .= ',' . $row['file_permissions'];
1433  // Setting workspace permissions:
1434  $this->dataLists['workspace_perms'] |= $row['workspace_perms'];
1435  // If this function is processing the users OWN group-list (not subgroups) AND
1436  // if the ->firstMainGroup is not set, then the ->firstMainGroup will be set.
1437  if ($idList === '' && !$this->firstMainGroup) {
1438  $this->firstMainGroup = $uid;
1439  }
1440  }
1441  }
1442  // HOOK: fetchGroups_postProcessing
1443  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroups_postProcessing'])) {
1444  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroups_postProcessing'] as $_funcRef) {
1445  $_params = array();
1446  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1447  }
1448  }
1449  }
1450 
1462  public function setCachedList($cList)
1463  {
1464  if ((string)$cList != (string)$this->user['usergroup_cached_list']) {
1465  $this->db->exec_UPDATEquery('be_users', 'uid=' . (int)$this->user['uid'], array('usergroup_cached_list' => $cList));
1466  }
1467  }
1468 
1475  protected function initializeFileStorages()
1476  {
1477  $this->fileStorages = array();
1479  $storageRepository = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\StorageRepository::class);
1480  // Admin users have all file storages visible, without any filters
1481  if ($this->isAdmin()) {
1482  $storageObjects = $storageRepository->findAll();
1483  foreach ($storageObjects as $storageObject) {
1484  $this->fileStorages[$storageObject->getUid()] = $storageObject;
1485  }
1486  } else {
1487  // Regular users only have storages that are defined in their filemounts
1488  // Permissions and file mounts for the storage are added in StoragePermissionAspect
1489  foreach ($this->getFileMountRecords() as $row) {
1490  if (!array_key_exists((int)$row['base'], $this->fileStorages)) {
1491  $storageObject = $storageRepository->findByUid($row['base']);
1492  if ($storageObject) {
1493  $this->fileStorages[$storageObject->getUid()] = $storageObject;
1494  }
1495  }
1496  }
1497  }
1498 
1499  // This has to be called always in order to set certain filters
1501  }
1502 
1509  public function getCategoryMountPoints()
1510  {
1511  $categoryMountPoints = '';
1512 
1513  // Category mounts of the groups
1514  if (is_array($this->userGroups)) {
1515  foreach ($this->userGroups as $group) {
1516  if ($group['category_perms']) {
1517  $categoryMountPoints .= ',' . $group['category_perms'];
1518  }
1519  }
1520  }
1521 
1522  // Category mounts of the user record
1523  if ($this->user['category_perms']) {
1524  $categoryMountPoints .= ',' . $this->user['category_perms'];
1525  }
1526 
1527  // Make the ids unique
1528  $categoryMountPoints = GeneralUtility::trimExplode(',', $categoryMountPoints);
1529  $categoryMountPoints = array_filter($categoryMountPoints); // remove empty value
1530  $categoryMountPoints = array_unique($categoryMountPoints); // remove unique value
1531 
1532  return $categoryMountPoints;
1533  }
1534 
1542  public function getFileMountRecords()
1543  {
1544  static $fileMountRecordCache = array();
1545 
1546  if (!empty($fileMountRecordCache)) {
1547  return $fileMountRecordCache;
1548  }
1549 
1550  // Processing file mounts (both from the user and the groups)
1551  $fileMounts = array_unique(GeneralUtility::intExplode(',', $this->dataLists['filemount_list'], true));
1552 
1553  // Limit file mounts if set in workspace record
1554  if ($this->workspace > 0 && !empty($this->workspaceRec['file_mountpoints'])) {
1555  $workspaceFileMounts = GeneralUtility::intExplode(',', $this->workspaceRec['file_mountpoints'], true);
1556  $fileMounts = array_intersect($fileMounts, $workspaceFileMounts);
1557  }
1558 
1559  if (!empty($fileMounts)) {
1560  $orderBy = isset($GLOBALS['TCA']['sys_filemounts']['ctrl']['default_sortby'])
1561  ? $this->db->stripOrderBy($GLOBALS['TCA']['sys_filemounts']['ctrl']['default_sortby'])
1562  : 'sorting';
1563  $fileMountRecords = $this->db->exec_SELECTgetRows(
1564  '*',
1565  'sys_filemounts',
1566  'deleted=0 AND hidden=0 AND pid=0 AND uid IN (' . implode(',', $fileMounts) . ')',
1567  '',
1568  $orderBy
1569  );
1570  foreach ($fileMountRecords as $fileMount) {
1571  $fileMountRecordCache[$fileMount['base'] . $fileMount['path']] = $fileMount;
1572  }
1573  }
1574 
1575  // Read-only file mounts
1576  $readOnlyMountPoints = trim($GLOBALS['BE_USER']->getTSConfigVal('options.folderTree.altElementBrowserMountPoints'));
1577  if ($readOnlyMountPoints) {
1578  // We cannot use the API here but need to fetch the default storage record directly
1579  // to not instantiate it (which directly applies mount points) before all mount points are resolved!
1580  $whereClause = 'is_default=1 ' . BackendUtility::BEenableFields('sys_file_storage') . BackendUtility::deleteClause('sys_file_storage');
1581  $defaultStorageRow = $this->db->exec_SELECTgetSingleRow('uid', 'sys_file_storage', $whereClause);
1582  $readOnlyMountPointArray = GeneralUtility::trimExplode(',', $readOnlyMountPoints);
1583  foreach ($readOnlyMountPointArray as $readOnlyMountPoint) {
1584  $readOnlyMountPointConfiguration = GeneralUtility::trimExplode(':', $readOnlyMountPoint);
1585  if (count($readOnlyMountPointConfiguration) === 2) {
1586  // A storage is passed in the configuration
1587  $storageUid = (int)$readOnlyMountPointConfiguration[0];
1588  $path = $readOnlyMountPointConfiguration[1];
1589  } else {
1590  if (empty($defaultStorageRow)) {
1591  throw new \RuntimeException('Read only mount points have been defined in User TsConfig without specific storage, but a default storage could not be resolved.', 1404472382);
1592  }
1593  // Backwards compatibility: If no storage is passed, we use the default storage
1594  $storageUid = $defaultStorageRow['uid'];
1595  $path = $readOnlyMountPointConfiguration[0];
1596  }
1597  $fileMountRecordCache[$storageUid . $path] = array(
1598  'base' => $storageUid,
1599  'title' => $path,
1600  'path' => $path,
1601  'read_only' => true
1602  );
1603  }
1604  }
1605 
1606 
1607  // Personal or Group filemounts are not accessible if file mount list is set in workspace record
1608  if ($this->workspace <= 0 || empty($this->workspaceRec['file_mountpoints'])) {
1609  // If userHomePath is set, we attempt to mount it
1610  if ($GLOBALS['TYPO3_CONF_VARS']['BE']['userHomePath']) {
1611  list($userHomeStorageUid, $userHomeFilter) = explode(':', $GLOBALS['TYPO3_CONF_VARS']['BE']['userHomePath'], 2);
1612  $userHomeStorageUid = (int)$userHomeStorageUid;
1613  $userHomeFilter = '/' . ltrim($userHomeFilter, '/');
1614  if ($userHomeStorageUid > 0) {
1615  // Try and mount with [uid]_[username]
1616  $path = $userHomeFilter . $this->user['uid'] . '_' . $this->user['username'] . $GLOBALS['TYPO3_CONF_VARS']['BE']['userUploadDir'];
1617  $fileMountRecordCache[$userHomeStorageUid . $path] = array(
1618  'base' => $userHomeStorageUid,
1619  'title' => $this->user['username'],
1620  'path' => $path,
1621  'read_only' => false,
1622  'user_mount' => true
1623  );
1624  // Try and mount with only [uid]
1625  $path = $userHomeFilter . $this->user['uid'] . $GLOBALS['TYPO3_CONF_VARS']['BE']['userUploadDir'];
1626  $fileMountRecordCache[$userHomeStorageUid . $path] = array(
1627  'base' => $userHomeStorageUid,
1628  'title' => $this->user['username'],
1629  'path' => $path,
1630  'read_only' => false,
1631  'user_mount' => true
1632  );
1633  }
1634  }
1635 
1636  // Mount group home-dirs
1637  if ((is_array($this->user) && $this->user['options'] & Permission::PAGE_EDIT) == 2 && $GLOBALS['TYPO3_CONF_VARS']['BE']['groupHomePath'] != '') {
1638  // If groupHomePath is set, we attempt to mount it
1639  list($groupHomeStorageUid, $groupHomeFilter) = explode(':', $GLOBALS['TYPO3_CONF_VARS']['BE']['groupHomePath'], 2);
1640  $groupHomeStorageUid = (int)$groupHomeStorageUid;
1641  $groupHomeFilter = '/' . ltrim($groupHomeFilter, '/');
1642  if ($groupHomeStorageUid > 0) {
1643  foreach ($this->userGroups as $groupData) {
1644  $path = $groupHomeFilter . $groupData['uid'];
1645  $fileMountRecordCache[$groupHomeStorageUid . $path] = array(
1646  'base' => $groupHomeStorageUid,
1647  'title' => $groupData['title'],
1648  'path' => $path,
1649  'read_only' => false,
1650  'user_mount' => true
1651  );
1652  }
1653  }
1654  }
1655  }
1656 
1657  return $fileMountRecordCache;
1658  }
1659 
1668  public function getFileStorages()
1669  {
1670  // Initializing file mounts after the groups are fetched
1671  if ($this->fileStorages === null) {
1672  $this->initializeFileStorages();
1673  }
1674  return $this->fileStorages;
1675  }
1676 
1685  {
1686  // Add the option for also displaying the non-hidden files
1687  if ($this->uc['showHiddenFilesAndFolders']) {
1688  \TYPO3\CMS\Core\Resource\Filter\FileNameFilter::setShowHiddenFilesAndFolders(true);
1689  }
1690  }
1691 
1729  public function getFilePermissions()
1730  {
1731  if (!isset($this->filePermissions)) {
1732  $filePermissions = array(
1733  // File permissions
1734  'addFile' => false,
1735  'readFile' => false,
1736  'writeFile' => false,
1737  'copyFile' => false,
1738  'moveFile' => false,
1739  'renameFile' => false,
1740  'unzipFile' => false,
1741  'deleteFile' => false,
1742  // Folder permissions
1743  'addFolder' => false,
1744  'readFolder' => false,
1745  'writeFolder' => false,
1746  'copyFolder' => false,
1747  'moveFolder' => false,
1748  'renameFolder' => false,
1749  'deleteFolder' => false,
1750  'recursivedeleteFolder' => false
1751  );
1752  if ($this->isAdmin()) {
1753  $filePermissions = array_map('is_bool', $filePermissions);
1754  } else {
1755  $userGroupRecordPermissions = GeneralUtility::trimExplode(',', $this->groupData['file_permissions'], true);
1756  array_walk(
1757  $userGroupRecordPermissions,
1758  function ($permission) use (&$filePermissions) {
1759  $filePermissions[$permission] = true;
1760  }
1761  );
1762 
1763  // Finally overlay any userTSconfig
1764  $permissionsTsConfig = $this->getTSConfigProp('permissions.file.default');
1765  if (!empty($permissionsTsConfig)) {
1766  array_walk(
1767  $permissionsTsConfig,
1768  function ($value, $permission) use (&$filePermissions) {
1769  $filePermissions[$permission] = (bool)$value;
1770  }
1771  );
1772  }
1773  }
1774  $this->filePermissions = $filePermissions;
1775  }
1776  return $this->filePermissions;
1777  }
1778 
1789  public function getFilePermissionsForStorage(\TYPO3\CMS\Core\Resource\ResourceStorage $storageObject)
1790  {
1791  $finalUserPermissions = $this->getFilePermissions();
1792  if (!$this->isAdmin()) {
1793  $storageFilePermissions = $this->getTSConfigProp('permissions.file.storage.' . $storageObject->getUid());
1794  if (!empty($storageFilePermissions)) {
1795  array_walk(
1796  $storageFilePermissions,
1797  function ($value, $permission) use (&$finalUserPermissions) {
1798  $finalUserPermissions[$permission] = (bool)$value;
1799  }
1800  );
1801  }
1802  }
1803  return $finalUserPermissions;
1804  }
1805 
1823  public function getDefaultUploadFolder($pid = null, $table = null, $field = null)
1824  {
1825  $uploadFolder = $this->getTSConfigVal('options.defaultUploadFolder');
1826  if ($uploadFolder) {
1827  $uploadFolder = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier($uploadFolder);
1828  } else {
1829  foreach ($this->getFileStorages() as $storage) {
1830  if ($storage->isDefault() && $storage->isWritable()) {
1831  try {
1832  $uploadFolder = $storage->getDefaultFolder();
1833  if ($uploadFolder->checkActionPermission('add')) {
1834  break;
1835  }
1836  $uploadFolder = null;
1837  } catch (\TYPO3\CMS\Core\Resource\Exception $folderAccessException) {
1838  // If the folder is not accessible (no permissions / does not exist) we skip this one.
1839  }
1840  break;
1841  }
1842  }
1843  if (!$uploadFolder instanceof \TYPO3\CMS\Core\Resource\Folder) {
1845  foreach ($this->getFileStorages() as $storage) {
1846  if ($storage->isWritable()) {
1847  try {
1848  $uploadFolder = $storage->getDefaultFolder();
1849  if ($uploadFolder->checkActionPermission('add')) {
1850  break;
1851  }
1852  $uploadFolder = null;
1853  } catch (\TYPO3\CMS\Core\Resource\Exception $folderAccessException) {
1854  // If the folder is not accessible (no permissions / does not exist) try the next one.
1855  }
1856  }
1857  }
1858  }
1859  }
1860 
1861  // HOOK: getDefaultUploadFolder
1862  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getDefaultUploadFolder'])) {
1863  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getDefaultUploadFolder'] as $_funcRef) {
1864  $_params = array(
1865  'uploadFolder' => $uploadFolder,
1866  'pid' => $pid,
1867  'table' => $table,
1868  'field' => $field,
1869  );
1870  $uploadFolder = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1871  }
1872  }
1873 
1874  if ($uploadFolder instanceof \TYPO3\CMS\Core\Resource\Folder) {
1875  return $uploadFolder;
1876  } else {
1877  return false;
1878  }
1879  }
1880 
1890  {
1891  $defaultTemporaryFolder = null;
1892  $defaultFolder = $this->getDefaultUploadFolder();
1893 
1894  if ($defaultFolder !== false) {
1895  $tempFolderName = '_temp_';
1896  $createFolder = !$defaultFolder->hasFolder($tempFolderName);
1897  if ($createFolder === true) {
1898  try {
1899  $defaultTemporaryFolder = $defaultFolder->createFolder($tempFolderName);
1900  } catch (\TYPO3\CMS\Core\Resource\Exception $folderAccessException) {
1901  }
1902  } else {
1903  $defaultTemporaryFolder = $defaultFolder->getSubfolder($tempFolderName);
1904  }
1905  }
1906 
1907  return $defaultTemporaryFolder;
1908  }
1909 
1916  public function addTScomment($str)
1917  {
1918  $delimiter = '# ***********************************************';
1919  $out = $delimiter . LF;
1920  $lines = GeneralUtility::trimExplode(LF, $str);
1921  foreach ($lines as $v) {
1922  $out .= '# ' . $v . LF;
1923  }
1924  $out .= $delimiter . LF;
1925  return $out;
1926  }
1927 
1935  public function workspaceInit()
1936  {
1937  // Initializing workspace by evaluating and setting the workspace, possibly updating it in the user record!
1938  $this->setWorkspace($this->user['workspace_id']);
1939  // Limiting the DB mountpoints if there any selected in the workspace record
1941  if ($allowed_languages = $this->getTSConfigVal('options.workspaces.allowed_languages.' . $this->workspace)) {
1942  $this->groupData['allowed_languages'] = $allowed_languages;
1943  $this->groupData['allowed_languages'] = GeneralUtility::uniqueList($this->groupData['allowed_languages']);
1944  }
1945  }
1946 
1953  {
1954  $dbMountpoints = trim($this->workspaceRec['db_mountpoints']);
1955  if ($this->workspace > 0 && $dbMountpoints != '') {
1956  $filteredDbMountpoints = array();
1957  // Notice: We cannot call $this->getPagePermsClause(1);
1958  // as usual because the group-list is not available at this point.
1959  // But bypassing is fine because all we want here is check if the
1960  // workspace mounts are inside the current webmounts rootline.
1961  // The actual permission checking on page level is done elsewhere
1962  // as usual anyway before the page tree is rendered.
1963  $readPerms = '1=1';
1964  // Traverse mount points of the
1965  $dbMountpoints = GeneralUtility::intExplode(',', $dbMountpoints);
1966  foreach ($dbMountpoints as $mpId) {
1967  if ($this->isInWebMount($mpId, $readPerms)) {
1968  $filteredDbMountpoints[] = $mpId;
1969  }
1970  }
1971  // Re-insert webmounts:
1972  $filteredDbMountpoints = array_unique($filteredDbMountpoints);
1973  $this->groupData['webmounts'] = implode(',', $filteredDbMountpoints);
1974  }
1975  }
1976 
1984  public function checkWorkspace($wsRec, $fields = 'uid,title,adminusers,members,reviewers,publish_access,stagechg_notification')
1985  {
1986  $retVal = false;
1987  // If not array, look up workspace record:
1988  if (!is_array($wsRec)) {
1989  switch ((string)$wsRec) {
1990  case '0':
1991  $wsRec = array('uid' => $wsRec);
1992  break;
1993  default:
1994  if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
1995  $wsRec = $this->db->exec_SELECTgetSingleRow($fields,
1996  'sys_workspace',
1997  'pid=0 AND uid=' . (int)$wsRec . BackendUtility::deleteClause('sys_workspace'),
1998  '',
1999  'title'
2000  );
2001  }
2002  }
2003  }
2004  // If wsRec is set to an array, evaluate it:
2005  if (is_array($wsRec)) {
2006  if ($this->isAdmin()) {
2007  return array_merge($wsRec, array('_ACCESS' => 'admin'));
2008  } else {
2009  switch ((string)$wsRec['uid']) {
2010  case '0':
2011  $retVal = $this->groupData['workspace_perms'] & Permission::PAGE_SHOW
2012  ? array_merge($wsRec, array('_ACCESS' => 'online'))
2013  : false;
2014  break;
2015  default:
2016  // Checking if the guy is admin:
2017  if (GeneralUtility::inList($wsRec['adminusers'], 'be_users_' . $this->user['uid'])) {
2018  return array_merge($wsRec, array('_ACCESS' => 'owner'));
2019  }
2020  // Checking if he is owner through a user group of his:
2021  foreach ($this->userGroupsUID as $groupUid) {
2022  if (GeneralUtility::inList($wsRec['adminusers'], 'be_groups_' . $groupUid)) {
2023  return array_merge($wsRec, array('_ACCESS' => 'owner'));
2024  }
2025  }
2026  // Checking if he is reviewer user:
2027  if (GeneralUtility::inList($wsRec['reviewers'], 'be_users_' . $this->user['uid'])) {
2028  return array_merge($wsRec, array('_ACCESS' => 'reviewer'));
2029  }
2030  // Checking if he is reviewer through a user group of his:
2031  foreach ($this->userGroupsUID as $groupUid) {
2032  if (GeneralUtility::inList($wsRec['reviewers'], 'be_groups_' . $groupUid)) {
2033  return array_merge($wsRec, array('_ACCESS' => 'reviewer'));
2034  }
2035  }
2036  // Checking if he is member as user:
2037  if (GeneralUtility::inList($wsRec['members'], 'be_users_' . $this->user['uid'])) {
2038  return array_merge($wsRec, array('_ACCESS' => 'member'));
2039  }
2040  // Checking if he is member through a user group of his:
2041  foreach ($this->userGroupsUID as $groupUid) {
2042  if (GeneralUtility::inList($wsRec['members'], 'be_groups_' . $groupUid)) {
2043  return array_merge($wsRec, array('_ACCESS' => 'member'));
2044  }
2045  }
2046  }
2047  }
2048  }
2049  return $retVal;
2050  }
2051 
2059  public function checkWorkspaceCurrent()
2060  {
2061  if (!isset($this->checkWorkspaceCurrent_cache)) {
2062  $this->checkWorkspaceCurrent_cache = $this->checkWorkspace($this->workspace);
2063  }
2065  }
2066 
2073  public function setWorkspace($workspaceId)
2074  {
2075  // Check workspace validity and if not found, revert to default workspace.
2076  if (!$this->setTemporaryWorkspace($workspaceId)) {
2077  $this->setDefaultWorkspace();
2078  }
2079  // Unset access cache:
2080  $this->checkWorkspaceCurrent_cache = null;
2081  // If ID is different from the stored one, change it:
2082  if ((int)$this->workspace !== (int)$this->user['workspace_id']) {
2083  $this->user['workspace_id'] = $this->workspace;
2084  $this->db->exec_UPDATEquery('be_users', 'uid=' . (int)$this->user['uid'], array('workspace_id' => $this->user['workspace_id']));
2085  $this->simplelog('User changed workspace to "' . $this->workspace . '"');
2086  }
2087  }
2088 
2095  public function setTemporaryWorkspace($workspaceId)
2096  {
2097  $result = false;
2098  $workspaceRecord = $this->checkWorkspace($workspaceId, '*');
2099 
2100  if ($workspaceRecord) {
2101  $this->workspaceRec = $workspaceRecord;
2102  $this->workspace = (int)$workspaceId;
2103  $result = true;
2104  }
2105 
2106  return $result;
2107  }
2108 
2114  public function setDefaultWorkspace()
2115  {
2116  $this->workspace = (int)$this->getDefaultWorkspace();
2117  $this->workspaceRec = $this->checkWorkspace($this->workspace, '*');
2118  }
2119 
2126  public function setWorkspacePreview($previewState)
2127  {
2128  $this->user['workspace_preview'] = $previewState;
2129  $this->db->exec_UPDATEquery('be_users', 'uid=' . (int)$this->user['uid'], array('workspace_preview' => $this->user['workspace_preview']));
2130  }
2131 
2139  public function getDefaultWorkspace()
2140  {
2141  $defaultWorkspace = -99;
2142  if (!\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces') || $this->checkWorkspace(0)) {
2143  // Check online
2144  $defaultWorkspace = 0;
2145  } elseif ($this->checkWorkspace(-1)) {
2146  // Check offline
2147  $defaultWorkspace = -1;
2148  } elseif (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
2149  // Traverse custom workspaces:
2150  $workspaces = $this->db->exec_SELECTgetRows('uid,title,adminusers,members,reviewers', 'sys_workspace', 'pid=0' . BackendUtility::deleteClause('sys_workspace'), '', 'title');
2151  foreach ($workspaces as $rec) {
2152  if ($this->checkWorkspace($rec)) {
2153  $defaultWorkspace = $rec['uid'];
2154  break;
2155  }
2156  }
2157  }
2158  return $defaultWorkspace;
2159  }
2160 
2179  public function writelog($type, $action, $error, $details_nr, $details, $data, $tablename = '', $recuid = '', $recpid = '', $event_pid = -1, $NEWid = '', $userId = 0)
2180  {
2181  if (!$userId) {
2182  // Use the original user's ID in case of a user switch
2183  if (!empty($this->user['ses_backuserid'])) {
2184  $userId = $this->user['ses_backuserid'];
2185  } elseif (!empty($this->user['uid'])) {
2186  $userId = $this->user['uid'];
2187  }
2188  }
2189 
2190  $fields_values = array(
2191  'userid' => (int)$userId,
2192  'type' => (int)$type,
2193  'action' => (int)$action,
2194  'error' => (int)$error,
2195  'details_nr' => (int)$details_nr,
2196  'details' => $details,
2197  'log_data' => serialize($data),
2198  'tablename' => $tablename,
2199  'recuid' => (int)$recuid,
2200  'IP' => (string)GeneralUtility::getIndpEnv('REMOTE_ADDR'),
2201  'tstamp' => time(),
2202  'event_pid' => (int)$event_pid,
2203  'NEWid' => $NEWid,
2204  'workspace' => $this->workspace
2205  );
2206  $this->db->exec_INSERTquery('sys_log', $fields_values);
2207  return $this->db->sql_insert_id();
2208  }
2209 
2218  public function simplelog($message, $extKey = '', $error = 0)
2219  {
2220  return $this->writelog(4, 0, $error, 0, ($extKey ? '[' . $extKey . '] ' : '') . $message, array());
2221  }
2222 
2235  public function checkLogFailures($email, $secondsBack = 3600, $max = 3)
2236  {
2237  if ($email) {
2238  // Get last flag set in the log for sending
2239  $theTimeBack = $GLOBALS['EXEC_TIME'] - $secondsBack;
2240  $res = $this->db->exec_SELECTquery('tstamp', 'sys_log', 'type=255 AND action=4 AND tstamp>' . (int)$theTimeBack, '', 'tstamp DESC', '1');
2241  if ($testRow = $this->db->sql_fetch_assoc($res)) {
2242  $theTimeBack = $testRow['tstamp'];
2243  }
2244  $this->db->sql_free_result($res);
2245  // Check for more than $max number of error failures with the last period.
2246  $res = $this->db->exec_SELECTquery('*', 'sys_log', 'type=255 AND action=3 AND error<>0 AND tstamp>' . (int)$theTimeBack, '', 'tstamp');
2247  if ($this->db->sql_num_rows($res) > $max) {
2248  // OK, so there were more than the max allowed number of login failures - so we will send an email then.
2249  $subject = 'TYPO3 Login Failure Warning (at ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ')';
2250  $email_body = 'There have been some attempts (' . $this->db->sql_num_rows($res) . ') to login at the TYPO3
2251 site "' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '" (' . GeneralUtility::getIndpEnv('HTTP_HOST') . ').
2252 
2253 This is a dump of the failures:
2254 
2255 ';
2256  while ($testRows = $this->db->sql_fetch_assoc($res)) {
2257  $theData = unserialize($testRows['log_data']);
2258  $email_body .= date(
2259  $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'],
2260  $testRows['tstamp']
2261  ) . ': ' . @sprintf($testRows['details'], (string)$theData[0], (string)$theData[1], (string)$theData[2]);
2262  $email_body .= LF;
2263  }
2264  $from = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFrom();
2266  $mail = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class);
2267  $mail->setTo($email)->setFrom($from)->setSubject($subject)->setBody($email_body);
2268  $mail->send();
2269  // Logout written to log
2270  $this->writelog(255, 4, 0, 3, 'Failure warning (%s failures within %s seconds) sent by email to %s', array($this->db->sql_num_rows($res), $secondsBack, $email));
2271  $this->db->sql_free_result($res);
2272  }
2273  }
2274  }
2275 
2282  public static function getCookieName()
2283  {
2284  $configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['cookieName']);
2285  if (empty($configuredCookieName)) {
2286  $configuredCookieName = 'be_typo_user';
2287  }
2288  return $configuredCookieName;
2289  }
2290 
2298  public function checkLockToIP()
2299  {
2300  $out = 1;
2301  if ($GLOBALS['TYPO3_CONF_VARS']['BE']['enabledBeUserIPLock']) {
2302  $IPList = $this->getTSConfigVal('options.lockToIP');
2303  if (trim($IPList)) {
2304  $baseIP = GeneralUtility::getIndpEnv('REMOTE_ADDR');
2305  $out = GeneralUtility::cmpIP($baseIP, $IPList);
2306  }
2307  }
2308  return $out;
2309  }
2310 
2322  public function backendCheckLogin($proceedIfNoUserIsLoggedIn = false)
2323  {
2324  if (empty($this->user['uid'])) {
2325  if ($proceedIfNoUserIsLoggedIn === false) {
2326  $url = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir;
2327  \TYPO3\CMS\Core\Utility\HttpUtility::redirect($url);
2328  }
2329  } else {
2330  // ...and if that's the case, call these functions
2331  $this->fetchGroupData();
2332  // The groups are fetched and ready for permission checking in this initialization.
2333  // Tables.php must be read before this because stuff like the modules has impact in this
2334  if ($this->checkLockToIP()) {
2335  if ($this->isUserAllowedToLogin()) {
2336  // Setting the UC array. It's needed with fetchGroupData first, due to default/overriding of values.
2337  $this->backendSetUC();
2338  // Email at login - if option set.
2339  $this->emailAtLogin();
2340  } else {
2341  throw new \RuntimeException('Login Error: TYPO3 is in maintenance mode at the moment. Only administrators are allowed access.', 1294585860);
2342  }
2343  } else {
2344  throw new \RuntimeException('Login Error: IP locking prevented you from being authorized. Can\'t proceed, sorry.', 1294585861);
2345  }
2346  }
2347  }
2348 
2355  public function checkCLIuser()
2356  {
2358  // First, check if cliMode is enabled:
2359  if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) {
2360  if (!$this->user['uid']) {
2361  if (substr($GLOBALS['MCONF']['name'], 0, 5) == '_CLI_') {
2362  $userName = strtolower($GLOBALS['MCONF']['name']);
2363  $this->setBeUserByName($userName);
2364  if ($this->user['uid']) {
2365  if (!$this->isAdmin()) {
2366  return true;
2367  } else {
2368  fwrite(STDERR, 'ERROR: CLI backend user "' . $userName . '" was ADMIN which is not allowed!' . LF . LF);
2369  die(3);
2370  }
2371  } else {
2372  fwrite(STDERR, 'ERROR: No backend user named "' . $userName . '" was found!' . LF . LF);
2373  die(3);
2374  }
2375  } else {
2376  fwrite(STDERR, 'ERROR: Module name, "' . $GLOBALS['MCONF']['name'] . '", was not prefixed with "_CLI_"' . LF . LF);
2377  die(3);
2378  }
2379  } else {
2380  fwrite(STDERR, 'ERROR: Another user was already loaded which is impossible in CLI mode!' . LF . LF);
2381  die(3);
2382  }
2383  }
2384  return false;
2385  }
2386 
2394  public function backendSetUC()
2395  {
2396  // UC - user configuration is a serialized array inside the user object
2397  // If there is a saved uc we implement that instead of the default one.
2398  $temp_theSavedUC = unserialize($this->user['uc']);
2399  if (is_array($temp_theSavedUC)) {
2400  $this->unpack_uc($temp_theSavedUC);
2401  }
2402  // Setting defaults if uc is empty
2403  $updated = false;
2404  $originalUc = array();
2405  if (is_array($this->uc) && isset($this->uc['ucSetByInstallTool'])) {
2406  $originalUc = $this->uc;
2407  unset($originalUc['ucSetByInstallTool'], $this->uc);
2408  }
2409  if (!is_array($this->uc)) {
2410  $this->uc = array_merge(
2411  $this->uc_default,
2412  (array)$GLOBALS['TYPO3_CONF_VARS']['BE']['defaultUC'],
2413  GeneralUtility::removeDotsFromTS((array)$this->getTSConfigProp('setup.default')),
2414  $originalUc
2415  );
2416  $this->overrideUC();
2417  $updated = true;
2418  }
2419  // If TSconfig is updated, update the defaultUC.
2420  if ($this->userTSUpdated) {
2421  $this->overrideUC();
2422  $updated = true;
2423  }
2424  // Setting default lang from be_user record.
2425  if (!isset($this->uc['lang'])) {
2426  $this->uc['lang'] = $this->user['lang'];
2427  $updated = true;
2428  }
2429  // Setting the time of the first login:
2430  if (!isset($this->uc['firstLoginTimeStamp'])) {
2431  $this->uc['firstLoginTimeStamp'] = $GLOBALS['EXEC_TIME'];
2432  $updated = true;
2433  }
2434  // Saving if updated.
2435  if ($updated) {
2436  $this->writeUC();
2437  }
2438  }
2439 
2447  public function overrideUC()
2448  {
2449  $this->uc = array_merge((array)$this->uc, (array)$this->getTSConfigProp('setup.override'));
2450  }
2451 
2458  public function resetUC()
2459  {
2460  $this->user['uc'] = '';
2461  $this->uc = '';
2462  $this->backendSetUC();
2463  }
2464 
2472  private function emailAtLogin()
2473  {
2474  if ($this->loginSessionStarted) {
2475  // Send notify-mail
2476  $subject = 'At "' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '"' . ' from '
2477  . GeneralUtility::getIndpEnv('REMOTE_ADDR')
2478  . (GeneralUtility::getIndpEnv('REMOTE_HOST') ? ' (' . GeneralUtility::getIndpEnv('REMOTE_HOST') . ')' : '');
2479  $msg = sprintf(
2480  'User "%s" logged in from %s (%s) at "%s" (%s)',
2481  $this->user['username'],
2482  GeneralUtility::getIndpEnv('REMOTE_ADDR'),
2483  GeneralUtility::getIndpEnv('REMOTE_HOST'),
2484  $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'],
2485  GeneralUtility::getIndpEnv('HTTP_HOST')
2486  );
2487  // Warning email address
2488  if ($GLOBALS['TYPO3_CONF_VARS']['BE']['warning_email_addr']) {
2489  $warn = 0;
2490  $prefix = '';
2491  if ((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['warning_mode'] & 1) {
2492  // first bit: All logins
2493  $warn = 1;
2494  $prefix = $this->isAdmin() ? '[AdminLoginWarning]' : '[LoginWarning]';
2495  }
2496  if ($this->isAdmin() && (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['warning_mode'] & 2) {
2497  // second bit: Only admin-logins
2498  $warn = 1;
2499  $prefix = '[AdminLoginWarning]';
2500  }
2501  if ($warn) {
2502  $from = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFrom();
2504  $mail = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class);
2505  $mail->setTo($GLOBALS['TYPO3_CONF_VARS']['BE']['warning_email_addr'])->setFrom($from)->setSubject($prefix . ' ' . $subject)->setBody($msg);
2506  $mail->send();
2507  }
2508  }
2509  // If An email should be sent to the current user, do that:
2510  if ($this->uc['emailMeAtLogin'] && strstr($this->user['email'], '@')) {
2511  $from = \TYPO3\CMS\Core\Utility\MailUtility::getSystemFrom();
2513  $mail = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class);
2514  $mail->setTo($this->user['email'])->setFrom($from)->setSubject($subject)->setBody($msg);
2515  $mail->send();
2516  }
2517  }
2518  }
2519 
2531  protected function isUserAllowedToLogin()
2532  {
2533  $isUserAllowedToLogin = false;
2534  $adminOnlyMode = $GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'];
2535  // Backend user is allowed if adminOnly is not set or user is an admin:
2536  if (!$adminOnlyMode || $this->isAdmin()) {
2537  $isUserAllowedToLogin = true;
2538  } elseif ($adminOnlyMode == 2 && TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) {
2539  $isUserAllowedToLogin = true;
2540  } elseif ($this->user['ses_backuserid']) {
2541  $backendUserId = (int)$this->user['ses_backuserid'];
2542  $whereAdmin = 'uid=' . $backendUserId . ' AND admin=1' . BackendUtility::BEenableFields('be_users');
2543  if ($this->db->exec_SELECTcountRows('uid', 'be_users', $whereAdmin) > 0) {
2544  $isUserAllowedToLogin = true;
2545  }
2546  }
2547  return $isUserAllowedToLogin;
2548  }
2549 
2553  public function logoff()
2554  {
2555  if (isset($GLOBALS['BE_USER']) && $GLOBALS['BE_USER'] instanceof BackendUserAuthentication && isset($GLOBALS['BE_USER']->user['uid'])) {
2556  \TYPO3\CMS\Core\FormProtection\FormProtectionFactory::get()->clean();
2557  }
2558  parent::logoff();
2559  }
2560 }