[FEATURE] Add scheduler task to anonymize IP addresses of tables 25/56925/6
authorGeorg Ringer <georg.ringer@gmail.com>
Thu, 19 Apr 2018 11:36:03 +0000 (13:36 +0200)
committerMathias Brodala <mbrodala@pagemachine.de>
Tue, 15 May 2018 07:19:14 +0000 (09:19 +0200)
IP anonymizing improves the privacy of users and required as part of
the GDPR.

Resolves: #84781
Releases: master, 8.7, 7.6
Change-Id: Idd938136bed8f29e86b5e3e541c9fb8c9afd48ff
Reviewed-on: https://review.typo3.org/56925
Tested-by: TYPO3com <no-reply@typo3.com>
Reviewed-by: Georg Ringer <georg.ringer@gmail.com>
Tested-by: Georg Ringer <georg.ringer@gmail.com>
typo3/sysext/core/Documentation/Changelog/7.6.x/Feature-84781-AddedSchedulerTaskToAnonymizeIPAddressesOfTables.rst [new file with mode: 0644]
typo3/sysext/indexed_search/ext_localconf.php
typo3/sysext/scheduler/Classes/Task/IpAnonymizationAdditionalFieldProvider.php [new file with mode: 0644]
typo3/sysext/scheduler/Classes/Task/IpAnonymizationTask.php [new file with mode: 0644]
typo3/sysext/scheduler/Resources/Private/Language/locallang.xlf
typo3/sysext/scheduler/ext_localconf.php

diff --git a/typo3/sysext/core/Documentation/Changelog/7.6.x/Feature-84781-AddedSchedulerTaskToAnonymizeIPAddressesOfTables.rst b/typo3/sysext/core/Documentation/Changelog/7.6.x/Feature-84781-AddedSchedulerTaskToAnonymizeIPAddressesOfTables.rst
new file mode 100644 (file)
index 0000000..e77b6a9
--- /dev/null
@@ -0,0 +1,38 @@
+.. include:: ../../Includes.txt
+
+==========================================================================
+Feature: #84781 - Added scheduler task to anonymize IP addresses of tables
+==========================================================================
+
+See :issue:`84781`
+
+Description
+===========
+
+A new scheduler task has been added which makes it possible to anonymize IP addresses stored in database tables.
+
+The task *Anonymize IP addresses in database tables* is configured in the :file:`ext_localconf.php`.
+
+.. code-block:: php
+
+    $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\IpAnonymizationTask::class]['options']['tables']['<tableName>'] = [
+        'dateField' => '<dateFieldName>',
+        'ipField' => '<ipFieldName>'
+    ];
+
+After the base configuration the table is available in the scheduler task with the following configuration options:
+
+- Table
+- Minimum age an entry must have to be anonymized
+- IP mask level
+
+
+Impact
+======
+
+The following tables are available by default:
+
+- index_stat_search
+- sys_log
+
+.. index:: CLI, ext:scheduler
\ No newline at end of file
index 274d841..554cab2 100644 (file)
@@ -75,3 +75,10 @@ if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3
         'expirePeriod' => 90
     ];
 }
