TYPO3 CMS  TYPO3_8-7
BulkUpdateTask.php
Go to the documentation of this file.
1 <?php
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 
19 
24 {
28  protected $canDeactivateSelf = true;
29 
38  protected $numberOfRecords = 250;
39 
43  protected $userRecordPointer = [];
44 
48  public function __construct()
49  {
50  parent::__construct();
51  $this->userRecordPointer = [
52  'FE' => 0,
53  'BE' => 0
54  ];
55  }
56 
62  public function execute()
63  {
64  $processedAllRecords = true;
65  // For frontend and backend
66  foreach ($this->userRecordPointer as $mode => $pointer) {
67  // If saltedpasswords is active for frontend / backend
68  if (\TYPO3\CMS\Saltedpasswords\Utility\SaltedPasswordsUtility::isUsageEnabled($mode)) {
69  $usersToUpdate = $this->findUsersToUpdate($mode);
70  $numberOfRows = count($usersToUpdate);
71  if ($numberOfRows > 0) {
72  $processedAllRecords = false;
73  $this->activateSelf();
74  $this->incrementUserRecordPointer($mode, $numberOfRows);
75  $this->convertPasswords($mode, $usersToUpdate);
76  }
77  }
78  }
79  if ($processedAllRecords) {
80  // Reset the user record pointer
81  $this->userRecordPointer = [
82  'FE' => 0,
83  'BE' => 0
84  ];
85  // Determine if task should disable itself
86  if ($this->canDeactivateSelf) {
87  $this->deactivateSelf();
88  }
89  }
90  // Use save() of parent class \TYPO3\CMS\Scheduler\Task\AbstractTask to persist changed task variables
91  $this->save();
92  return true;
93  }
94 
100  public function getAdditionalInformation()
101  {
102  $information = $GLOBALS['LANG']->sL('LLL:EXT:saltedpasswords/Resources/Private/Language/locallang.xlf:ext.saltedpasswords.tasks.bulkupdate.label.additionalinformation.deactivateself') . $this->getCanDeactivateSelf() . '; ' . $GLOBALS['LANG']->sL('LLL:EXT:saltedpasswords/Resources/Private/Language/locallang.xlf:ext.saltedpasswords.tasks.bulkupdate.label.additionalinformation.numberofrecords') . $this->getNumberOfRecords();
103  return $information;
104  }
105 
112  protected function findUsersToUpdate($mode)
113  {
114  $queryBuilder = GeneralUtility::makeInstance(ConnectionPool::class)
115  ->getQueryBuilderForTable($this->getTablename($mode));
116  $queryBuilder->getRestrictions()->removeAll();
117 
118  $usersToUpdate = $queryBuilder
119  ->select('uid', 'password')
120  ->from($this->getTablename($mode))
121  ->orderBy('uid')
122  ->setFirstResult($this->userRecordPointer[$mode])
123  ->setMaxResults($this->numberOfRecords)
124  ->execute()
125  ->fetchAll();
126 
127  return $usersToUpdate;
128  }
129 
136  protected function convertPasswords($mode, array $users)
137  {
138  $updateUsers = [];
139  foreach ($users as $user) {
140  // If a password is already a salted hash it must not be updated
141  if ($this->isSaltedHash($user['password'])) {
142  continue;
143  }
144  $updateUsers[] = $user;
145  }
146  if (!empty($updateUsers)) {
147  $this->updatePasswords($mode, $updateUsers);
148  }
149  }
150 
157  protected function updatePasswords($mode, array $users)
158  {
160  $saltedpasswordsInstance = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance(null, $mode);
161  foreach ($users as $user) {
162  $newPassword = $saltedpasswordsInstance->getHashedPassword($user['password']);
163  // If a given password is a md5 hash (usually default be_users without saltedpasswords activated),
164  // result of getHashedPassword() is a salted hashed md5 hash.
165  // We prefix those with 'M', saltedpasswords will then update this password
166  // to a usual salted hash upon first login of the user.
167  if ($this->isMd5Password($user['password'])) {
168  $newPassword = 'M' . $newPassword;
169  }
170  // Persist updated password
171 
172  GeneralUtility::makeInstance(ConnectionPool::class)
173  ->getConnectionForTable($this->getTablename($mode))
174  ->update(
175  $this->getTablename($mode),
176  ['password' => $newPassword],
177  ['uid' => (int)$user['uid']]
178  );
179  }
180  }
181 
193  protected function isSaltedHash($password)
194  {
195  $isSaltedHash = false;
196  if (strlen($password) > 2 && (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($password, 'C$') || \TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($password, 'M$'))) {
197  // Cut off M or C and test if we have a salted hash
199  }
200  // Test if given password is already a usual salted hash
201  if (!$isSaltedHash) {
203  }
204  return $isSaltedHash;
205  }
206 
213  protected function isMd5Password($password)
214  {
215  return (bool)preg_match('/[0-9abcdef]{32,32}/i', $password);
216  }
217 
224  protected function incrementUserRecordPointer($mode, $number)
225  {
226  $this->userRecordPointer[$mode] += $number;
227  }
228 
233  protected function activateSelf()
234  {
235  $this->setDisabled(false);
236  }
237 
242  protected function deactivateSelf()
243  {
244  $this->setDisabled(true);
245  }
246 
253  {
254  $this->canDeactivateSelf = $canDeactivateSelf;
255  }
256 
262  public function getCanDeactivateSelf()
263  {
265  }
266 
273  {
274  $this->numberOfRecords = $numberOfRecords;
275  }
276 
282  public function getNumberOfRecords()
283  {
284  return $this->numberOfRecords;
285  }
286 
292  protected function getTablename(string $mode): string
293  {
294  $mode = strtolower($mode);
295  if ($mode === 'be') {
296  return 'be_users';
297  }
298  if ($mode === 'fe') {
299  return 'fe_users';
300  }
301  throw new \InvalidArgumentException(
302  'Invalid mode "' . $mode . '" for salted passwords bulk update',
303  1465392861
304  );
305  }
306 }
static determineSaltingHashingMethod($saltedHash, $mode=TYPO3_MODE)
static isFirstPartOfStr($str, $partStr)
static getSaltingInstance($saltedHash='', $mode=TYPO3_MODE)
Definition: SaltFactory.php:83
static makeInstance($className,... $constructorArguments)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']