|
TYPO3 API
SVNRelease
|
00001 <?php 00002 /*************************************************************** 00003 * Copyright notice 00004 * 00005 * (c) 2010-2011 Christian Kuhn <lolli@schwarzbu.ch> 00006 * Marcus Krause <marcus#exp2010@t3sec.info> 00007 * 00008 * All rights reserved 00009 * 00010 * This script is part of the TYPO3 project. The TYPO3 project is 00011 * free software; you can redistribute it and/or modify 00012 * it under the terms of the GNU General Public License as published by 00013 * the Free Software Foundation; either version 2 of the License, or 00014 * (at your option) any later version. 00015 * 00016 * The GNU General Public License can be found at 00017 * http://www.gnu.org/copyleft/gpl.html. 00018 * 00019 * This script is distributed in the hope that it will be useful, 00020 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00021 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00022 * GNU General Public License for more details. 00023 * 00024 * This copyright notice MUST APPEAR in all copies of the script! 00025 ***************************************************************/ 00026 00027 /** 00028 * Update plaintext and hashed passwords of existing users to salted passwords. 00029 * 00030 * @author Christian Kuhn <lolli@schwarzbu.ch> 00031 * @author Marcus Krause <marcus#exp2010@t3sec.info> 00032 * @package TYPO3 00033 * @subpackage saltedpasswords 00034 */ 00035 class tx_saltedpasswords_Tasks_BulkUpdate extends tx_scheduler_Task { 00036 /** 00037 * @var boolean Whether or not the task is allowed to deactivate itself after processing all existing user records. 00038 * @TODO: This could be set with an additional field later on. 00039 * The idea is to not disable the task after all initial users where handled. 00040 * This could be handy for example if new users are imported regularily from some external source. 00041 */ 00042 protected $canDeactivateSelf = TRUE; 00043 00044 /** 00045 * Converting a password to a salted hash takes some milliseconds (~100ms on an entry system in 2010). 00046 * If all users are updated in one run, the task might run a long time if a lot of users must be handled. 00047 * Therefore only a small number of frontend and backend users are processed. 00048 * If saltedpasswords is enabled for both frontend and backend 2 * numberOfRecords will be handled. 00049 * 00050 * @var integer Number of records 00051 * @TODO: This could be set with an additional field later on 00052 */ 00053 protected $numberOfRecords = 42; // 23 is too low ;) 00054 00055 /** 00056 * @var integer Pointer to last handled frontend and backend user row 00057 */ 00058 protected $userRecordPointer = array(); 00059 00060 /** 00061 * Constructor initializes user record pointer 00062 * 00063 * @return void 00064 */ 00065 public function __construct() { 00066 parent::__construct(); 00067 00068 $this->userRecordPointer = array( 00069 'FE' => 0, 00070 'BE' => 0, 00071 ); 00072 } 00073 00074 /** 00075 * Execute task 00076 * 00077 * @return void 00078 */ 00079 public function execute() { 00080 $processedAllRecords = TRUE; 00081 00082 // For frontend and backend 00083 foreach ($this->userRecordPointer as $mode => $pointer) { 00084 // If saltedpasswords is active for frontend / backend 00085 if (tx_saltedpasswords_div::isUsageEnabled($mode)) { 00086 $usersToUpdate = $this->findUsersToUpdate($mode); 00087 $numberOfRows = count($usersToUpdate); 00088 if ($numberOfRows > 0) { 00089 $processedAllRecords = FALSE; 00090 $this->incrementUserRecordPointer($mode, $numberOfRows); 00091 $this->convertPasswords($mode, $usersToUpdate); 00092 } 00093 } 00094 } 00095 00096 // Determine if task should disable itself 00097 if ($this->canDeactivateSelf && $processedAllRecords) { 00098 $this->deactivateSelf(); 00099 } 00100 00101 // Use save() of parent class tx_scheduler_Task to persist 00102 // changed task variables: $this->userRecordPointer and $this->disabled 00103 $this->save(); 00104 00105 return(TRUE); 00106 } 00107 00108 /** 00109 * Find next set of frontend or backend users to update. 00110 * 00111 * @param string 'FE' for frontend, 'BE' for backend user records 00112 * @return array Rows with uid and password 00113 */ 00114 protected function findUsersToUpdate($mode) { 00115 $usersToUpdate = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows( 00116 'uid, password', 00117 strtolower($mode) . '_users', 00118 '1 = 1', // retrieve and update all records (also disabled/deleted) for security reasons 00119 '', 00120 'uid ASC', 00121 $this->userRecordPointer[$mode] . ', ' . $this->numberOfRecords 00122 ); 00123 00124 return $usersToUpdate; 00125 } 00126 00127 /** 00128 * Iterate over given user records and update password if needed. 00129 * 00130 * @param string 'FE' for frontend, 'BE' for backend user records 00131 * @param array with user uids and passwords 00132 * @return void 00133 */ 00134 protected function convertPasswords($mode, $users) { 00135 $updateUsers = array(); 00136 foreach ($users as $user) { 00137 // If a password is already a salted hash it must not be updated 00138 if ($this->isSaltedHash($user['password'])) { 00139 continue; 00140 } 00141 00142 $updateUsers[] = $user; 00143 } 00144 00145 if (count($updateUsers) > 0) { 00146 $this->updatePasswords($mode, $updateUsers); 00147 } 00148 } 00149 00150 /** 00151 * Update password and persist salted hash. 00152 * 00153 * @param string 'FE' for frontend, 'BE' for backend user records 00154 * @param array with user uids and passwords 00155 * @return void 00156 */ 00157 protected function updatePasswords($mode, $users) { 00158 // Get a default saltedpasswords instance 00159 $saltedpasswordsInstance = tx_saltedpasswords_salts_factory::getSaltingInstance(NULL, $mode); 00160 00161 foreach ($users as $user) { 00162 $newPassword = $saltedpasswordsInstance->getHashedPassword($user['password']); 00163 00164 // If a given password is a md5 hash (usually default be_users without saltedpasswords activated), 00165 // result of getHasedPassword() is a salted hashed md5 hash. 00166 // We prefix those with 'M', saltedpasswords will then update this password 00167 // to a usual salted hash upon first login of the user. 00168 if ($this->isMd5Password($user['password'])) { 00169 $newPassword = 'M' . $newPassword; 00170 } 00171 00172 // Persist updated password 00173 $GLOBALS['TYPO3_DB']->exec_UPDATEquery( 00174 strtolower($mode) . '_users', 00175 'uid = ' . $user['uid'], 00176 array( 00177 'password' => $newPassword 00178 ) 00179 ); 00180 } 00181 } 00182 00183 /** 00184 * Passwords prefixed with M or C might be salted passwords: 00185 * M means: originally a md5 hash before it was salted (eg. default be_users). 00186 * C means: originally a cleartext password with lower hash looping count generated by t3sec_saltedpw. 00187 * Both M and C will be updated to usual salted hashes on first login of user. 00188 * 00189 * If a password does not start with M or C determine if a password is already a usual salted hash. 00190 * 00191 * @param string Password 00192 * @return boolean True if password is a salted hash 00193 */ 00194 protected function isSaltedHash($password) { 00195 $isSaltedHash = FALSE; 00196 if (strlen($password) > 2 && (t3lib_div::isFirstPartOfStr($password, 'C$') || t3lib_div::isFirstPartOfStr($password, 'M$'))) { 00197 // Cut off M or C and test if we have a salted hash 00198 $isSaltedHash = tx_saltedpasswords_salts_factory::determineSaltingHashingMethod(substr($password, 1)); 00199 } 00200 00201 // Test if given password is a already a usual salted hash 00202 if (!$isSaltedHash) { 00203 $isSaltedHash = tx_saltedpasswords_salts_factory::determineSaltingHashingMethod($password); 00204 } 00205 00206 return $isSaltedHash; 00207 } 00208 00209 /** 00210 * Check if a given password is a md5 hash, the default for be_user records before saltedpasswords. 00211 * 00212 * @return boolean TRUE if password is md5 00213 */ 00214 protected function isMd5Password($password) { 00215 return (bool) preg_match('/[0-9abcdef]{32,32}/i', $password); 00216 } 00217 00218 /** 00219 * Increment current user record counter by number of handled rows. 00220 * 00221 * @param string 'FE' for frontend, 'BE' for backend user records 00222 * @param integer Number of handled rows 00223 * @return void 00224 */ 00225 protected function incrementUserRecordPointer($mode, $number) { 00226 $this->userRecordPointer[$mode] += $number; 00227 } 00228 00229 /** 00230 * Deactivate this task instance. 00231 * Uses setDisabled() method of parent class tx_scheduler_Task. 00232 * 00233 * @return void 00234 */ 00235 protected function deactivateSelf() { 00236 $this->setDisabled(TRUE); 00237 } 00238 } // End of class 00239 00240 if (defined('TYPO3_MODE') && $GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/tasks/class.tx_saltedpasswords_tasks_bulkupdate.php']) { 00241 include_once($GLOBALS['TYPO3_CONF_VARS'][TYPO3_MODE]['XCLASS']['ext/saltedpasswords/classes/tasks/class.tx_saltedpasswords_tasks_bulkupdate.php']); 00242 } 00243 00244 ?>
1.8.0