+
+if (isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\IpAnonymizationTask::class]['options']['tables'])) {
+    $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\IpAnonymizationTask::class]['options']['tables']['index_stat_search'] = [
+        'dateField' => 'tstamp',
+        'ipField' => 'IP'
+    ];
+}
diff --git a/typo3/sysext/scheduler/Classes/Task/IpAnonymizationAdditionalFieldProvider.php b/typo3/sysext/scheduler/Classes/Task/IpAnonymizationAdditionalFieldProvider.php
new file mode 100644 (file)
index 0000000..0d7b373
--- /dev/null
@@ -0,0 +1,248 @@
+<?php
+
+namespace TYPO3\CMS\Scheduler\Task;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Lang\LanguageService;
+use TYPO3\CMS\Scheduler\AdditionalFieldProviderInterface;
+
+/**
+ * Additional BE fields for ip address anonymization task.
+ */
+class IpAnonymizationAdditionalFieldProvider implements AdditionalFieldProviderInterface
+{
+    /**
+     * @var array Default number of days by table
+     */
+    protected $defaultNumberOfDays = [];
+
+    /**
+     * Add additional fields
+     *
+     * @param array $taskInfo Reference to the array containing the info used in the add/edit form
+     * @param AbstractTask|null $task When editing, reference to the current task. NULL when adding.
+     * @param \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $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, \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject)
+    {
+        $additionalFields = [];
+        $additionalFields['task_ipAnonymization_table'] = $this->getTableAdditionalField($taskInfo, $task, $parentObject);
+        $additionalFields['task_ipAnonymization_numberOfDays'] = $this->getNumberOfDaysAdditionalField($taskInfo, $task, $parentObject);
+        $additionalFields['task_ipAnonymization_mask'] = $this->getMaskAdditionalField($taskInfo, $task, $parentObject);
+        return $additionalFields;
+    }
+
+    /**
+     * Add a select field of available tables.
+     *
+     * @param array $taskInfo Reference to the array containing the info used in the add/edit form
+     * @param AbstractTask|IpAnonymizationTask|null $task When editing, reference to the current task. NULL when adding.
+     * @param \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject Reference to the calling object (Scheduler's BE module)
+     * @return array Array containing all the information pertaining to the additional fields
+     */
+    protected function getTableAdditionalField(array &$taskInfo, $task, \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject)
+    {
+        $tableConfiguration = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][IpAnonymizationTask::class]['options']['tables'];
+        $options = [];
+        // Add an empty option on top if an existing task is configured
+        // with a table that can not be found in configuration anymore
+        if ($parentObject->CMD === 'edit' && !array_key_exists($task->table, $tableConfiguration)) {
+            $options[] = '<option value="" selected="selected"></option>';
+        }
+        foreach ($tableConfiguration as $tableName => $configuration) {
+            if ($parentObject->CMD === 'add' && empty($options)) {
+                // Select first table by default if adding a new task
+                $options[] = '<option value="' . $tableName . '" selected="selected">' . $tableName . '</option>';
+            } elseif ($task->table === $tableName) {
+                // Select currently selected table
+                $options[] = '<option value="' . $tableName . '" selected="selected">' . $tableName . '</option>';
+            } else {
+                $options[] = '<option value="' . $tableName . '">' . $tableName . '</option>';
+            }
+        }
+        $fieldName = 'tx_scheduler[scheduler_ipAnonymization_table]';
+        $fieldId = 'task_ipAnonymization_table';
+        $fieldHtml = [];
+        // Add table drop down html
+        $fieldHtml[] = '<select class="form-control" name="' . $fieldName . '" id="' . $fieldId . '">' . implode(LF, $options) . '</select>';
+        // Add js array for default 'number of days' values
+        $fieldHtml[] = '<script type="text/javascript">/*<![CDATA[*/<!--';
+        $fieldHtml[] = 'var defaultNumberOfDays = ' . json_encode($this->defaultNumberOfDays) . ';';
+        $fieldHtml[] = '// -->/*]]>*/</script>';
+        $fieldConfiguration = [
+            'code' => implode(LF, $fieldHtml),
+            'label' => 'LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:label.ipAnonymization.table',
+            'cshKey' => '_MOD_system_txschedulerM1',
+            'cshLabel' => $fieldId
+        ];
+        return $fieldConfiguration;
+    }
+
+    /**
+     * Add an input field to get the number of days.
+     *
+     * @param array $taskInfo Reference to the array containing the info used in the add/edit form
+     * @param AbstractTask|IpAnonymizationTask|null $task When editing, reference to the current task. NULL when adding.
+     * @param \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject Reference to the calling object (Scheduler's BE module)
+     * @return array Array containing all the information pertaining to the additional fields
+     */
+    protected function getNumberOfDaysAdditionalField(array &$taskInfo, $task, \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject)
+    {
+        $fieldId = 'scheduler_ipAnonymization_numberOfDays';
+        // Initialize selected fields
+        $disabled = '';
+        if (empty($taskInfo[$fieldId])) {
+            if ($parentObject->CMD === 'add') {
+                // In case of new task, set to 180 days
+                $taskInfo[$fieldId] = 180;
+            } elseif ($parentObject->CMD === 'edit') {
+                // In case of editing the task, set to currently selected value
+                $taskInfo[$fieldId] = $task->numberOfDays;
+                if ($task->numberOfDays === 0 && !isset($this->defaultNumberOfDays[$task->table])) {
+                    $disabled = ' disabled="disabled"';
+                }
+            }
+        }
+        $fieldName = 'tx_scheduler[' . $fieldId . ']';
+        $fieldHtml = '<input class="form-control" type="text" ' . 'name="' . $fieldName . '" ' . 'id="' . $fieldId . '" ' . $disabled . 'value="' . (int)$taskInfo['scheduler_ipAnonymization_numberOfDays'] . '" ' . 'size="4">';
+        $fieldConfiguration = [
+            'code' => $fieldHtml,
+            'label' => 'LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:label.ipAnonymization.numberOfDays',
+            'cshKey' => '_MOD_system_txschedulerM1',
+            'cshLabel' => $fieldId
+        ];
+        return $fieldConfiguration;
+    }
+
+    /**
+     * Add an input field to get the mask.
+     *
+     * @param array $taskInfo Reference to the array containing the info used in the add/edit form
+     * @param AbstractTask|IpAnonymizationTask|null $task When editing, reference to the current task. NULL when adding.
+     * @param \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject Reference to the calling object (Scheduler's BE module)
+     * @return array Array containing all the information pertaining to the additional fields
+     */
+    protected function getMaskAdditionalField(array &$taskInfo, $task, \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject)
+    {
+        $fieldId = 'scheduler_ipAnonymization_mask';
+        // Initialize selected fields
+        if (empty($taskInfo[$fieldId])) {
+            if ($parentObject->CMD === 'add') {
+                // In case of new task, set to 180 days
+                $taskInfo[$fieldId] = 2;
+            } elseif ($parentObject->CMD === 'edit') {
+                // In case of editing the task, set to currently selected value
+                $taskInfo[$fieldId] = $task->mask;
+            }
+        }
+        $fieldName = 'tx_scheduler[' . $fieldId . ']';
+
+        $fieldHtml = '';
+        foreach ([1, 2] as $mask) {
+            $selected = (int)$taskInfo[$fieldId] === $mask ? ' selected' : '';
+            $fieldHtml .= '<option value="' . $mask . '"' . $selected . '>'
+                . htmlspecialchars($this->getLanguageService()->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:label.ipAnonymization.mask.' . $mask))
+                . '</option>';
+        }
+
+        $fieldHtml = '<select class="form-control" name="' . $fieldName . '" ' . 'id="' . $fieldId . '">' . $fieldHtml . '</select>';
+
+        $fieldConfiguration = [
+            'code' => $fieldHtml,
+            'label' => 'LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:label.ipAnonymization.mask',
+            'cshKey' => '_MOD_system_txschedulerM1',
+            'cshLabel' => $fieldId
+        ];
+        return $fieldConfiguration;
+    }
+
+    /**
+     * Validate additional fields
+     *
+     * @param array $submittedData Reference to the array containing the data submitted by the user
+     * @param \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject Reference to the calling object (Scheduler's BE module)
+     * @return bool True if validation was ok (or selected class is not relevant), false otherwise
+     */
+    public function validateAdditionalFields(array &$submittedData, \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject)
+    {
+        $validData = $this->validateTableAdditionalField($submittedData, $parentObject);
+        $validData &= $this->validateNumberOfDaysAdditionalField($submittedData, $parentObject);
+        return $validData;
+    }
+
+    /**
+     * Checks given table for existence in configuration array
+     *
+     * @param array $submittedData Reference to the array containing the data submitted by the user
+     * @param \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject Reference to the calling object (Scheduler's BE module)
+     * @return bool True if table exists in configuration, false otherwise
+     */
+    public function validateTableAdditionalField(array &$submittedData, \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject)
+    {
+        $validData = false;
+        $tableConfiguration = $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][IpAnonymizationTask::class]['options']['tables'];
+        if (!isset($submittedData['scheduler_ipAnonymization_table'])) {
+            $validData = true;
+        } elseif (array_key_exists($submittedData['scheduler_ipAnonymization_table'], $tableConfiguration)) {
+            $validData = true;
+        }
+        return $validData;
+    }
+
+    /**
+     * Checks if given number of days is a positive integer
+     *
+     * @param array $submittedData Reference to the array containing the data submitted by the user
+     * @param \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject Reference to the calling object (Scheduler's BE module)
+     * @return bool True if validation was ok (or selected class is not relevant), false otherwise
+     */
+    public function validateNumberOfDaysAdditionalField(array &$submittedData, \TYPO3\CMS\Scheduler\Controller\SchedulerModuleController $parentObject)
+    {
+        $validData = false;
+        if (!isset($submittedData['scheduler_ipAnonymization_numberOfDays'])) {
+            $validData = true;
+        } elseif ((int)$submittedData['scheduler_ipAnonymization_numberOfDays'] >= 0) {
+            $validData = true;
+        } else {
+            // Issue error message
+            $parentObject->addMessage($this->getLanguageService()->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:msg.invalidNumberOfDays'), \TYPO3\CMS\Core\Messaging\FlashMessage::ERROR);
+        }
+        return $validData;
+    }
+
+    /**
+     * Save additional field in task
+     *
+     * @param array $submittedData Contains data submitted by the user
+     * @param AbstractTask|IpAnonymizationTask $task Reference to the current task object
+     */
+    public function saveAdditionalFields(array $submittedData, AbstractTask $task)
+    {
+        $task->table = $submittedData['scheduler_ipAnonymization_table'];
+        $task->mask = $submittedData['scheduler_ipAnonymization_mask'];
+        $task->numberOfDays = (int)$submittedData['scheduler_ipAnonymization_numberOfDays'];
+    }
+
+    /**
+     * Returns an instance of LanguageService
+     *
+     * @return LanguageService
+     */
+    protected function getLanguageService()
+    {
+        return $GLOBALS['LANG'];
+    }
+}
diff --git a/typo3/sysext/scheduler/Classes/Task/IpAnonymizationTask.php b/typo3/sysext/scheduler/Classes/Task/IpAnonymizationTask.php
new file mode 100644 (file)
index 0000000..3befe3f
--- /dev/null
@@ -0,0 +1,123 @@
+<?php
+
+namespace TYPO3\CMS\Scheduler\Task;
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+use TYPO3\CMS\Core\Database\DatabaseConnection;
+use TYPO3\CMS\Core\Utility\IpAnonymizationUtility;
+
+/**
+ * Anonymize IP addresses in records
+ *
+ * This task anonymizes IP addresses in tables older than the given number of days.
+ *
+ * Available tables must be registered in
+ * $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\IpAnonymizationTask::class]['options']['tables']
+ * See ext_localconf.php of scheduler extension for an example
+ */
+class IpAnonymizationTask extends AbstractTask
+{
+
+    /**
+     * @var int Number of days
+     */
+    public $numberOfDays = 180;
+
+    /**
+     * @var int mask level see \TYPO3\CMS\Core\Utility\IpAnonymizationUtility::anonymizeIp
+     */
+    public $mask = 2;
+
+    /**
+     * @var string Table to clean up
+     */
+    public $table = '';
+
+    /**
+     * Execute garbage collection, called by scheduler.
+     *
+     * @throws \RuntimeException If configured table was not cleaned up
+     * @return bool TRUE if task run was successful
+     */
+    public function execute()
+    {
+        $configuration = isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][self::class]['options']['tables'][$this->table])
+            ? $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][self::class]['options']['tables'][$this->table]
+            : [];
+        if (empty($configuration)) {
+            throw new \RuntimeException(self::class . ' misconfiguration: ' . $this->table . ' does not exist in configuration', 1524502548);
+        }
+        $this->handleTable($this->table, $configuration);
+        return true;
+    }
+
+    /**
+     * Execute clean up of a specific table
+     *
+     * @throws \RuntimeException If table configuration is broken
+     * @param string $table The table to handle
+     * @param array $configuration Clean up configuration
+     * @return bool TRUE if cleanup was successful
+     */
+    protected function handleTable($table, array $configuration)
+    {
+        $database = $this->getDatabase();
+        if (empty($configuration['dateField'])) {
+            throw new \RuntimeException(self::class . ' misconfiguration: "dateField" must be defined for table ' . $table, 1524502549);
+        }
+        if (empty($configuration['ipField'])) {
+            throw new \RuntimeException(self::class . ' misconfiguration: "ipField" must be defined for table ' . $table, 1524502666);
+        }
+        $deleteTimestamp = strtotime('-' . $this->numberOfDays . 'days');
+        if ($deleteTimestamp === false) {
+            throw new \RuntimeException(self::class . ' misconfiguration: number of days could not be calculated for table ' . $table, 1524526354);
+        }
+        $where = $configuration['dateField'] . ' < ' . (int)$deleteTimestamp
+                 . ' AND ' . $configuration['ipField'] . ' <> \'\''
+                 . ' AND ' . $configuration['ipField'] . ' is not null'
+                 . ' AND ' . $configuration['ipField'] . ' not like \'%.0.0\''
+                 . ' AND ' . $configuration['ipField'] . ' not like \'%::\'';
+        $res = $database->exec_SELECTquery('uid,' . $configuration['ipField'], $table, $where);
+        if (!$res) {
+            throw new \RuntimeException(self::class . ' failed for table ' . $this->table . ' with error: ' . $database->sql_error(), 1524502550);
+        }
+        while ($row = $database->sql_fetch_assoc($res)) {
+            $ip = (string)$row[$configuration['ipField']];
+            $database->exec_UPDATEquery($table, 'uid = ' . (int)$row['uid'], [
+                $configuration['ipField'] => IpAnonymizationUtility::anonymizeIp($ip, $this->mask)
+            ]);
+        }
+        $database->sql_free_result($res);
+        return true;
+    }
+
+    /**
+     * This method returns the selected table as additional information
+     *
+     * @return string Information to display
+     */
+    public function getAdditionalInformation()
+    {
+        return sprintf($GLOBALS['LANG']->sL('LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:label.ipAnonymization.additionalInformationTable'), $this->table, $this->numberOfDays);
+    }
+
+    /**
+     * @return DatabaseConnection|null
+     */
+    private function getDatabase()
+    {
+        return $GLOBALS['TYPO3_DB'];
+    }
+}
index b3e1502..21a5535 100644 (file)
                        <trans-unit id="optimizeDatabaseTable.description">
                                <source>This task executes "OPTIMIZE TABLE" statements on the selected database tables. This helps to reduce storage space and improve I/O efficiency. Warning! Tables will be locked during the optimization process.</source>
                        </trans-unit>
