[FEATURE] Make saltedpasswords conversion task options configurable
authorPhilipp Gampe <forge.typo3.org@philippgampe.info>
Tue, 21 Feb 2012 23:32:29 +0000 (00:32 +0100)
committerChristian Kuhn <lolli@schwarzbu.ch>
Sat, 30 Jun 2012 11:58:11 +0000 (13:58 +0200)
The saltedpassword password conversion task for the schedular features
two configuration option:
 * canDeactivateSelf to disable the task after all records are processed
 * numberOfRecords to define how many records should be processed at
   once
Make those two options configurable via the schedular task edit
interface.

Change-Id: I9b090fa534b14c0051cda51fa3faf4e610b3948d
Resolves: #33703
Releases: 6.0
Reviewed-on: http://review.typo3.org/9139
Reviewed-by: Wouter Wolters
Tested-by: Wouter Wolters
Reviewed-by: Christian Kuhn
Tested-by: Christian Kuhn
typo3/sysext/saltedpasswords/classes/tasks/class.tx_saltedpasswords_tasks_bulkupdate.php
typo3/sysext/saltedpasswords/classes/tasks/class.tx_saltedpasswords_tasks_bulkupdate_additionalfieldprovider.php [new file with mode: 0644]
typo3/sysext/saltedpasswords/ext_autoload.php
typo3/sysext/saltedpasswords/ext_localconf.php
typo3/sysext/saltedpasswords/ext_tables.php
typo3/sysext/saltedpasswords/locallang.xlf
typo3/sysext/saltedpasswords/locallang_csh_saltedpasswords.xlf [new file with mode: 0644]

