TYPO3CMS  8
 All Classes Namespaces Files Functions Variables Pages
BulkUpdateTask.php
Go to the documentation of this file.
1 <?php
2 namespace TYPO3\CMS\Saltedpasswords\Task;
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 
137  protected function convertPasswords($mode, array $users)
138  {
139  $updateUsers = [];
140  foreach ($users as $user) {
141  // If a password is already a salted hash it must not be updated
142  if ($this->isSaltedHash($user['password'])) {
143  continue;
144  }
145  $updateUsers[] = $user;
146  }
147  if (!empty($updateUsers)) {
148  $this->updatePasswords($mode, $updateUsers);
149  }
150  }
151 
159  protected function updatePasswords($mode, array $users)
160  {
162  $saltedpasswordsInstance = \TYPO3\CMS\Saltedpasswords\Salt\SaltFactory::getSaltingInstance(null, $mode);
163  foreach ($users as $user) {
164  $newPassword = $saltedpasswordsInstance->getHashedPassword($user['password']);
165  // If a given password is a md5 hash (usually default be_users without saltedpasswords activated),
166  // result of getHashedPassword() is a salted hashed md5 hash.
167  // We prefix those with 'M', saltedpasswords will then update this password
168  // to a usual salted hash upon first login of the user.
169  if ($this->isMd5Password($user['password'])) {
170  $newPassword = 'M' . $newPassword;
171  }
172  // Persist updated password
173 
174  GeneralUtility::makeInstance(ConnectionPool::class)
175  ->getConnectionForTable($this->getTablename($mode))
176  ->update(
177  $this->getTablename($mode),
178  ['password' => $newPassword],
179  ['uid' => (int)$user['uid']]
180  );
181  }
182  }
183 
195  protected function isSaltedHash($password)
196  {
197  $isSaltedHash = false;
198  if (strlen($password) > 2 && (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($password, 'C$') || \TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($password, 'M$'))) {
199  // Cut off M or C and test if we have a salted hash
201  }
202  // Test if given password is already a usual salted hash
203  if (!$isSaltedHash) {
205  }
206  return $isSaltedHash;
207  }
208 
215  protected function isMd5Password($password)
216  {
217  return (bool)preg_match('/[0-9abcdef]{32,32}/i', $password);
218  }
219 
227  protected function incrementUserRecordPointer($mode, $number)
228  {
229  $this->userRecordPointer[$mode] += $number;
230  }
231 
238  protected function activateSelf()
239  {
240  $this->setDisabled(false);
241  }
242 
249  protected function deactivateSelf()
250  {
251  $this->setDisabled(true);
252  }
253 
261  {
262  $this->canDeactivateSelf = $canDeactivateSelf;
263  }
264 
270  public function getCanDeactivateSelf()
271  {
273  }
274 
282  {
283  $this->numberOfRecords = $numberOfRecords;
284  }
285 
291  public function getNumberOfRecords()
292  {
293  return $this->numberOfRecords;
294  }
295 
301  protected function getTablename(string $mode): string
302  {
303  $mode = strtolower($mode);
304  if ($mode === 'be') {
305  return 'be_users';
306  } elseif ($mode === 'fe') {
307  return 'fe_users';
308  } else {
309  throw new \InvalidArgumentException(
310  'Invalid mode "' . $mode . '" for salted passwords bulk update',
311  1465392861
312  );
313  }
314  }
315 }
static isFirstPartOfStr($str, $partStr)
static getSaltingInstance($saltedHash= '', $mode=TYPO3_MODE)
Definition: SaltFactory.php:83
if(TYPO3_MODE=== 'BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
static determineSaltingHashingMethod($saltedHash, $mode=TYPO3_MODE)
static makeInstance($className,...$constructorArguments)