+                       <trans-unit id="ipAnonymization.name">
+                               <source>Anonymize IP addresses in database tables</source>
+                       </trans-unit>
+                       <trans-unit id="ipAnonymization.description">
+                               <source>This task anonymizes the IP addresses to enforce the privacy of the persisted data.</source>
+                       </trans-unit>
+                       <trans-unit id="label.ipAnonymization.table">
+                               <source>Table to anoymize</source>
+                       </trans-unit>
+                       <trans-unit id="label.ipAnonymization.additionalInformationTable">
+                               <source>Table: %s after %s days</source>
+                       </trans-unit>
+                       <trans-unit id="label.ipAnonymization.additionalInformationAllTables">
+                               <source>All tables</source>
+                       </trans-unit>
+                       <trans-unit id="label.ipAnonymization.numberOfDays">
+                               <source>Handle entries older than given number of days</source>
+                       </trans-unit>
+                       <trans-unit id="label.ipAnonymization.mask">
+                               <source>Mask level</source>
+                       </trans-unit>
+                       <trans-unit id="label.ipAnonymization.mask.1">
+                               <source>Last byte for IPv4 / Interface ID for IPv6</source>
+                       </trans-unit>
+                       <trans-unit id="label.ipAnonymization.mask.2">
+                               <source>Last 2 bytes for IPv4 / Interface &amp; SLA ID for IPv6</source>
+                       </trans-unit>
                </body>
        </file>
 </xliff>