index 123838e..7b58427 100644 (file)
@@ -35,9 +35,6 @@
 class tx_saltedpasswords_Tasks_BulkUpdate extends tx_scheduler_Task {
        /**
         * @var boolean Whether or not the task is allowed to deactivate itself after processing all existing user records.
-        * @TODO: This could be set with an additional field later on.
-        *              The idea is to not disable the task after all initial users where handled.
-        *              This could be handy for example if new users are imported regularily from some external source.
         */
        protected $canDeactivateSelf = TRUE;
 
@@ -48,7 +45,6 @@ class tx_saltedpasswords_Tasks_BulkUpdate extends tx_scheduler_Task {
         * If saltedpasswords is enabled for both frontend and backend 2 * numberOfRecords will be handled.
         *
         * @var integer Number of records
-        * @TODO: This could be set with an additional field later on
         */
        protected $numberOfRecords = 250;
 
@@ -72,7 +68,7 @@ class tx_saltedpasswords_Tasks_BulkUpdate extends tx_scheduler_Task {
        /**
         * Execute task
         *
-        * @return void
+        * @return boolean
         */
        public function execute() {
                $processedAllRecords = TRUE;
@@ -91,20 +87,45 @@ class tx_saltedpasswords_Tasks_BulkUpdate extends tx_scheduler_Task {
                        }
                }
 
-                       // Determine if task should disable itself
-               if ($this->canDeactivateSelf && $processedAllRecords) {
-                       $this->deactivateSelf();
+               if ($processedAllRecords) {
+                               // Reset the user record pointer
+                       $this->userRecordPointer = array(
+                               'FE' => 0,
+                               'BE' => 0,
+                       );
+                               // Determine if task should disable itself
+                       if ($this->canDeactivateSelf) {
+                               $this->deactivateSelf();
+                       }
                }
 
-                       // Use save() of parent class tx_scheduler_Task to persist
-                       // changed task variables: $this->userRecordPointer and $this->disabled
+                       // Use save() of parent class tx_scheduler_Task to persist changed task variables
                $this->save();
 
-               return(TRUE);
+               return TRUE;
+       }
+
+       /**
+        * Get additional information
+        *
+        * @return string Additional information
+        */
+       public function getAdditionalInformation() {
+               $information =
+                       $GLOBALS['LANG']->sL(
+                               'LLL:EXT:saltedpasswords/locallang.xml:ext.saltedpasswords.tasks.bulkupdate.label.additionalinformation.deactivateself'
+                       ) .
+                       $this->getCanDeactivateSelf() . '; ' .
+                       $GLOBALS['LANG']->sL(
+                               'LLL:EXT:saltedpasswords/locallang.xml:ext.saltedpasswords.tasks.bulkupdate.label.additionalinformation.numberofrecords'
+                       ) .
+                       $this->getNumberOfRecords();
+
+               return $information;
        }
 
        /**
-        * Find next set of frontend or backend users to update.
+        * Finds next set of frontend or backend users to update.
         *
         * @param string $mode 'FE' for frontend, 'BE' for backend user records
         * @return array Rows with uid and password
@@ -113,7 +134,7 @@ class tx_saltedpasswords_Tasks_BulkUpdate extends tx_scheduler_Task {
                $usersToUpdate = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
                        'uid, password',
                        strtolower($mode) . '_users',
-                               // retrieve and update all records (also disabled/deleted) for security reasons
+                               // Retrieve and update all records (also disabled/deleted) for security reasons
                        '1 = 1',
                        '',
                        'uid ASC',
@@ -124,13 +145,13 @@ class tx_saltedpasswords_Tasks_BulkUpdate extends tx_scheduler_Task {
        }
 
        /**
-        * Iterate over given user records and update password if needed.
+        * Iterates over given user records and update password if needed.
         *
         * @param string $mode 'FE' for frontend, 'BE' for backend user records
         * @param array $users With user uids and passwords
         * @return void
         */
-       protected function convertPasswords($mode, $users) {
+       protected function convertPasswords($mode, array $users) {
                $updateUsers = array();
                foreach ($users as $user) {
                                // If a password is already a salted hash it must not be updated
@@ -147,21 +168,21 @@ class tx_saltedpasswords_Tasks_BulkUpdate extends tx_scheduler_Task {
        }
 
        /**
-        * Update password and persist salted hash.
+        * Updates password and persist salted hash.
         *
         * @param string $mode 'FE' for frontend, 'BE' for backend user records
         * @param array $users With user uids and passwords
         * @return void
         */
-       protected function updatePasswords($mode, $users) {
-                       // Get a default saltedpasswords instance
+       protected function updatePasswords($mode, array $users) {
+               /** @var $saltedpasswordsInstance tx_saltedpasswords_salts */
                $saltedpasswordsInstance = tx_saltedpasswords_salts_factory::getSaltingInstance(NULL, $mode);
 
                foreach ($users as $user) {
                        $newPassword = $saltedpasswordsInstance->getHashedPassword($user['password']);
 
                                // If a given password is a md5 hash (usually default be_users without saltedpasswords activated),
-                               // result of getHasedPassword() is a salted hashed md5 hash.
+                               // result of getHashedPassword() is a salted hashed md5 hash.
                                // We prefix those with 'M', saltedpasswords will then update this password
                                // to a usual salted hash upon first login of the user.
                        if ($this->isMd5Password($user['password'])) {
@@ -206,9 +227,9 @@ class tx_saltedpasswords_Tasks_BulkUpdate extends tx_scheduler_Task {
        }
 
        /**
-        * Check if a given password is a md5 hash, the default for be_user records before saltedpasswords.
+        * Checks if a given password is a md5 hash, the default for be_user records before saltedpasswords.
         *
-        * @param string $password
+        * @param string $password The password to test
         * @return boolean TRUE if password is md5
         */
        protected function isMd5Password($password) {
@@ -216,7 +237,7 @@ class tx_saltedpasswords_Tasks_BulkUpdate extends tx_scheduler_Task {
        }
 
        /**
-        * Increment current user record counter by number of handled rows.
+        * Increments current user record counter by number of handled rows.
         *
         * @param string $mode 'FE' for frontend, 'BE' for backend user records
         * @param integer $number Number of handled rows
@@ -227,7 +248,7 @@ class tx_saltedpasswords_Tasks_BulkUpdate extends tx_scheduler_Task {
        }
 
        /**
-        * Deactivate this task instance.
+        * Deactivates this task instance.
         * Uses setDisabled() method of parent class tx_scheduler_Task.
         *
         * @return void
@@ -235,5 +256,44 @@ class tx_saltedpasswords_Tasks_BulkUpdate extends tx_scheduler_Task {
        protected function deactivateSelf() {
                $this->setDisabled(TRUE);
        }
+
+       /**
+        * Set if it can deactivate self
+        *
+        * @param boolean $canDeactivateSelf
+        * @return void
+        */
+       public function setCanDeactivateSelf($canDeactivateSelf) {
+               $this->canDeactivateSelf = $canDeactivateSelf;
+       }
+
+       /**
+        * Get if it can deactivate self
+        *
+        * @return boolean TRUE if task shall deactivate itself, FALSE otherwise
+        */
+       public function getCanDeactivateSelf() {
+               return $this->canDeactivateSelf;
+       }
+
+       /**
+        * Set number of records
+        *
+        * @param integer $numberOfRecords
+        * @return void
+        */
+       public function setNumberOfRecords($numberOfRecords) {
+               $this->numberOfRecords = $numberOfRecords;
+       }
+
+       /**
+        * Get number of records
+        *
+        * @return integer The number of records
+        */
+       public function getNumberOfRecords() {
+               return $this->numberOfRecords;
+       }
 }
+
 ?>
\ No newline at end of file
diff --git a/typo3/sysext/saltedpasswords/classes/tasks/class.tx_saltedpasswords_tasks_bulkupdate_additionalfieldprovider.php b/typo3/sysext/saltedpasswords/classes/tasks/class.tx_saltedpasswords_tasks_bulkupdate_additionalfieldprovider.php
new file mode 100644 (file)
index 0000000..e0c4b77
--- /dev/null
@@ -0,0 +1,152 @@
+<?php
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2012 Philipp Gampe (typo3@philippgampe.info)
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Additional field for salted passwords bulk update task
+ *
+ * @autor Philipp Gampe <typo3@philippgampe.info>
+ * @package TYPO3
+ * @subpackage saltedpasswords
+ */
+class tx_saltedpasswords_Tasks_BulkUpdate_AdditionalFieldProvider implements tx_scheduler_AdditionalFieldProvider {
+
+       /**
+        * Default value whether the task deactivates itself after last run.
+        * @var boolean Whether the task is allowed to deactivate itself after processing all existing user records.
+        */
+       protected $defaultCanDeactivateSelf = TRUE;
+
+       /**
+        * Default value for the number of records to handle at each run.
+        * @var integer Number of records
+        */
+       protected $defaultNumberOfRecords = 250;
+
+       /**
+        * Add a field for the number of records and if the task should deactivate itself after
+        * processing all records
+        *
+        * @param array $taskInfo Reference to the array containing the info used in the add/edit form
+        * @param tx_saltedpasswords_Tasks_BulkUpdate $task When editing, reference to the current task object. Null when adding.
+        * @param tx_scheduler_Module $parentObject Reference to the calling object (Scheduler's BE module)
+        * @return array Array containing all the information pertaining to the additional fields
+        */
+       public function getAdditionalFields(array &$taskInfo, $task, tx_scheduler_Module $parentObject) {
+                       // Initialize selected fields
+               if (!isset($taskInfo['scheduler_saltedpasswordsBulkUpdateCanDeactivateSelf'])) {
+                       $taskInfo['scheduler_saltedpasswordsBulkUpdateCanDeactivateSelf'] = $this->defaultCanDeactivateSelf;
+                       if ($parentObject->CMD === 'edit') {
+                               $taskInfo['scheduler_saltedpasswordsBulkUpdateCanDeactivateSelf'] = $task->getCanDeactivateSelf();
+                       }
+               }
+               if (!isset($taskInfo['scheduler_saltedpasswordsBulkUpdateNumberOfRecords'])) {
+                       $taskInfo['scheduler_saltedpasswordsBulkUpdateNumberOfRecords'] = $this->defaultNumberOfRecords;
+                       if ($parentObject->CMD === 'edit') {
+                               $taskInfo['scheduler_saltedpasswordsBulkUpdateNumberOfRecords'] = $task->getNumberOfRecords();
+                       }
+               }
+
+                       // Configuration for canDeactivateSelf
+               $fieldName = 'tx_scheduler[scheduler_saltedpasswordsBulkUpdateCanDeactivateSelf]';
+               $fieldId = 'task_saltedpasswordsBulkUpdateCanDeactivateSelf';
+               $fieldValue = 'IsChecked';
+               $fieldChecked = (bool)($taskInfo['scheduler_saltedpasswordsBulkUpdateCanDeactivateSelf']);
+               $fieldHtml = '<input type="checkbox"' .
+                       ' name="' . $fieldName .'"' .
+                       ' id="' . $fieldId .'"' .
+                       ' value="' . $fieldValue .'"' .
+                       ($fieldChecked ? ' checked="checked"' : '') .
+                       ' />';
+
+               $additionalFields[$fieldId] = array(
+                       'code' => $fieldHtml,
+                       'label' => 'LLL:EXT:saltedpasswords/locallang.xml:ext.saltedpasswords.tasks.bulkupdate.label.canDeactivateSelf',
+                       'cshKey' => '_txsaltedpasswords',
+                       'cshLabel' => $fieldId,
+               );
+
+                       // Configuration for numberOfRecords
+               $fieldName = 'tx_scheduler[scheduler_saltedpasswordsBulkUpdateNumberOfRecords]';
+               $fieldId = 'task_saltedpasswordsBulkUpdateNumberOfRecords';
+               $fieldValue = intval($taskInfo['scheduler_saltedpasswordsBulkUpdateNumberOfRecords']);
+               $fieldHtml = '<input type="text" name="' . $fieldName . '" id="' . $fieldId . '" value="' . htmlspecialchars($fieldValue) . '" />';
+
+               $additionalFields[$fieldId] = array(
+                       'code' => $fieldHtml,
+                       'label' => 'LLL:EXT:saltedpasswords/locallang.xml:ext.saltedpasswords.tasks.bulkupdate.label.numberOfRecords',
+                       'cshKey' => '_txsaltedpasswords',
+                       'cshLabel' => $fieldId,
+               );
+
+               return $additionalFields;
+       }
+
+       /**
+        * Checks if the given values are boolean and integer
+        *
+        * @param array $submittedData Reference to the array containing the data submitted by the user
+        * @param tx_scheduler_Module $parentObject Reference to the calling object (Scheduler's BE module)
+        * @return boolean TRUE if validation was ok (or selected class is not relevant), FALSE otherwise
+        */
+       public function validateAdditionalFields(array &$submittedData, tx_scheduler_Module $parentObject) {
+               $result = TRUE;
+
+                       // Check if number of records is indeed a number and greater or equals to 0
+                       // If not, fail validation and issue error message
+               if (
+                       !is_numeric($submittedData['scheduler_saltedpasswordsBulkUpdateNumberOfRecords']) ||
+                       intval($submittedData['scheduler_saltedpasswordsBulkUpdateNumberOfRecords']) < 0
+               ) {
+                       $result = FALSE;
+                       $parentObject->addMessage($GLOBALS['LANG']->sL('LLL:EXT:saltedpasswords/locallang.xml:ext.saltedpasswords.tasks.bulkupdate.invalidNumberOfRecords'), t3lib_FlashMessage::ERROR);
+               }
+
+               return $result;
+       }
+
+       /**
+        * Saves given values in task object
+        *
+        * @param array $submittedData Contains data submitted by the user
+        * @param tx_scheduler_Task|tx_saltedpasswords_Tasks_BulkUpdate $task Reference to the current task object
+        * @return void
+        */
+       public function saveAdditionalFields(array $submittedData, tx_scheduler_Task $task) {
+               if (
+                       isset($submittedData['scheduler_saltedpasswordsBulkUpdateCanDeactivateSelf'])
+                       && $submittedData['scheduler_saltedpasswordsBulkUpdateCanDeactivateSelf'] === 'IsChecked'
+               ) {
+                       $task->setCanDeactivateSelf(TRUE);
+               } else {
+                       $task->setCanDeactivateSelf(FALSE);
+               }
+
+               $task->setNumberOfRecords(intval($submittedData['scheduler_saltedpasswordsBulkUpdateNumberOfRecords']));
+       }
+
+}
+?>
\ No newline at end of file
index 3b7eb78..07035f9 100644 (file)
@@ -16,6 +16,7 @@ return array(
        'tx_saltedpasswords_eval_be' => $extensionPath . 'classes/eval/class.tx_saltedpasswords_eval_be.php',
        'tx_saltedpasswords_eval_fe' => $extensionPath . 'classes/eval/class.tx_saltedpasswords_eval_fe.php',
        'tx_saltedpasswords_tasks_bulkupdate' => $extensionPath . 'classes/tasks/class.tx_saltedpasswords_tasks_bulkupdate.php',
+       'tx_saltedpasswords_tasks_bulkupdate_additionalfieldprovider' => $extensionPath .'classes/tasks/class.tx_saltedpasswords_tasks_bulkupdate_additionalfieldprovider.php',
        'tx_saltedpasswords_sv1' => $extensionPath . 'sv1/class.tx_saltedpasswords_sv1.php',
        'tx_saltedpasswords_div_testcase' => $extensionPath . 'tests/tx_saltedpasswords_div_testcase.php',
        'tx_saltedpasswords_salts_blowfish_testcase' => $extensionPath . 'tests/tx_saltedpasswords_salts_blowfish_testcase.php',
index cde96fa..8f3fe38 100644 (file)
@@ -45,7 +45,7 @@ $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks']['tx_saltedpasswo
        'extension' => $_EXTKEY,
        'title' => 'LLL:EXT:' . $_EXTKEY . '/locallang.xml:ext.saltedpasswords.tasks.bulkupdate.name',
        'description' => 'LLL:EXT:' . $_EXTKEY . '/locallang.xml:ext.saltedpasswords.tasks.bulkupdate.description',
-       'additionalFields' => '',
+       'additionalFields' => 'tx_saltedpasswords_Tasks_BulkUpdate_AdditionalFieldProvider',
 );
 
 ?>
\ No newline at end of file
index 74cf18e..ef2a200 100644 (file)
@@ -46,4 +46,6 @@ if (tx_saltedpasswords_div::isUsageEnabled('BE')) {
        $GLOBALS['TYPO3_USER_SETTINGS']['columns']['password2']['eval'] = '';
 }
 
+               // Add context sensitive help (csh) for scheduler task
+       t3lib_extMgm::addLLrefForTCAdescr('_txsaltedpasswords', 'EXT:' . $_EXTKEY . '/locallang_csh_saltedpasswords.xml');
 ?>
\ No newline at end of file
index 0dde570..8b32536 100644 (file)
                        <trans-unit id="ext.saltedpasswords.tasks.bulkupdate.description" xml:space="preserve">
                                <source>Update all frontend and backend user passwords to salted hashes. This task deactivates itself when completed.</source>
                        </trans-unit>
+                       <trans-unit id="ext.saltedpasswords.tasks.bulkupdate.label.numberOfRecords" xml:space="preserve">
+                               <source>The number of records to process on each run</source>
+                       </trans-unit>
+                       <trans-unit id="ext.saltedpasswords.tasks.bulkupdate.invalidNumberOfRecords" xml:space="preserve">
+                               <source>The number of records to process on each run must be greater than zero and numeric.</source>
+                       </trans-unit>
+                       <trans-unit id="ext.saltedpasswords.tasks.bulkupdate.label.canDeactivateSelf" xml:space="preserve">
+                               <source>Deactivate if all records are converted</source>
+                       </trans-unit>
+                       <trans-unit id="ext.saltedpasswords.tasks.bulkupdate.label.additionalinformation.deactivateself" xml:space="preserve">
+                               <source>Deactivate=</source>
+                       </trans-unit>
+                       <trans-unit id="ext.saltedpasswords.tasks.bulkupdate.label.additionalinformation.numberofrecords" xml:space="preserve">
+                               <source>Number of records=</source>
+                       </trans-unit>
                </body>
        </file>
 </xliff>
diff --git a/typo3/sysext/saltedpasswords/locallang_csh_saltedpasswords.xlf b/typo3/sysext/saltedpasswords/locallang_csh_saltedpasswords.xlf
new file mode 100644 (file)
index 0000000..691c2c2
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xliff version="1.0">
+       <file source-language="en" datatype="plaintext" original="messages" date="2012-02-22T00:00:00Z" product-name="saltedpasswords">
+               <header/>
+               <body>
+                       <trans-unit id="task_saltedpasswordsBulkUpdateNumberOfRecords.alttitle" xml:space="preserve">
+                               <source>Number of records to process</source>
+                       </trans-unit>
+                       <trans-unit id="task_saltedpasswordsBulkUpdateNumberOfRecords.description" xml:space="preserve">
+                               <source>Number of records to process for each run. If enabled for both FE &amp; BE, the value will be doubled.</source>
+                       </trans-unit>
+                       <trans-unit id="task_saltedpasswordsBulkUpdateNumberOfRecords.details" xml:space="preserve">
+                               <source>Converting a password to a salted hash takes some milliseconds (~100ms on an entry system in 2010).
+                                       If all users are updated in one run, the task might run a long time if a lot of users must be handled.
+                                       Therefore only a small number of frontend and backend users are processed.
+                                       If saltedpasswords is enabled for both FE &amp; BE, this value will be doubled.</source>
+                       </trans-unit>
+                       <trans-unit id="task_saltedpasswordsBulkUpdateCanDeactivateSelf.alttitle" xml:space="preserve">
+                               <source>Deactivate this task</source>
+                       </trans-unit>
+                       <trans-unit id="task_saltedpasswordsBulkUpdateCanDeactivateSelf.description" xml:space="preserve">
+                               <source>Whether the task should deactivate itself after all records are processed.
+                                       Uncheck this if you are importing users from an external source and/or need to run this task periodically.</source>
+                       </trans-unit>
+               </body>
+       </file>
+</xliff>
\ No newline at end of file