TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
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 
30 
39 {
44  public $usergroup_column = 'usergroup';
45 
50  public $usergroup_table = 'be_groups';
51 
57  public $groupData = [
58  'filemounts' => []
59  ];
60 
65  public $userGroups = [];
66 
71  public $userGroupsUID = [];
72 
77  public $groupList = '';
78 
87  public $workspace = -99;
88 
93  public $workspaceRec = [];
94 
102  public $dataLists = [
103  'webmount_list' => '',
104  'filemount_list' => '',
105  'file_permissions' => '',
106  'modList' => '',
107  'tables_select' => '',
108  'tables_modify' => '',
109  'pagetypes_select' => '',
110  'non_exclude_fields' => '',
111  'explicit_allowdeny' => '',
112  'allowed_languages' => '',
113  'workspace_perms' => '',
114  'custom_options' => ''
115  ];
116 
121  public $includeGroupArray = [];
122 
127  public $TSdataArray = [];
128 
133  public $userTS_text = '';
134 
139  public $userTS = [];
140 
145  public $userTSUpdated = false;
146 
151  public $userTS_dontGetCached = false;
152 
157  public $errorMsg = '';
158 
164 
168  protected $fileStorages;
169 
173  protected $filePermissions;
174 
179  public $session_table = 'be_sessions';
180 
185  public $user_table = 'be_users';
186 
191  public $username_column = 'username';
192 
197  public $userident_column = 'password';
198 
203  public $userid_column = 'uid';
204 
208  public $lastLogin_column = 'lastlogin';
209 
213  public $enablecolumns = [
214  'rootLevel' => 1,
215  'deleted' => 'deleted',
216  'disabled' => 'disable',
217  'starttime' => 'starttime',
218  'endtime' => 'endtime'
219  ];
220 
225  public $formfield_uname = 'username';
226 
231  public $formfield_uident = 'userident';
232 
237  public $formfield_status = 'login_status';
238 
243  public $writeStdLog = true;
244 
249  public $writeAttemptLog = true;
250 
260  public $sessionTimeout = 6000;
261 
265  public $firstMainGroup = 0;
266 
271  public $uc;
272 
282  public $uc_default = [
283  'interfaceSetup' => '',
284  // serialized content that is used to store interface pane and menu positions. Set by the logout.php-script
285  'moduleData' => [],
286  // user-data for the modules
287  'thumbnailsByDefault' => 1,
288  'emailMeAtLogin' => 0,
289  'startModule' => 'help_AboutAboutmodules',
290  'titleLen' => 50,
291  'edit_RTE' => '1',
292  'edit_docModuleUpload' => '1',
293  'resizeTextareas' => 1,
294  'resizeTextareas_MaxHeight' => 500,
295  'resizeTextareas_Flexible' => 0
296  ];
297 
301  public function __construct()
302  {
303  parent::__construct();
304  $this->name = self::getCookieName();
305  $this->loginType = 'BE';
306  $this->warningEmail = $GLOBALS['TYPO3_CONF_VARS']['BE']['warning_email_addr'];
307  $this->lockIP = $GLOBALS['TYPO3_CONF_VARS']['BE']['lockIP'];
308  $this->sessionTimeout = (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['sessionTimeout'];
309  if (TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) {
310  $this->dontSetCookie = true;
311  }
312  }
313 
320  public function isAdmin()
321  {
322  return is_array($this->user) && ($this->user['admin'] & 1) == 1;
323  }
324 
333  public function isMemberOfGroup($groupId)
334  {
335  $groupId = (int)$groupId;
336  if ($this->groupList && $groupId) {
337  return GeneralUtility::inList($this->groupList, $groupId);
338  }
339  return false;
340  }
341 
357  public function doesUserHaveAccess($row, $perms)
358  {
359  $userPerms = $this->calcPerms($row);
360  return ($userPerms & $perms) == $perms;
361  }
362 
379  public function isInWebMount($id, $readPerms = '', $exitOnError = 0)
380  {
381  if (!$GLOBALS['TYPO3_CONF_VARS']['BE']['lockBeUserToDBmounts'] || $this->isAdmin()) {
382  return 1;
383  }
384  $id = (int)$id;
385  // Check if input id is an offline version page in which case we will map id to the online version:
386  $checkRec = BackendUtility::getRecord('pages', $id, 'pid,t3ver_oid');
387  if ($checkRec['pid'] == -1) {
388  $id = (int)$checkRec['t3ver_oid'];
389  }
390  if (!$readPerms) {
391  $readPerms = $this->getPagePermsClause(1);
392  }
393  if ($id > 0) {
394  $wM = $this->returnWebmounts();
395  $rL = BackendUtility::BEgetRootLine($id, ' AND ' . $readPerms);
396  foreach ($rL as $v) {
397  if ($v['uid'] && in_array($v['uid'], $wM)) {
398  return $v['uid'];
399  }
400  }
401  }
402  if ($exitOnError) {
403  throw new \RuntimeException('Access Error: This page is not within your DB-mounts', 1294586445);
404  }
405  return null;
406  }
407 
416  public function modAccess($conf, $exitOnError)
417  {
418  if (!BackendUtility::isModuleSetInTBE_MODULES($conf['name'])) {
419  if ($exitOnError) {
420  throw new \RuntimeException('Fatal Error: This module "' . $conf['name'] . '" is not enabled in TBE_MODULES', 1294586446);
421  }
422  return false;
423  }
424  // Workspaces check:
425  if (
426  !empty($conf['workspaces'])
427  && \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')
428  && ($this->workspace !== 0 || !GeneralUtility::inList($conf['workspaces'], 'online'))
429  && ($this->workspace !== -1 || !GeneralUtility::inList($conf['workspaces'], 'offline'))
430  && ($this->workspace <= 0 || !GeneralUtility::inList($conf['workspaces'], 'custom'))
431  ) {
432  if ($exitOnError) {
433  throw new \RuntimeException('Workspace Error: This module "' . $conf['name'] . '" is not available under the current workspace', 1294586447);
434  }
435  return false;
436  }
437  // Returns TRUE if conf[access] is not set at all or if the user is admin
438  if (!$conf['access'] || $this->isAdmin()) {
439  return true;
440  }
441  // If $conf['access'] is set but not with 'admin' then we return TRUE, if the module is found in the modList
442  $acs = false;
443  if (!strstr($conf['access'], 'admin') && $conf['name']) {
444  $acs = $this->check('modules', $conf['name']);
445  }
446  if (!$acs && $exitOnError) {
447  throw new \RuntimeException('Access Error: You don\'t have access to this module.', 1294586448);
448  } else {
449  return $acs;
450  }
451  }
452 
468  public function getPagePermsClause($perms)
469  {
470  if (is_array($this->user)) {
471  if ($this->isAdmin()) {
472  return ' 1=1';
473  }
474  // Make sure it's integer.
475  $perms = (int)$perms;
476  $expressionBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
477  ->getQueryBuilderForTable('pages')
478  ->expr();
479 
480  // User
481  $constraint = $expressionBuilder->orX(
482  $expressionBuilder->comparison(
483  $expressionBuilder->bitAnd('pages.perms_everybody', $perms),
485  $perms
486  ),
487  $expressionBuilder->andX(
488  $expressionBuilder->eq('pages.perms_userid', (int)$this->user['uid']),
489  $expressionBuilder->comparison(
490  $expressionBuilder->bitAnd('pages.perms_user', $perms),
492  $perms
493  )
494  )
495  );
496 
497  // Group (if any is set)
498  if ($this->groupList) {
499  $constraint->add(
500  $expressionBuilder->andX(
501  $expressionBuilder->in(
502  'pages.perms_groupid',
503  GeneralUtility::intExplode(',', $this->groupList)
504  ),
505  $expressionBuilder->comparison(
506  $expressionBuilder->bitAnd('pages.perms_group', $perms),
508  $perms
509  )
510  )
511  );
512  }
513 
514  $constraint = ' (' . (string)$constraint . ')';
515 
516  // ****************
517  // getPagePermsClause-HOOK
518  // ****************
519  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getPagePermsClause'])) {
520  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getPagePermsClause'] as $_funcRef) {
521  $_params = ['currentClause' => $constraint, 'perms' => $perms];
522  $constraint = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
523  }
524  }
525  return $constraint;
526  } else {
527  return ' 1=0';
528  }
529  }
530 
540  public function calcPerms($row)
541  {
542  // Return 31 for admin users.
543  if ($this->isAdmin()) {
544  return Permission::ALL;
545  }
546  // Return 0 if page is not within the allowed web mount
547  if (!$this->isInWebMount($row['uid'])) {
548  return Permission::NOTHING;
549  }
550  $out = Permission::NOTHING;
551  if (
552  isset($row['perms_userid']) && isset($row['perms_user']) && isset($row['perms_groupid'])
553  && isset($row['perms_group']) && isset($row['perms_everybody']) && isset($this->groupList)
554  ) {
555  if ($this->user['uid'] == $row['perms_userid']) {
556  $out |= $row['perms_user'];
557  }
558  if ($this->isMemberOfGroup($row['perms_groupid'])) {
559  $out |= $row['perms_group'];
560  }
561  $out |= $row['perms_everybody'];
562  }
563  // ****************
564  // CALCPERMS hook
565  // ****************
566  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['calcPerms'])) {
567  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['calcPerms'] as $_funcRef) {
568  $_params = [
569  'row' => $row,
570  'outputPermissions' => $out
571  ];
572  $out = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
573  }
574  }
575  return $out;
576  }
577 
583  public function isRTE()
584  {
585  return (bool)$this->uc['edit_RTE'];
586  }
587 
598  public function check($type, $value)
599  {
600  if (isset($this->groupData[$type])) {
601  if ($this->isAdmin() || GeneralUtility::inList($this->groupData[$type], $value)) {
602  return true;
603  }
604  }
605  return false;
606  }
607 
617  public function checkAuthMode($table, $field, $value, $authMode)
618  {
619  // Admin users can do anything:
620  if ($this->isAdmin()) {
621  return true;
622  }
623  // Allow all blank values:
624  if ((string)$value === '') {
625  return true;
626  }
627  // Certain characters are not allowed in the value
628  if (preg_match('/[:|,]/', $value)) {
629  return false;
630  }
631  // Initialize:
632  $testValue = $table . ':' . $field . ':' . $value;
633  $out = true;
634  // Checking value:
635  switch ((string)$authMode) {
636  case 'explicitAllow':
637  if (!GeneralUtility::inList($this->groupData['explicit_allowdeny'], ($testValue . ':ALLOW'))) {
638  $out = false;
639  }
640  break;
641  case 'explicitDeny':
642  if (GeneralUtility::inList($this->groupData['explicit_allowdeny'], $testValue . ':DENY')) {
643  $out = false;
644  }
645  break;
646  case 'individual':
647  if (is_array($GLOBALS['TCA'][$table]) && is_array($GLOBALS['TCA'][$table]['columns'][$field])) {
648  $items = $GLOBALS['TCA'][$table]['columns'][$field]['config']['items'];
649  if (is_array($items)) {
650  foreach ($items as $iCfg) {
651  if ((string)$iCfg[1] === (string)$value && $iCfg[4]) {
652  switch ((string)$iCfg[4]) {
653  case 'EXPL_ALLOW':
654  if (!GeneralUtility::inList($this->groupData['explicit_allowdeny'], ($testValue . ':ALLOW'))) {
655  $out = false;
656  }
657  break;
658  case 'EXPL_DENY':
659  if (GeneralUtility::inList($this->groupData['explicit_allowdeny'], $testValue . ':DENY')) {
660  $out = false;
661  }
662  break;
663  }
664  break;
665  }
666  }
667  }
668  }
669  break;
670  }
671  return $out;
672  }
673 
680  public function checkLanguageAccess($langValue)
681  {
682  // The users language list must be non-blank - otherwise all languages are allowed.
683  if (trim($this->groupData['allowed_languages']) !== '') {
684  $langValue = (int)$langValue;
685  // Language must either be explicitly allowed OR the lang Value be "-1" (all languages)
686  if ($langValue != -1 && !$this->check('allowed_languages', $langValue)) {
687  return false;
688  }
689  }
690  return true;
691  }
692 
700  public function checkFullLanguagesAccess($table, $record)
701  {
702  $recordLocalizationAccess = $this->checkLanguageAccess(0);
703  if ($recordLocalizationAccess && (BackendUtility::isTableLocalizable($table) || $table === 'pages')) {
704  if ($table === 'pages') {
705  $l10nTable = 'pages_language_overlay';
706  $pointerField = $GLOBALS['TCA'][$l10nTable]['ctrl']['transOrigPointerField'];
707  $pointerValue = $record['uid'];
708  } else {
709  $l10nTable = $table;
710  $pointerField = $GLOBALS['TCA'][$l10nTable]['ctrl']['transOrigPointerField'];
711  $pointerValue = $record[$pointerField] > 0 ? $record[$pointerField] : $record['uid'];
712  }
713  $recordLocalizations = BackendUtility::getRecordsByField($l10nTable, $pointerField, $pointerValue, '', '', '', '1');
714  if (is_array($recordLocalizations)) {
715  foreach ($recordLocalizations as $localization) {
716  $recordLocalizationAccess = $recordLocalizationAccess
717  && $this->checkLanguageAccess($localization[$GLOBALS['TCA'][$l10nTable]['ctrl']['languageField']]);
718  if (!$recordLocalizationAccess) {
719  break;
720  }
721  }
722  }
723  }
724  return $recordLocalizationAccess;
725  }
726 
742  public function recordEditAccessInternals($table, $idOrRow, $newRecord = false, $deletedRecord = false, $checkFullLanguageAccess = false)
743  {
744  if (!isset($GLOBALS['TCA'][$table])) {
745  return false;
746  }
747  // Always return TRUE for Admin users.
748  if ($this->isAdmin()) {
749  return true;
750  }
751  // Fetching the record if the $idOrRow variable was not an array on input:
752  if (!is_array($idOrRow)) {
753  if ($deletedRecord) {
754  $idOrRow = BackendUtility::getRecord($table, $idOrRow, '*', '', false);
755  } else {
756  $idOrRow = BackendUtility::getRecord($table, $idOrRow);
757  }
758  if (!is_array($idOrRow)) {
759  $this->errorMsg = 'ERROR: Record could not be fetched.';
760  return false;
761  }
762  }
763  // Checking languages:
764  if ($GLOBALS['TCA'][$table]['ctrl']['languageField']) {
765  // Language field must be found in input row - otherwise it does not make sense.
766  if (isset($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) {
767  if (!$this->checkLanguageAccess($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']])) {
768  $this->errorMsg = 'ERROR: Language was not allowed.';
769  return false;
770  } elseif (
771  $checkFullLanguageAccess && $idOrRow[$GLOBALS['TCA'][$table]['ctrl']['languageField']] == 0
772  && !$this->checkFullLanguagesAccess($table, $idOrRow)
773  ) {
774  $this->errorMsg = 'ERROR: Related/affected language was not allowed.';
775  return false;
776  }
777  } else {
778  $this->errorMsg = 'ERROR: The "languageField" field named "'
779  . $GLOBALS['TCA'][$table]['ctrl']['languageField'] . '" was not found in testing record!';
780  return false;
781  }
782  } elseif (
783  $table === 'pages' && $checkFullLanguageAccess &&
784  !$this->checkFullLanguagesAccess($table, $idOrRow)
785  ) {
786  return false;
787  }
788  // Checking authMode fields:
789  if (is_array($GLOBALS['TCA'][$table]['columns'])) {
790  foreach ($GLOBALS['TCA'][$table]['columns'] as $fieldName => $fieldValue) {
791  if (isset($idOrRow[$fieldName])) {
792  if (
793  $fieldValue['config']['type'] === 'select' && $fieldValue['config']['authMode']
794  && $fieldValue['config']['authMode_enforce'] === 'strict'
795  ) {
796  if (!$this->checkAuthMode($table, $fieldName, $idOrRow[$fieldName], $fieldValue['config']['authMode'])) {
797  $this->errorMsg = 'ERROR: authMode "' . $fieldValue['config']['authMode']
798  . '" failed for field "' . $fieldName . '" with value "'
799  . $idOrRow[$fieldName] . '" evaluated';
800  return false;
801  }
802  }
803  }
804  }
805  }
806  // Checking "editlock" feature (doesn't apply to new records)
807  if (!$newRecord && $GLOBALS['TCA'][$table]['ctrl']['editlock']) {
808  if (isset($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['editlock']])) {
809  if ($idOrRow[$GLOBALS['TCA'][$table]['ctrl']['editlock']]) {
810  $this->errorMsg = 'ERROR: Record was locked for editing. Only admin users can change this state.';
811  return false;
812  }
813  } else {
814  $this->errorMsg = 'ERROR: The "editLock" field named "' . $GLOBALS['TCA'][$table]['ctrl']['editlock']
815  . '" was not found in testing record!';
816  return false;
817  }
818  }
819  // Checking record permissions
820  // THIS is where we can include a check for "perms_" fields for other records than pages...
821  // Process any hooks
822  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['recordEditAccessInternals'])) {
823  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['recordEditAccessInternals'] as $funcRef) {
824  $params = [
825  'table' => $table,
826  'idOrRow' => $idOrRow,
827  'newRecord' => $newRecord
828  ];
829  if (!GeneralUtility::callUserFunction($funcRef, $params, $this)) {
830  return false;
831  }
832  }
833  }
834  // Finally, return TRUE if all is well.
835  return true;
836  }
837 
848  public function isPSet($compiledPermissions, $tableName, $actionType = '')
849  {
850  if ($this->isAdmin()) {
851  $result = true;
852  } elseif ($tableName == 'pages') {
853  switch ($actionType) {
854  case 'edit':
855  $result = ($compiledPermissions & Permission::PAGE_EDIT) !== 0;
856  break;
857  case 'new':
858  // Create new page OR page content
859  $result = ($compiledPermissions & Permission::PAGE_NEW + Permission::CONTENT_EDIT) !== 0;
860  break;
861  case 'delete':
862  $result = ($compiledPermissions & Permission::PAGE_DELETE) !== 0;
863  break;
864  case 'editcontent':
865  $result = ($compiledPermissions & Permission::CONTENT_EDIT) !== 0;
866  break;
867  default:
868  $result = false;
869  }
870  } else {
871  $result = ($compiledPermissions & Permission::CONTENT_EDIT) !== 0;
872  }
873  return $result;
874  }
875 
881  public function mayMakeShortcut()
882  {
883  return $this->getTSConfigVal('options.enableBookmarks')
884  && !$this->getTSConfigVal('options.mayNotCreateEditBookmarks');
885  }
886 
898  public function workspaceCannotEditRecord($table, $recData)
899  {
900  // Only test offline spaces:
901  if ($this->workspace !== 0) {
902  if (!is_array($recData)) {
903  $recData = BackendUtility::getRecord(
904  $table,
905  $recData,
906  'pid' . ($GLOBALS['TCA'][$table]['ctrl']['versioningWS'] ? ',t3ver_wsid,t3ver_stage' : '')
907  );
908  }
909  if (is_array($recData)) {
910  // We are testing a "version" (identified by a pid of -1): it can be edited provided
911  // that workspace matches and versioning is enabled for the table.
912  if ((int)$recData['pid'] === -1) {
913  // No versioning, basic error, inconsistency even! Such records should not have a pid of -1!
914  if (!$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
915  return 'Versioning disabled for table';
916  } elseif ((int)$recData['t3ver_wsid'] !== $this->workspace) {
917  // So does workspace match?
918  return 'Workspace ID of record didn\'t match current workspace';
919  } else {
920  // So is the user allowed to "use" the edit stage within the workspace?
921  return $this->workspaceCheckStageForCurrent(0)
922  ? false
923  : 'User\'s access level did not allow for editing';
924  }
925  } else {
926  // We are testing a "live" record:
927  // For "Live" records, check that PID for table allows editing
928  if ($res = $this->workspaceAllowLiveRecordsInPID($recData['pid'], $table)) {
929  // Live records are OK in this branch, but what about the stage of branch point, if any:
930  // OK
931  return $res > 0
932  ? false
933  : 'Stage for versioning root point and users access level did not allow for editing';
934  } else {
935  // If not offline and not in versionized branch, output error:
936  return 'Online record was not in versionized branch!';
937  }
938  }
939  } else {
940  return 'No record';
941  }
942  } else {
943  // OK because workspace is 0
944  return false;
945  }
946  }
947 
956  public function workspaceCannotEditOfflineVersion($table, $recData)
957  {
958  if ($GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
959  if (!is_array($recData)) {
960  $recData = BackendUtility::getRecord($table, $recData, 'uid,pid,t3ver_wsid,t3ver_stage');
961  }
962  if (is_array($recData)) {
963  if ((int)$recData['pid'] === -1) {
964  return $this->workspaceCannotEditRecord($table, $recData);
965  } else {
966  return 'Not an offline version';
967  }
968  } else {
969  return 'No record';
970  }
971  } else {
972  return 'Table does not support versioning.';
973  }
974  }
975 
987  public function workspaceAllowLiveRecordsInPID($pid, $table)
988  {
989  // Always for Live workspace AND if live-edit is enabled
990  // and tables are completely without versioning it is ok as well.
991  if (
992  $this->workspace === 0
993  || $this->workspaceRec['live_edit'] && !$GLOBALS['TCA'][$table]['ctrl']['versioningWS']
994  || $GLOBALS['TCA'][$table]['ctrl']['versioningWS_alwaysAllowLiveEdit']
995  ) {
996  // OK to create for this table.
997  return 2;
998  } else {
999  // If the answer is FALSE it means the only valid way to create or edit records in the PID is by versioning
1000  return false;
1001  }
1002  }
1003 
1011  public function workspaceCreateNewRecord($pid, $table)
1012  {
1013  if ($res = $this->workspaceAllowLiveRecordsInPID($pid, $table)) {
1014  // If LIVE records cannot be created in the current PID due to workspace restrictions, prepare creation of placeholder-record
1015  if ($res < 0) {
1016  // Stage for versioning root point and users access level did not allow for editing
1017  return false;
1018  }
1019  } elseif (!$GLOBALS['TCA'][$table]['ctrl']['versioningWS']) {
1020  // So, if no live records were allowed, we have to create a new version of this record:
1021  return false;
1022  }
1023  return true;
1024  }
1025 
1034  public function workspaceAllowAutoCreation($table, $id, $recpid)
1035  {
1036  // Auto-creation of version: In offline workspace, test if versioning is
1037  // enabled and look for workspace version of input record.
1038  // If there is no versionized record found we will create one and save to that.
1039  if (
1040  $this->workspace !== 0
1041  && $GLOBALS['TCA'][$table]['ctrl']['versioningWS'] && $recpid >= 0
1042  && !BackendUtility::getWorkspaceVersionOfRecord($this->workspace, $table, $id, 'uid')
1043  ) {
1044  // There must be no existing version of this record in workspace.
1045  return true;
1046  }
1047  return false;
1048  }
1049 
1059  public function workspaceCheckStageForCurrent($stage)
1060  {
1061  // Always allow for admins
1062  if ($this->isAdmin()) {
1063  return true;
1064  }
1065  if ($this->workspace !== 0 && \TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
1066  $stage = (int)$stage;
1067  $stat = $this->checkWorkspaceCurrent();
1068  // Check if custom staging is activated
1069  $workspaceRec = BackendUtility::getRecord('sys_workspace', $stat['uid']);
1070  if ($workspaceRec['custom_stages'] > 0 && $stage !== 0 && $stage !== -10) {
1071  // Get custom stage record
1072  $workspaceStageRec = BackendUtility::getRecord('sys_workspace_stage', $stage);
1073  // Check if the user is responsible for the current stage
1074  if (
1075  $stat['_ACCESS'] === 'owner'
1076  || $stat['_ACCESS'] === 'member'
1077  && GeneralUtility::inList($workspaceStageRec['responsible_persons'], 'be_users_' . $this->user['uid'])
1078  ) {
1079  return true;
1080  }
1081  // Check if the user is in a group which is responsible for the current stage
1082  foreach ($this->userGroupsUID as $groupUid) {
1083  if (
1084  $stat['_ACCESS'] === 'owner'
1085  || $stat['_ACCESS'] === 'member'
1086  && GeneralUtility::inList($workspaceStageRec['responsible_persons'], 'be_groups_' . $groupUid)
1087  ) {
1088  return true;
1089  }
1090  }
1091  } elseif ($stage == -10 || $stage == -20) {
1092  if ($stat['_ACCESS'] === 'owner') {
1093  return true;
1094  } else {
1095  return false;
1096  }
1097  } else {
1098  $memberStageLimit = $this->workspaceRec['review_stage_edit'] ? 1 : 0;
1099  if (
1100  $stat['_ACCESS'] === 'owner'
1101  || $stat['_ACCESS'] === 'reviewer' && $stage <= 1
1102  || $stat['_ACCESS'] === 'member' && $stage <= $memberStageLimit
1103  ) {
1104  return true;
1105  }
1106  }
1107  } else {
1108  // Always OK for live workspace.
1109  return true;
1110  }
1111  return false;
1112  }
1113 
1124  public function workspacePublishAccess($wsid)
1125  {
1126  if ($this->isAdmin()) {
1127  return true;
1128  }
1129  // If no access to workspace, of course you cannot publish!
1130  $retVal = false;
1131  $wsAccess = $this->checkWorkspace($wsid);
1132  if ($wsAccess) {
1133  switch ($wsAccess['uid']) {
1134  case 0:
1135  // Live workspace
1136  // If access to Live workspace, no problem.
1137  $retVal = true;
1138  break;
1139  default:
1140  // Custom workspace
1141  $retVal = $wsAccess['_ACCESS'] === 'owner' || $this->checkWorkspace(0) && !($wsAccess['publish_access'] & Permission::PAGE_EDIT);
1142  // Either be an adminuser OR have access to online
1143  // workspace which is OK as well as long as publishing
1144  // access is not limited by workspace option.
1145  }
1146  }
1147  return $retVal;
1148  }
1149 
1155  public function workspaceSwapAccess()
1156  {
1157  if ($this->workspace > 0 && (int)$this->workspaceRec['swap_modes'] === 2) {
1158  return false;
1159  } else {
1160  return true;
1161  }
1162  }
1163 
1172  public function getTSConfig($objectString, $config = '')
1173  {
1174  if (!is_array($config)) {
1175  // Getting Root-ts if not sent
1176  $config = $this->userTS;
1177  }
1178  $TSConf = ['value' => null, 'properties' => null];
1179  $parts = GeneralUtility::trimExplode('.', $objectString, true, 2);
1180  $key = $parts[0];
1181  if ($key !== '') {
1182  if (count($parts) > 1 && $parts[1] !== '') {
1183  // Go on, get the next level
1184  if (is_array($config[$key . '.'])) {
1185  $TSConf = $this->getTSConfig($parts[1], $config[$key . '.']);
1186  }
1187  } else {
1188  $TSConf['value'] = $config[$key];
1189  $TSConf['properties'] = $config[$key . '.'];
1190  }
1191  }
1192  return $TSConf;
1193  }
1194 
1202  public function getTSConfigVal($objectString)
1203  {
1204  $TSConf = $this->getTSConfig($objectString);
1205  return $TSConf['value'];
1206  }
1207 
1215  public function getTSConfigProp($objectString)
1216  {
1217  $TSConf = $this->getTSConfig($objectString);
1218  return $TSConf['properties'];
1219  }
1220 
1229  public function returnWebmounts()
1230  {
1231  return (string)$this->groupData['webmounts'] != '' ? explode(',', $this->groupData['webmounts']) : [];
1232  }
1233 
1241  public function setWebmounts(array $mountPointUids, $append = false)
1242  {
1243  if (empty($mountPointUids)) {
1244  return;
1245  }
1246  if ($append) {
1247  $currentWebMounts = GeneralUtility::intExplode(',', $this->groupData['webmounts']);
1248  $mountPointUids = array_merge($currentWebMounts, $mountPointUids);
1249  }
1250  $this->groupData['webmounts'] = implode(',', array_unique($mountPointUids));
1251  }
1252 
1261  public function jsConfirmation($bitmask)
1262  {
1263  try {
1264  $alertPopupsSetting = trim((string)$this->getTSConfig('options.alertPopups')['value']);
1265  $alertPopup = JsConfirmation::cast($alertPopupsSetting === '' ? null : (int)$alertPopupsSetting);
1266  } catch (InvalidEnumerationValueException $e) {
1267  $alertPopup = new JsConfirmation();
1268  }
1269 
1270  return JsConfirmation::cast($bitmask)->matches($alertPopup);
1271  }
1272 
1283  public function fetchGroupData()
1284  {
1285  if ($this->user['uid']) {
1286  // Get lists for the be_user record and set them as default/primary values.
1287  // Enabled Backend Modules
1288  $this->dataLists['modList'] = $this->user['userMods'];
1289  // Add Allowed Languages
1290  $this->dataLists['allowed_languages'] = $this->user['allowed_languages'];
1291  // Set user value for workspace permissions.
1292  $this->dataLists['workspace_perms'] = $this->user['workspace_perms'];
1293  // Database mountpoints
1294  $this->dataLists['webmount_list'] = $this->user['db_mountpoints'];
1295  // File mountpoints
1296  $this->dataLists['filemount_list'] = $this->user['file_mountpoints'];
1297  // Fileoperation permissions
1298  $this->dataLists['file_permissions'] = $this->user['file_permissions'];
1299  // Setting default User TSconfig:
1300  $this->TSdataArray[] = $this->addTScomment('From $GLOBALS["TYPO3_CONF_VARS"]["BE"]["defaultUserTSconfig"]:')
1301  . $GLOBALS['TYPO3_CONF_VARS']['BE']['defaultUserTSconfig'];
1302  // Default TSconfig for admin-users
1303  if ($this->isAdmin()) {
1304  $this->TSdataArray[] = $this->addTScomment('"admin" user presets:') . '
1305  admPanel.enable.all = 1
1306  ';
1307  if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('sys_note')) {
1308  $this->TSdataArray[] = '
1309  // Setting defaults for sys_note author / email...
1310  TCAdefaults.sys_note.author = ' . $this->user['realName'] . '
1311  TCAdefaults.sys_note.email = ' . $this->user['email'] . '
1312  ';
1313  }
1314  }
1315  // BE_GROUPS:
1316  // Get the groups...
1317  if (!empty($this->user[$this->usergroup_column])) {
1318  // Fetch groups will add a lot of information to the internal arrays: modules, accesslists, TSconfig etc.
1319  // Refer to fetchGroups() function.
1320  $this->fetchGroups($this->user[$this->usergroup_column]);
1321  }
1322 
1323  // Populating the $this->userGroupsUID -array with the groups in the order in which they were LAST included.!!
1324  $this->userGroupsUID = array_reverse(array_unique(array_reverse($this->includeGroupArray)));
1325  // Finally this is the list of group_uid's in the order they are parsed (including subgroups!)
1326  // and without duplicates (duplicates are presented with their last entrance in the list,
1327  // which thus reflects the order of the TypoScript in TSconfig)
1328  $this->groupList = implode(',', $this->userGroupsUID);
1329  $this->setCachedList($this->groupList);
1330 
1331  // Add the TSconfig for this specific user:
1332  $this->TSdataArray[] = $this->addTScomment('USER TSconfig field') . $this->user['TSconfig'];
1333  // Check include lines.
1334  $this->TSdataArray = \TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::checkIncludeLines_array($this->TSdataArray);
1335  // Imploding with "[global]" will make sure that non-ended confinements with braces are ignored.
1336  $this->userTS_text = implode(LF . '[GLOBAL]' . LF, $this->TSdataArray);
1337  if (!$this->userTS_dontGetCached) {
1338  // Perform TS-Config parsing with condition matching
1339  $parseObj = GeneralUtility::makeInstance(\TYPO3\CMS\Backend\Configuration\TsConfigParser::class);
1340  $res = $parseObj->parseTSconfig($this->userTS_text, 'userTS');
1341  if ($res) {
1342  $this->userTS = $res['TSconfig'];
1343  $this->userTSUpdated = (bool)$res['cached'];
1344  }
1345  } else {
1346  // Parsing the user TSconfig (or getting from cache)
1347  $hash = md5('userTS:' . $this->userTS_text);
1348  $cachedContent = BackendUtility::getHash($hash);
1349  if (is_array($cachedContent) && !$this->userTS_dontGetCached) {
1350  $this->userTS = $cachedContent;
1351  } else {
1352  $parseObj = GeneralUtility::makeInstance(\TYPO3\CMS\Core\TypoScript\Parser\TypoScriptParser::class);
1353  $parseObj->parse($this->userTS_text);
1354  $this->userTS = $parseObj->setup;
1355  BackendUtility::storeHash($hash, $this->userTS, 'BE_USER_TSconfig');
1356  // Update UC:
1357  $this->userTSUpdated = true;
1358  }
1359  }
1360  // Processing webmounts
1361  // Admin's always have the root mounted
1362  if ($this->isAdmin() && !$this->getTSConfigVal('options.dontMountAdminMounts')) {
1363  $this->dataLists['webmount_list'] = '0,' . $this->dataLists['webmount_list'];
1364  }
1365  // The lists are cleaned for duplicates
1366  $this->groupData['webmounts'] = GeneralUtility::uniqueList($this->dataLists['webmount_list']);
1367  $this->groupData['pagetypes_select'] = GeneralUtility::uniqueList($this->dataLists['pagetypes_select']);
1368  $this->groupData['tables_select'] = GeneralUtility::uniqueList($this->dataLists['tables_modify'] . ',' . $this->dataLists['tables_select']);
1369  $this->groupData['tables_modify'] = GeneralUtility::uniqueList($this->dataLists['tables_modify']);
1370  $this->groupData['non_exclude_fields'] = GeneralUtility::uniqueList($this->dataLists['non_exclude_fields']);
1371  $this->groupData['explicit_allowdeny'] = GeneralUtility::uniqueList($this->dataLists['explicit_allowdeny']);
1372  $this->groupData['allowed_languages'] = GeneralUtility::uniqueList($this->dataLists['allowed_languages']);
1373  $this->groupData['custom_options'] = GeneralUtility::uniqueList($this->dataLists['custom_options']);
1374  $this->groupData['modules'] = GeneralUtility::uniqueList($this->dataLists['modList']);
1375  $this->groupData['file_permissions'] = GeneralUtility::uniqueList($this->dataLists['file_permissions']);
1376  $this->groupData['workspace_perms'] = $this->dataLists['workspace_perms'];
1377 
1378  // Checking read access to webmounts:
1379  if (trim($this->groupData['webmounts']) !== '') {
1380  $webmounts = explode(',', $this->groupData['webmounts']);
1381  // Explode mounts
1382  // Selecting all webmounts with permission clause for reading
1383  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('pages');
1384  $queryBuilder->getRestrictions()
1385  ->removeAll()
1386  ->add(GeneralUtility::makeInstance(DeletedRestriction::class));
1387 
1388  $MProws = $queryBuilder->select('uid')
1389  ->from('pages')
1390  // @todo DOCTRINE: check how to make getPagePermsClause() portable
1391  ->where(
1392  $this->getPagePermsClause(1),
1393  $queryBuilder->expr()->in(
1394  'uid',
1395  $queryBuilder->createNamedParameter(
1396  GeneralUtility::intExplode(',', $this->groupData['webmounts']),
1397  Connection::PARAM_INT_ARRAY
1398  )
1399  )
1400  )
1401  ->execute()
1402  ->fetchAll();
1403  $MProws = array_column(($MProws ?: []), 'uid', 'uid');
1404  foreach ($webmounts as $idx => $mountPointUid) {
1405  // If the mount ID is NOT found among selected pages, unset it:
1406  if ($mountPointUid > 0 && !isset($MProws[$mountPointUid])) {
1407  unset($webmounts[$idx]);
1408  }
1409  }
1410  // Implode mounts in the end.
1411  $this->groupData['webmounts'] = implode(',', $webmounts);
1412  }
1413  // Setting up workspace situation (after webmounts are processed!):
1414  $this->workspaceInit();
1415  }
1416  }
1417 
1427  public function fetchGroups($grList, $idList = '')
1428  {
1429  // Fetching records of the groups in $grList (which are not blocked by lockedToDomain either):
1430  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable($this->usergroup_table);
1431  $expressionBuilder = $queryBuilder->expr();
1432  $constraints = $expressionBuilder->andX(
1433  $expressionBuilder->eq(
1434  'pid',
1435  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
1436  ),
1437  $expressionBuilder->in(
1438  'uid',
1439  $queryBuilder->createNamedParameter(
1440  GeneralUtility::intExplode(',', $grList),
1441  Connection::PARAM_INT_ARRAY
1442  )
1443  ),
1444  $expressionBuilder->orX(
1445  $expressionBuilder->eq('lockToDomain', $queryBuilder->quote('')),
1446  $expressionBuilder->isNull('lockToDomain'),
1447  $expressionBuilder->eq(
1448  'lockToDomain',
1449  $queryBuilder->createNamedParameter(GeneralUtility::getIndpEnv('HTTP_HOST'), \PDO::PARAM_STR)
1450  )
1451  )
1452  );
1453  // Hook for manipulation of the WHERE sql sentence which controls which BE-groups are included
1454  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroupQuery'])) {
1455  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroupQuery'] as $classRef) {
1456  $hookObj = GeneralUtility::getUserObj($classRef);
1457  if (method_exists($hookObj, 'fetchGroupQuery_processQuery')) {
1458  $constraints = $hookObj->fetchGroupQuery_processQuery($this, $grList, $idList, (string)$constraints);
1459  }
1460  }
1461  }
1462  $res = $queryBuilder->select('*')
1463  ->from($this->usergroup_table)
1464  ->where($constraints)
1465  ->execute();
1466  // The userGroups array is filled
1467  while ($row = $res->fetch(\PDO::FETCH_ASSOC)) {
1468  $this->userGroups[$row['uid']] = $row;
1469  }
1470  // Traversing records in the correct order
1471  foreach (explode(',', $grList) as $uid) {
1472  // Get row:
1473  $row = $this->userGroups[$uid];
1474  // Must be an array and $uid should not be in the idList, because then it is somewhere previously in the grouplist
1475  if (is_array($row) && !GeneralUtility::inList($idList, $uid)) {
1476  // Include sub groups
1477  if (trim($row['subgroup'])) {
1478  // Make integer list
1479  $theList = implode(',', GeneralUtility::intExplode(',', $row['subgroup']));
1480  // Call recursively, pass along list of already processed groups so they are not recursed again.
1481  $this->fetchGroups($theList, $idList . ',' . $uid);
1482  }
1483  // Add the group uid, current list, TSconfig to the internal arrays.
1484  $this->includeGroupArray[] = $uid;
1485  $this->TSdataArray[] = $this->addTScomment('Group "' . $row['title'] . '" [' . $row['uid'] . '] TSconfig field:') . $row['TSconfig'];
1486  // Mount group database-mounts
1487  if (($this->user['options'] & Permission::PAGE_SHOW) == 1) {
1488  $this->dataLists['webmount_list'] .= ',' . $row['db_mountpoints'];
1489  }
1490  // Mount group file-mounts
1491  if (($this->user['options'] & Permission::PAGE_EDIT) == 2) {
1492  $this->dataLists['filemount_list'] .= ',' . $row['file_mountpoints'];
1493  }
1494  // The lists are made: groupMods, tables_select, tables_modify, pagetypes_select, non_exclude_fields, explicit_allowdeny, allowed_languages, custom_options
1495  $this->dataLists['modList'] .= ',' . $row['groupMods'];
1496  $this->dataLists['tables_select'] .= ',' . $row['tables_select'];
1497  $this->dataLists['tables_modify'] .= ',' . $row['tables_modify'];
1498  $this->dataLists['pagetypes_select'] .= ',' . $row['pagetypes_select'];
1499  $this->dataLists['non_exclude_fields'] .= ',' . $row['non_exclude_fields'];
1500  $this->dataLists['explicit_allowdeny'] .= ',' . $row['explicit_allowdeny'];
1501  $this->dataLists['allowed_languages'] .= ',' . $row['allowed_languages'];
1502  $this->dataLists['custom_options'] .= ',' . $row['custom_options'];
1503  $this->dataLists['file_permissions'] .= ',' . $row['file_permissions'];
1504  // Setting workspace permissions:
1505  $this->dataLists['workspace_perms'] |= $row['workspace_perms'];
1506  // If this function is processing the users OWN group-list (not subgroups) AND
1507  // if the ->firstMainGroup is not set, then the ->firstMainGroup will be set.
1508  if ($idList === '' && !$this->firstMainGroup) {
1509  $this->firstMainGroup = $uid;
1510  }
1511  }
1512  }
1513  // HOOK: fetchGroups_postProcessing
1514  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroups_postProcessing'])) {
1515  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['fetchGroups_postProcessing'] as $_funcRef) {
1516  $_params = [];
1517  GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1518  }
1519  }
1520  }
1521 
1533  public function setCachedList($cList)
1534  {
1535  if ((string)$cList != (string)$this->user['usergroup_cached_list']) {
1536  GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('be_users')->update(
1537  'be_users',
1538  ['usergroup_cached_list' => $cList],
1539  ['uid' => (int)$this->user['uid']]
1540  );
1541  }
1542  }
1543 
1550  protected function initializeFileStorages()
1551  {
1552  $this->fileStorages = [];
1554  $storageRepository = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Resource\StorageRepository::class);
1555  // Admin users have all file storages visible, without any filters
1556  if ($this->isAdmin()) {
1557  $storageObjects = $storageRepository->findAll();
1558  foreach ($storageObjects as $storageObject) {
1559  $this->fileStorages[$storageObject->getUid()] = $storageObject;
1560  }
1561  } else {
1562  // Regular users only have storages that are defined in their filemounts
1563  // Permissions and file mounts for the storage are added in StoragePermissionAspect
1564  foreach ($this->getFileMountRecords() as $row) {
1565  if (!array_key_exists((int)$row['base'], $this->fileStorages)) {
1566  $storageObject = $storageRepository->findByUid($row['base']);
1567  if ($storageObject) {
1568  $this->fileStorages[$storageObject->getUid()] = $storageObject;
1569  }
1570  }
1571  }
1572  }
1573 
1574  // This has to be called always in order to set certain filters
1576  }
1577 
1584  public function getCategoryMountPoints()
1585  {
1586  $categoryMountPoints = '';
1587 
1588  // Category mounts of the groups
1589  if (is_array($this->userGroups)) {
1590  foreach ($this->userGroups as $group) {
1591  if ($group['category_perms']) {
1592  $categoryMountPoints .= ',' . $group['category_perms'];
1593  }
1594  }
1595  }
1596 
1597  // Category mounts of the user record
1598  if ($this->user['category_perms']) {
1599  $categoryMountPoints .= ',' . $this->user['category_perms'];
1600  }
1601 
1602  // Make the ids unique
1603  $categoryMountPoints = GeneralUtility::trimExplode(',', $categoryMountPoints);
1604  $categoryMountPoints = array_filter($categoryMountPoints); // remove empty value
1605  $categoryMountPoints = array_unique($categoryMountPoints); // remove unique value
1606 
1607  return $categoryMountPoints;
1608  }
1609 
1617  public function getFileMountRecords()
1618  {
1619  static $fileMountRecordCache = [];
1620 
1621  if (!empty($fileMountRecordCache)) {
1622  return $fileMountRecordCache;
1623  }
1624 
1625  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
1626 
1627  // Processing file mounts (both from the user and the groups)
1628  $fileMounts = array_unique(GeneralUtility::intExplode(',', $this->dataLists['filemount_list'], true));
1629 
1630  // Limit file mounts if set in workspace record
1631  if ($this->workspace > 0 && !empty($this->workspaceRec['file_mountpoints'])) {
1632  $workspaceFileMounts = GeneralUtility::intExplode(',', $this->workspaceRec['file_mountpoints'], true);
1633  $fileMounts = array_intersect($fileMounts, $workspaceFileMounts);
1634  }
1635 
1636  if (!empty($fileMounts)) {
1637  $orderBy = $GLOBALS['TCA']['sys_filemounts']['ctrl']['default_sortby'] ?? 'sorting';
1638 
1639  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_filemounts');
1640  $queryBuilder->getRestrictions()
1641  ->removeAll()
1642  ->add(GeneralUtility::makeInstance(DeletedRestriction::class))
1643  ->add(GeneralUtility::makeInstance(HiddenRestriction::class))
1644  ->add(GeneralUtility::makeInstance(RootLevelRestriction::class));
1645 
1646  $queryBuilder->select('*')
1647  ->from('sys_filemounts')
1648  ->where(
1649  $queryBuilder->expr()->in('uid', $queryBuilder->createNamedParameter($fileMounts, Connection::PARAM_INT_ARRAY))
1650  );
1651 
1652  foreach (QueryHelper::parseOrderBy($orderBy) as $fieldAndDirection) {
1653  $queryBuilder->addOrderBy(...$fieldAndDirection);
1654  }
1655 
1656  $fileMountRecords = $queryBuilder->execute()->fetchAll(\PDO::FETCH_ASSOC);
1657  if ($fileMountRecords !== false) {
1658  foreach ($fileMountRecords as $fileMount) {
1659  $fileMountRecordCache[$fileMount['base'] . $fileMount['path']] = $fileMount;
1660  }
1661  }
1662  }
1663 
1664  // Read-only file mounts
1665  $readOnlyMountPoints = trim($GLOBALS['BE_USER']->getTSConfigVal('options.folderTree.altElementBrowserMountPoints'));
1666  if ($readOnlyMountPoints) {
1667  // We cannot use the API here but need to fetch the default storage record directly
1668  // to not instantiate it (which directly applies mount points) before all mount points are resolved!
1669  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_file_storage');
1670  $defaultStorageRow = $queryBuilder->select('uid')
1671  ->from('sys_file_storage')
1672  ->where(
1673  $queryBuilder->expr()->eq('is_default', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT))
1674  )
1675  ->setMaxResults(1)
1676  ->execute()
1677  ->fetch(\PDO::FETCH_ASSOC);
1678 
1679  $readOnlyMountPointArray = GeneralUtility::trimExplode(',', $readOnlyMountPoints);
1680  foreach ($readOnlyMountPointArray as $readOnlyMountPoint) {
1681  $readOnlyMountPointConfiguration = GeneralUtility::trimExplode(':', $readOnlyMountPoint);
1682  if (count($readOnlyMountPointConfiguration) === 2) {
1683  // A storage is passed in the configuration
1684  $storageUid = (int)$readOnlyMountPointConfiguration[0];
1685  $path = $readOnlyMountPointConfiguration[1];
1686  } else {
1687  if (empty($defaultStorageRow)) {
1688  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);
1689  }
1690  // Backwards compatibility: If no storage is passed, we use the default storage
1691  $storageUid = $defaultStorageRow['uid'];
1692  $path = $readOnlyMountPointConfiguration[0];
1693  }
1694  $fileMountRecordCache[$storageUid . $path] = [
1695  'base' => $storageUid,
1696  'title' => $path,
1697  'path' => $path,
1698  'read_only' => true
1699  ];
1700  }
1701  }
1702 
1703  // Personal or Group filemounts are not accessible if file mount list is set in workspace record
1704  if ($this->workspace <= 0 || empty($this->workspaceRec['file_mountpoints'])) {
1705  // If userHomePath is set, we attempt to mount it
1706  if ($GLOBALS['TYPO3_CONF_VARS']['BE']['userHomePath']) {
1707  list($userHomeStorageUid, $userHomeFilter) = explode(':', $GLOBALS['TYPO3_CONF_VARS']['BE']['userHomePath'], 2);
1708  $userHomeStorageUid = (int)$userHomeStorageUid;
1709  $userHomeFilter = '/' . ltrim($userHomeFilter, '/');
1710  if ($userHomeStorageUid > 0) {
1711  // Try and mount with [uid]_[username]
1712  $path = $userHomeFilter . $this->user['uid'] . '_' . $this->user['username'] . $GLOBALS['TYPO3_CONF_VARS']['BE']['userUploadDir'];
1713  $fileMountRecordCache[$userHomeStorageUid . $path] = [
1714  'base' => $userHomeStorageUid,
1715  'title' => $this->user['username'],
1716  'path' => $path,
1717  'read_only' => false,
1718  'user_mount' => true
1719  ];
1720  // Try and mount with only [uid]
1721  $path = $userHomeFilter . $this->user['uid'] . $GLOBALS['TYPO3_CONF_VARS']['BE']['userUploadDir'];
1722  $fileMountRecordCache[$userHomeStorageUid . $path] = [
1723  'base' => $userHomeStorageUid,
1724  'title' => $this->user['username'],
1725  'path' => $path,
1726  'read_only' => false,
1727  'user_mount' => true
1728  ];
1729  }
1730  }
1731 
1732  // Mount group home-dirs
1733  if ((is_array($this->user) && $this->user['options'] & Permission::PAGE_EDIT) == 2 && $GLOBALS['TYPO3_CONF_VARS']['BE']['groupHomePath'] != '') {
1734  // If groupHomePath is set, we attempt to mount it
1735  list($groupHomeStorageUid, $groupHomeFilter) = explode(':', $GLOBALS['TYPO3_CONF_VARS']['BE']['groupHomePath'], 2);
1736  $groupHomeStorageUid = (int)$groupHomeStorageUid;
1737  $groupHomeFilter = '/' . ltrim($groupHomeFilter, '/');
1738  if ($groupHomeStorageUid > 0) {
1739  foreach ($this->userGroups as $groupData) {
1740  $path = $groupHomeFilter . $groupData['uid'];
1741  $fileMountRecordCache[$groupHomeStorageUid . $path] = [
1742  'base' => $groupHomeStorageUid,
1743  'title' => $groupData['title'],
1744  'path' => $path,
1745  'read_only' => false,
1746  'user_mount' => true
1747  ];
1748  }
1749  }
1750  }
1751  }
1752 
1753  return $fileMountRecordCache;
1754  }
1755 
1764  public function getFileStorages()
1765  {
1766  // Initializing file mounts after the groups are fetched
1767  if ($this->fileStorages === null) {
1768  $this->initializeFileStorages();
1769  }
1770  return $this->fileStorages;
1771  }
1772 
1781  {
1782  // Add the option for also displaying the non-hidden files
1783  if ($this->uc['showHiddenFilesAndFolders']) {
1785  }
1786  }
1787 
1824  public function getFilePermissions()
1825  {
1826  if (!isset($this->filePermissions)) {
1827  $filePermissions = [
1828  // File permissions
1829  'addFile' => false,
1830  'readFile' => false,
1831  'writeFile' => false,
1832  'copyFile' => false,
1833  'moveFile' => false,
1834  'renameFile' => false,
1835  'deleteFile' => false,
1836  // Folder permissions
1837  'addFolder' => false,
1838  'readFolder' => false,
1839  'writeFolder' => false,
1840  'copyFolder' => false,
1841  'moveFolder' => false,
1842  'renameFolder' => false,
1843  'deleteFolder' => false,
1844  'recursivedeleteFolder' => false
1845  ];
1846  if ($this->isAdmin()) {
1847  $filePermissions = array_map('is_bool', $filePermissions);
1848  } else {
1849  $userGroupRecordPermissions = GeneralUtility::trimExplode(',', $this->groupData['file_permissions'], true);
1850  array_walk(
1851  $userGroupRecordPermissions,
1852  function ($permission) use (&$filePermissions) {
1853  $filePermissions[$permission] = true;
1854  }
1855  );
1856 
1857  // Finally overlay any userTSconfig
1858  $permissionsTsConfig = $this->getTSConfigProp('permissions.file.default');
1859  if (!empty($permissionsTsConfig)) {
1860  array_walk(
1861  $permissionsTsConfig,
1862  function ($value, $permission) use (&$filePermissions) {
1863  $filePermissions[$permission] = (bool)$value;
1864  }
1865  );
1866  }
1867  }
1868  $this->filePermissions = $filePermissions;
1869  }
1870  return $this->filePermissions;
1871  }
1872 
1883  public function getFilePermissionsForStorage(\TYPO3\CMS\Core\Resource\ResourceStorage $storageObject)
1884  {
1885  $finalUserPermissions = $this->getFilePermissions();
1886  if (!$this->isAdmin()) {
1887  $storageFilePermissions = $this->getTSConfigProp('permissions.file.storage.' . $storageObject->getUid());
1888  if (!empty($storageFilePermissions)) {
1889  array_walk(
1890  $storageFilePermissions,
1891  function ($value, $permission) use (&$finalUserPermissions) {
1892  $finalUserPermissions[$permission] = (bool)$value;
1893  }
1894  );
1895  }
1896  }
1897  return $finalUserPermissions;
1898  }
1899 
1917  public function getDefaultUploadFolder($pid = null, $table = null, $field = null)
1918  {
1919  $uploadFolder = $this->getTSConfigVal('options.defaultUploadFolder');
1920  if ($uploadFolder) {
1921  $uploadFolder = \TYPO3\CMS\Core\Resource\ResourceFactory::getInstance()->getFolderObjectFromCombinedIdentifier($uploadFolder);
1922  } else {
1923  foreach ($this->getFileStorages() as $storage) {
1924  if ($storage->isDefault() && $storage->isWritable()) {
1925  try {
1926  $uploadFolder = $storage->getDefaultFolder();
1927  if ($uploadFolder->checkActionPermission('write')) {
1928  break;
1929  }
1930  $uploadFolder = null;
1931  } catch (\TYPO3\CMS\Core\Resource\Exception $folderAccessException) {
1932  // If the folder is not accessible (no permissions / does not exist) we skip this one.
1933  }
1934  break;
1935  }
1936  }
1937  if (!$uploadFolder instanceof \TYPO3\CMS\Core\Resource\Folder) {
1939  foreach ($this->getFileStorages() as $storage) {
1940  if ($storage->isWritable()) {
1941  try {
1942  $uploadFolder = $storage->getDefaultFolder();
1943  if ($uploadFolder->checkActionPermission('write')) {
1944  break;
1945  }
1946  $uploadFolder = null;
1947  } catch (\TYPO3\CMS\Core\Resource\Exception $folderAccessException) {
1948  // If the folder is not accessible (no permissions / does not exist) try the next one.
1949  }
1950  }
1951  }
1952  }
1953  }
1954 
1955  // HOOK: getDefaultUploadFolder
1956  if (is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getDefaultUploadFolder'])) {
1957  foreach ($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauthgroup.php']['getDefaultUploadFolder'] as $_funcRef) {
1958  $_params = [
1959  'uploadFolder' => $uploadFolder,
1960  'pid' => $pid,
1961  'table' => $table,
1962  'field' => $field,
1963  ];
1964  $uploadFolder = GeneralUtility::callUserFunction($_funcRef, $_params, $this);
1965  }
1966  }
1967 
1968  if ($uploadFolder instanceof \TYPO3\CMS\Core\Resource\Folder) {
1969  return $uploadFolder;
1970  } else {
1971  return false;
1972  }
1973  }
1974 
1984  {
1985  $defaultTemporaryFolder = null;
1986  $defaultFolder = $this->getDefaultUploadFolder();
1987 
1988  if ($defaultFolder !== false) {
1989  $tempFolderName = '_temp_';
1990  $createFolder = !$defaultFolder->hasFolder($tempFolderName);
1991  if ($createFolder === true) {
1992  try {
1993  $defaultTemporaryFolder = $defaultFolder->createFolder($tempFolderName);
1994  } catch (\TYPO3\CMS\Core\Resource\Exception $folderAccessException) {
1995  }
1996  } else {
1997  $defaultTemporaryFolder = $defaultFolder->getSubfolder($tempFolderName);
1998  }
1999  }
2000 
2001  return $defaultTemporaryFolder;
2002  }
2003 
2010  public function addTScomment($str)
2011  {
2012  $delimiter = '# ***********************************************';
2013  $out = $delimiter . LF;
2014  $lines = GeneralUtility::trimExplode(LF, $str);
2015  foreach ($lines as $v) {
2016  $out .= '# ' . $v . LF;
2017  }
2018  $out .= $delimiter . LF;
2019  return $out;
2020  }
2021 
2029  public function workspaceInit()
2030  {
2031  // Initializing workspace by evaluating and setting the workspace, possibly updating it in the user record!
2032  $this->setWorkspace($this->user['workspace_id']);
2033  // Limiting the DB mountpoints if there any selected in the workspace record
2035  if ($allowed_languages = $this->getTSConfigVal('options.workspaces.allowed_languages.' . $this->workspace)) {
2036  $this->groupData['allowed_languages'] = $allowed_languages;
2037  $this->groupData['allowed_languages'] = GeneralUtility::uniqueList($this->groupData['allowed_languages']);
2038  }
2039  }
2040 
2047  {
2048  $dbMountpoints = trim($this->workspaceRec['db_mountpoints']);
2049  if ($this->workspace > 0 && $dbMountpoints != '') {
2050  $filteredDbMountpoints = [];
2051  // Notice: We cannot call $this->getPagePermsClause(1);
2052  // as usual because the group-list is not available at this point.
2053  // But bypassing is fine because all we want here is check if the
2054  // workspace mounts are inside the current webmounts rootline.
2055  // The actual permission checking on page level is done elsewhere
2056  // as usual anyway before the page tree is rendered.
2057  $readPerms = '1=1';
2058  // Traverse mount points of the
2059  $dbMountpoints = GeneralUtility::intExplode(',', $dbMountpoints);
2060  foreach ($dbMountpoints as $mpId) {
2061  if ($this->isInWebMount($mpId, $readPerms)) {
2062  $filteredDbMountpoints[] = $mpId;
2063  }
2064  }
2065  // Re-insert webmounts:
2066  $filteredDbMountpoints = array_unique($filteredDbMountpoints);
2067  $this->groupData['webmounts'] = implode(',', $filteredDbMountpoints);
2068  }
2069  }
2070 
2078  public function checkWorkspace($wsRec, $fields = 'uid,title,adminusers,members,reviewers,publish_access,stagechg_notification')
2079  {
2080  $retVal = false;
2081  // If not array, look up workspace record:
2082  if (!is_array($wsRec)) {
2083  switch ((string)$wsRec) {
2084  case '0':
2085  $wsRec = ['uid' => $wsRec];
2086  break;
2087  default:
2088  if (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
2089  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_workspace');
2090  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(RootLevelRestriction::class));
2091  $wsRec = $queryBuilder->select(...GeneralUtility::trimExplode(',', $fields))
2092  ->from('sys_workspace')
2093  ->where($queryBuilder->expr()->eq(
2094  'uid',
2095  $queryBuilder->createNamedParameter($wsRec, \PDO::PARAM_INT)
2096  ))
2097  ->orderBy('title')
2098  ->setMaxResults(1)
2099  ->execute()
2100  ->fetch(\PDO::FETCH_ASSOC);
2101  }
2102  }
2103  }
2104  // If wsRec is set to an array, evaluate it:
2105  if (is_array($wsRec)) {
2106  if ($this->isAdmin()) {
2107  return array_merge($wsRec, ['_ACCESS' => 'admin']);
2108  } else {
2109  switch ((string)$wsRec['uid']) {
2110  case '0':
2111  $retVal = $this->groupData['workspace_perms'] & Permission::PAGE_SHOW
2112  ? array_merge($wsRec, ['_ACCESS' => 'online'])
2113  : false;
2114  break;
2115  default:
2116  // Checking if the guy is admin:
2117  if (GeneralUtility::inList($wsRec['adminusers'], 'be_users_' . $this->user['uid'])) {
2118  return array_merge($wsRec, ['_ACCESS' => 'owner']);
2119  }
2120  // Checking if he is owner through a user group of his:
2121  foreach ($this->userGroupsUID as $groupUid) {
2122  if (GeneralUtility::inList($wsRec['adminusers'], 'be_groups_' . $groupUid)) {
2123  return array_merge($wsRec, ['_ACCESS' => 'owner']);
2124  }
2125  }
2126  // Checking if he is reviewer user:
2127  if (GeneralUtility::inList($wsRec['reviewers'], 'be_users_' . $this->user['uid'])) {
2128  return array_merge($wsRec, ['_ACCESS' => 'reviewer']);
2129  }
2130  // Checking if he is reviewer through a user group of his:
2131  foreach ($this->userGroupsUID as $groupUid) {
2132  if (GeneralUtility::inList($wsRec['reviewers'], 'be_groups_' . $groupUid)) {
2133  return array_merge($wsRec, ['_ACCESS' => 'reviewer']);
2134  }
2135  }
2136  // Checking if he is member as user:
2137  if (GeneralUtility::inList($wsRec['members'], 'be_users_' . $this->user['uid'])) {
2138  return array_merge($wsRec, ['_ACCESS' => 'member']);
2139  }
2140  // Checking if he is member through a user group of his:
2141  foreach ($this->userGroupsUID as $groupUid) {
2142  if (GeneralUtility::inList($wsRec['members'], 'be_groups_' . $groupUid)) {
2143  return array_merge($wsRec, ['_ACCESS' => 'member']);
2144  }
2145  }
2146  }
2147  }
2148  }
2149  return $retVal;
2150  }
2151 
2159  public function checkWorkspaceCurrent()
2160  {
2161  if (!isset($this->checkWorkspaceCurrent_cache)) {
2162  $this->checkWorkspaceCurrent_cache = $this->checkWorkspace($this->workspace);
2163  }
2165  }
2166 
2173  public function setWorkspace($workspaceId)
2174  {
2175  // Check workspace validity and if not found, revert to default workspace.
2176  if (!$this->setTemporaryWorkspace($workspaceId)) {
2177  $this->setDefaultWorkspace();
2178  }
2179  // Unset access cache:
2180  $this->checkWorkspaceCurrent_cache = null;
2181  // If ID is different from the stored one, change it:
2182  if ((int)$this->workspace !== (int)$this->user['workspace_id']) {
2183  $this->user['workspace_id'] = $this->workspace;
2184  GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('be_users')->update(
2185  'be_users',
2186  ['workspace_id' => $this->user['workspace_id']],
2187  ['uid' => (int)$this->user['uid']]
2188  );
2189  $this->simplelog('User changed workspace to "' . $this->workspace . '"');
2190  }
2191  }
2192 
2199  public function setTemporaryWorkspace($workspaceId)
2200  {
2201  $result = false;
2202  $workspaceRecord = $this->checkWorkspace($workspaceId, '*');
2203 
2204  if ($workspaceRecord) {
2205  $this->workspaceRec = $workspaceRecord;
2206  $this->workspace = (int)$workspaceId;
2207  $result = true;
2208  }
2209 
2210  return $result;
2211  }
2212 
2218  public function setDefaultWorkspace()
2219  {
2220  $this->workspace = (int)$this->getDefaultWorkspace();
2221  $this->workspaceRec = $this->checkWorkspace($this->workspace, '*');
2222  }
2223 
2230  public function setWorkspacePreview($previewState)
2231  {
2232  $this->user['workspace_preview'] = $previewState;
2233  GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('be_users')->update(
2234  'be_users',
2235  ['workspace_preview_id' => $this->user['workspace_preview']],
2236  ['uid' => (int)$this->user['uid']]
2237  );
2238  }
2239 
2247  public function getDefaultWorkspace()
2248  {
2249  $defaultWorkspace = -99;
2250  if (!\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces') || $this->checkWorkspace(0)) {
2251  // Check online
2252  $defaultWorkspace = 0;
2253  } elseif ($this->checkWorkspace(-1)) {
2254  // Check offline
2255  $defaultWorkspace = -1;
2256  } elseif (\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLoaded('workspaces')) {
2257  // Traverse custom workspaces:
2258  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('sys_workspace');
2259  $queryBuilder->getRestrictions()->add(GeneralUtility::makeInstance(RootLevelRestriction::class));
2260  $workspaces = $queryBuilder->select('uid', 'title', 'adminusers', 'reviewers')
2261  ->from('sys_workspace')
2262  ->orderBy('title')
2263  ->execute()
2264  ->fetchAll(\PDO::FETCH_ASSOC);
2265 
2266  if ($workspaces !== false) {
2267  foreach ($workspaces as $rec) {
2268  if ($this->checkWorkspace($rec)) {
2269  $defaultWorkspace = $rec['uid'];
2270  break;
2271  }
2272  }
2273  }
2274  }
2275  return $defaultWorkspace;
2276  }
2277 
2296  public function writelog($type, $action, $error, $details_nr, $details, $data, $tablename = '', $recuid = '', $recpid = '', $event_pid = -1, $NEWid = '', $userId = 0)
2297  {
2298  if (!$userId && !empty($this->user['uid'])) {
2299  $userId = $this->user['uid'];
2300  }
2301 
2302  if (!empty($this->user['ses_backuserid'])) {
2303  if (empty($data)) {
2304  $data = [];
2305  }
2306  $data['originalUser'] = $this->user['ses_backuserid'];
2307  }
2308 
2309  $fields = [
2310  'userid' => (int)$userId,
2311  'type' => (int)$type,
2312  'action' => (int)$action,
2313  'error' => (int)$error,
2314  'details_nr' => (int)$details_nr,
2315  'details' => $details,
2316  'log_data' => serialize($data),
2317  'tablename' => $tablename,
2318  'recuid' => (int)$recuid,
2319  'IP' => (string)GeneralUtility::getIndpEnv('REMOTE_ADDR'),
2320  'tstamp' => time(),
2321  'event_pid' => (int)$event_pid,
2322  'NEWid' => $NEWid,
2323  'workspace' => $this->workspace
2324  ];
2325 
2326  $connection = GeneralUtility::makeInstance(ConnectionPool::class)->getConnectionForTable('sys_log');
2327  $connection->insert(
2328  'sys_log',
2329  $fields,
2330  [
2331  \PDO::PARAM_INT,
2332  \PDO::PARAM_INT,
2333  \PDO::PARAM_INT,
2334  \PDO::PARAM_INT,
2335  \PDO::PARAM_INT,
2336  \PDO::PARAM_STR,
2337  \PDO::PARAM_STR,
2338  \PDO::PARAM_STR,
2339  \PDO::PARAM_INT,
2340  \PDO::PARAM_STR,
2341  \PDO::PARAM_INT,
2342  \PDO::PARAM_INT,
2343  \PDO::PARAM_STR,
2344  \PDO::PARAM_STR,
2345  ]
2346  );
2347 
2348  return (int)$connection->lastInsertId('sys_log');
2349  }
2350 
2359  public function simplelog($message, $extKey = '', $error = 0)
2360  {
2361  return $this->writelog(4, 0, $error, 0, ($extKey ? '[' . $extKey . '] ' : '') . $message, []);
2362  }
2363 
2376  public function checkLogFailures($email, $secondsBack = 3600, $max = 3)
2377  {
2378  if ($email) {
2379  $connectionPool = GeneralUtility::makeInstance(ConnectionPool::class);
2380 
2381  // Get last flag set in the log for sending
2382  $theTimeBack = $GLOBALS['EXEC_TIME'] - $secondsBack;
2383  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_log');
2384  $queryBuilder->select('tstamp')
2385  ->from('sys_log')
2386  ->where(
2387  $queryBuilder->expr()->eq(
2388  'type',
2389  $queryBuilder->createNamedParameter(255, \PDO::PARAM_INT)
2390  ),
2391  $queryBuilder->expr()->eq(
2392  'action',
2393  $queryBuilder->createNamedParameter(4, \PDO::PARAM_INT)
2394  ),
2395  $queryBuilder->expr()->gt(
2396  'tstamp',
2397  $queryBuilder->createNamedParameter($theTimeBack, \PDO::PARAM_INT)
2398  )
2399  )
2400  ->orderBy('tstamp', 'DESC')
2401  ->setMaxResults(1);
2402  if ($testRow = $queryBuilder->execute()->fetch(\PDO::FETCH_ASSOC)) {
2403  $theTimeBack = $testRow['tstamp'];
2404  }
2405 
2406  $queryBuilder = $connectionPool->getQueryBuilderForTable('sys_log');
2407  $result = $queryBuilder->select('*')
2408  ->from('sys_log')
2409  ->where(
2410  $queryBuilder->expr()->eq(
2411  'type',
2412  $queryBuilder->createNamedParameter(255, \PDO::PARAM_INT)
2413  ),
2414  $queryBuilder->expr()->eq(
2415  'action',
2416  $queryBuilder->createNamedParameter(3, \PDO::PARAM_INT)
2417  ),
2418  $queryBuilder->expr()->neq(
2419  'error',
2420  $queryBuilder->createNamedParameter(0, \PDO::PARAM_INT)
2421  ),
2422  $queryBuilder->expr()->gt(
2423  'tstamp',
2424  $queryBuilder->createNamedParameter($theTimeBack, \PDO::PARAM_INT)
2425  )
2426  )
2427  ->orderBy('tstamp')
2428  ->execute();
2429 
2430  // Check for more than $max number of error failures with the last period.
2431  if ($result->rowCount() > $max) {
2432  // OK, so there were more than the max allowed number of login failures - so we will send an email then.
2433  $subject = 'TYPO3 Login Failure Warning (at ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . ')';
2434  $email_body = 'There have been some attempts (' . $result->rowCount() . ') to login at the TYPO3
2435 site "' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '" (' . GeneralUtility::getIndpEnv('HTTP_HOST') . ').
2436 
2437 This is a dump of the failures:
2438 
2439 ';
2440  while ($row = $result->fetch(\PDO::FETCH_ASSOC)) {
2441  $theData = unserialize($row['log_data']);
2442  $email_body .= date(
2443  $GLOBALS['TYPO3_CONF_VARS']['SYS']['ddmmyy'] . ' ' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['hhmm'],
2444  $row['tstamp']
2445  ) . ': ' . @sprintf($row['details'], (string)$theData[0], (string)$theData[1], (string)$theData[2]);
2446  $email_body .= LF;
2447  }
2450  $mail = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class);
2451  $mail->setTo($email)->setFrom($from)->setSubject($subject)->setBody($email_body);
2452  $mail->send();
2453  // Logout written to log
2454  $this->writelog(255, 4, 0, 3, 'Failure warning (%s failures within %s seconds) sent by email to %s', [$result->rowCount(), $secondsBack, $email]);
2455  }
2456  }
2457  }
2458 
2465  public static function getCookieName()
2466  {
2467  $configuredCookieName = trim($GLOBALS['TYPO3_CONF_VARS']['BE']['cookieName']);
2468  if (empty($configuredCookieName)) {
2469  $configuredCookieName = 'be_typo_user';
2470  }
2471  return $configuredCookieName;
2472  }
2473 
2481  public function checkLockToIP()
2482  {
2483  $out = 1;
2484  if ($GLOBALS['TYPO3_CONF_VARS']['BE']['enabledBeUserIPLock']) {
2485  $IPList = $this->getTSConfigVal('options.lockToIP');
2486  if (trim($IPList)) {
2487  $baseIP = GeneralUtility::getIndpEnv('REMOTE_ADDR');
2488  $out = GeneralUtility::cmpIP($baseIP, $IPList);
2489  }
2490  }
2491  return $out;
2492  }
2493 
2504  public function backendCheckLogin($proceedIfNoUserIsLoggedIn = false)
2505  {
2506  if (empty($this->user['uid'])) {
2507  if ($proceedIfNoUserIsLoggedIn === false) {
2508  $url = GeneralUtility::getIndpEnv('TYPO3_SITE_URL') . TYPO3_mainDir;
2510  }
2511  } else {
2512  // ...and if that's the case, call these functions
2513  $this->fetchGroupData();
2514  // The groups are fetched and ready for permission checking in this initialization.
2515  // Tables.php must be read before this because stuff like the modules has impact in this
2516  if ($this->checkLockToIP()) {
2517  if ($this->isUserAllowedToLogin()) {
2518  // Setting the UC array. It's needed with fetchGroupData first, due to default/overriding of values.
2519  $this->backendSetUC();
2520  // Email at login - if option set.
2521  $this->emailAtLogin();
2522  } else {
2523  throw new \RuntimeException('Login Error: TYPO3 is in maintenance mode at the moment. Only administrators are allowed access.', 1294585860);
2524  }
2525  } else {
2526  throw new \RuntimeException('Login Error: IP locking prevented you from being authorized. Can\'t proceed, sorry.', 1294585861);
2527  }
2528  }
2529  }
2530 
2538  public function backendSetUC()
2539  {
2540  // UC - user configuration is a serialized array inside the user object
2541  // If there is a saved uc we implement that instead of the default one.
2542  $this->unpack_uc();
2543  // Setting defaults if uc is empty
2544  $updated = false;
2545  $originalUc = [];
2546  if (is_array($this->uc) && isset($this->uc['ucSetByInstallTool'])) {
2547  $originalUc = $this->uc;
2548  unset($originalUc['ucSetByInstallTool'], $this->uc);
2549  }
2550  if (!is_array($this->uc)) {
2551  $this->uc = array_merge(
2552  $this->uc_default,
2553  (array)$GLOBALS['TYPO3_CONF_VARS']['BE']['defaultUC'],
2554  GeneralUtility::removeDotsFromTS((array)$this->getTSConfigProp('setup.default')),
2555  $originalUc
2556  );
2557  $this->overrideUC();
2558  $updated = true;
2559  }
2560  // If TSconfig is updated, update the defaultUC.
2561  if ($this->userTSUpdated) {
2562  $this->overrideUC();
2563  $updated = true;
2564  }
2565  // Setting default lang from be_user record.
2566  if (!isset($this->uc['lang'])) {
2567  $this->uc['lang'] = $this->user['lang'];
2568  $updated = true;
2569  }
2570  // Setting the time of the first login:
2571  if (!isset($this->uc['firstLoginTimeStamp'])) {
2572  $this->uc['firstLoginTimeStamp'] = $GLOBALS['EXEC_TIME'];
2573  $updated = true;
2574  }
2575  // Saving if updated.
2576  if ($updated) {
2577  $this->writeUC();
2578  }
2579  }
2580 
2588  public function overrideUC()
2589  {
2590  $this->uc = array_merge((array)$this->uc, (array)$this->getTSConfigProp('setup.override'));
2591  }
2592 
2599  public function resetUC()
2600  {
2601  $this->user['uc'] = '';
2602  $this->uc = '';
2603  $this->backendSetUC();
2604  }
2605 
2613  private function emailAtLogin()
2614  {
2615  if ($this->loginSessionStarted) {
2616  // Send notify-mail
2617  $subject = 'At "' . $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'] . '"' . ' from '
2618  . GeneralUtility::getIndpEnv('REMOTE_ADDR')
2619  . (GeneralUtility::getIndpEnv('REMOTE_HOST') ? ' (' . GeneralUtility::getIndpEnv('REMOTE_HOST') . ')' : '');
2620  $msg = sprintf(
2621  'User "%s" logged in from %s (%s) at "%s" (%s)',
2622  $this->user['username'],
2623  GeneralUtility::getIndpEnv('REMOTE_ADDR'),
2624  GeneralUtility::getIndpEnv('REMOTE_HOST'),
2625  $GLOBALS['TYPO3_CONF_VARS']['SYS']['sitename'],
2626  GeneralUtility::getIndpEnv('HTTP_HOST')
2627  );
2628  // Warning email address
2629  if ($GLOBALS['TYPO3_CONF_VARS']['BE']['warning_email_addr']) {
2630  $warn = 0;
2631  $prefix = '';
2632  if ((int)$GLOBALS['TYPO3_CONF_VARS']['BE']['warning_mode'] & 1) {
2633  // first bit: All logins
2634  $warn = 1;
2635  $prefix = $this->isAdmin() ? '[AdminLoginWarning]' : '[LoginWarning]';
2636  }
2637  if ($this->isAdmin() && (int)$GLOBALS['TYPO3_CONF_VARS']['BE']['warning_mode'] & 2) {
2638  // second bit: Only admin-logins
2639  $warn = 1;
2640  $prefix = '[AdminLoginWarning]';
2641  }
2642  if ($warn) {
2645  $mail = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class);
2646  $mail->setTo($GLOBALS['TYPO3_CONF_VARS']['BE']['warning_email_addr'])->setFrom($from)->setSubject($prefix . ' ' . $subject)->setBody($msg);
2647  $mail->send();
2648  }
2649  }
2650  // If An email should be sent to the current user, do that:
2651  if ($this->uc['emailMeAtLogin'] && strstr($this->user['email'], '@')) {
2654  $mail = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Mail\MailMessage::class);
2655  $mail->setTo($this->user['email'])->setFrom($from)->setSubject($subject)->setBody($msg);
2656  $mail->send();
2657  }
2658  }
2659  }
2660 
2672  protected function isUserAllowedToLogin()
2673  {
2674  $isUserAllowedToLogin = false;
2675  $adminOnlyMode = $GLOBALS['TYPO3_CONF_VARS']['BE']['adminOnly'];
2676  // Backend user is allowed if adminOnly is not set or user is an admin:
2677  if (!$adminOnlyMode || $this->isAdmin()) {
2678  $isUserAllowedToLogin = true;
2679  } elseif ($adminOnlyMode == 2 && TYPO3_REQUESTTYPE & TYPO3_REQUESTTYPE_CLI) {
2680  $isUserAllowedToLogin = true;
2681  } elseif ($this->user['ses_backuserid']) {
2682  $backendUserId = (int)$this->user['ses_backuserid'];
2683  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)->getQueryBuilderForTable('be_users');
2684  $isUserAllowedToLogin = (bool)$queryBuilder->count('uid')
2685  ->from('be_users')
2686  ->where(
2687  $queryBuilder->expr()->eq(
2688  'uid',
2689  $queryBuilder->createNamedParameter($backendUserId, \PDO::PARAM_INT)
2690  ),
2691  $queryBuilder->expr()->eq('admin', $queryBuilder->createNamedParameter(1, \PDO::PARAM_INT))
2692  )
2693  ->execute()
2694  ->fetchColumn(0);
2695  }
2696  return $isUserAllowedToLogin;
2697  }
2698 
2702  public function logoff()
2703  {
2704  if (isset($GLOBALS['BE_USER']) && $GLOBALS['BE_USER'] instanceof self && isset($GLOBALS['BE_USER']->user['uid'])) {
2706  }
2707  parent::logoff();
2708  }
2709 }
getFilePermissionsForStorage(\TYPO3\CMS\Core\Resource\ResourceStorage $storageObject)
static redirect($url, $httpStatus=self::HTTP_STATUS_303)
Definition: HttpUtility.php:76
static trimExplode($delim, $string, $removeEmptyValues=false, $limit=0)
checkWorkspace($wsRec, $fields= 'uid, title, adminusers, members, reviewers, publish_access, stagechg_notification')
static get($classNameOrType= 'default',...$constructorArguments)
recordEditAccessInternals($table, $idOrRow, $newRecord=false, $deletedRecord=false, $checkFullLanguageAccess=false)
static BEgetRootLine($uid, $clause= '', $workspaceOL=false)
static getRecord($table, $uid, $fields= '*', $where= '', $useDeleteClause=true)
isPSet($compiledPermissions, $tableName, $actionType= '')
static uniqueList($in_list, $secondParameter=null)
static getRecordsByField($theTable, $theField, $theValue, $whereClause= '', $groupBy= '', $orderBy= '', $limit= '', $useDeleteClause=true, $queryBuilder=null)
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static makeInstance($className,...$constructorArguments)
static getWorkspaceVersionOfRecord($workspace, $table, $uid, $fields= '*')
static setShowHiddenFilesAndFolders($showHiddenFilesAndFolders)
writelog($type, $action, $error, $details_nr, $details, $data, $tablename= '', $recuid= '', $recpid= '', $event_pid=-1, $NEWid= '', $userId=0)
static intExplode($delimiter, $string, $removeEmptyValues=false, $limit=0)
static callUserFunction($funcName, &$params, &$ref, $_= '', $errorMode=0)