index 850cf5a..b0bdbf7 100644 (file)
@@ -100,6 +100,34 @@ if (!is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\T
     ];
 }
 
+// Save any previous option array for ip anonymization task
+// to temporary variable so it can be pre-populated by other
+// extensions and LocalConfiguration/AdditionalConfiguration
+$ipAnonymizeCollectionTaskOptions = isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\IpAnonymizationTask::class]['options'])
+    ? $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\IpAnonymizationTask::class]['options']
+    : [];
+$ipAnonymizeCollectionTaskOptions['tables'] = isset($ipAnonymizeCollectionTaskOptions['tables'])
+    ? $ipAnonymizeCollectionTaskOptions['tables']
+    : [];
+// Add ip anonymization task
+$GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\IpAnonymizationTask::class] = [
+    'extension' => 'scheduler',
+    'title' => 'LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:ipAnonymization.name',
+    'description' => 'LLL:EXT:scheduler/Resources/Private/Language/locallang.xlf:tableGarbageCollection.description',
+    'additionalFields' => \TYPO3\CMS\Scheduler\Task\IpAnonymizationAdditionalFieldProvider::class,
+    'options' => $ipAnonymizeCollectionTaskOptions
+];
+unset($ipAnonymizeCollectionTaskOptions);
+
+if (!isset($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\IpAnonymizationTask::class]['options']['tables']['sys_log'])
+    || !is_array($GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\IpAnonymizationTask::class]['options']['tables']['sys_log'])
+) {
+    $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\IpAnonymizationTask::class]['options']['tables']['sys_log'] = [
+        'dateField' => 'tstamp',
+        'ipField' => 'IP'
+    ];
+}
+
 // Add task for optimizing database tables
 $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['scheduler']['tasks'][\TYPO3\CMS\Scheduler\Task\OptimizeDatabaseTableTask::class] = [
     'extension' => 'scheduler',