[FEATURE] Add IP filtering 99/35499/2
authorFrancois Suter <francois@typo3.org>
Mon, 15 Dec 2014 21:38:37 +0000 (22:38 +0100)
committerFrancois Suter <francois@typo3.org>
Mon, 15 Dec 2014 21:41:19 +0000 (22:41 +0100)
Allow for filtering devlog entries on IP address upon logging.
Make it possible to reuse devIPmask global configuration value
if so desired.

Resolves: #2859
Releases: 3.0
Change-Id: I3d52a1984a9d683a99fd934536e9ff8c5730ac3c
Reviewed-on: http://review.typo3.org/35499
Reviewed-by: Francois Suter <francois@typo3.org>
Tested-by: Francois Suter <francois@typo3.org>
ChangeLog
Classes/Utility/Logger.php
Resources/Private/Language/locallang_configuration.xlf
Tests/Unit/Utility/LoggerTest.php
ext_conf_template.txt

index 219c7fa..0d7b40c 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2014-12-15 Francois Suter <typo3@cobweb.ch>
+
+       * Allow for IP filtering, resolves #2859
+
 2014-12-08 Francois Suter <typo3@cobweb.ch>
 
        * New table structure, new logging utility, database and file writer, resolves #63689
index 426a21c..10dac4e 100644 (file)
@@ -87,6 +87,8 @@ class Logger implements \TYPO3\CMS\Core\SingletonInterface {
                if (!$this->isLoggingEnabled) {
                        return;
                }
+               // Add IP address for validation
+               $logData['ip'] = GeneralUtility::getIndpEnv('REMOTE_ADDR');
                // If the log entry doesn't pass the basic filters, exit early doing nothing
                if (!$this->isEntryAccepted($logData)) {
                        return;
@@ -128,7 +130,7 @@ class Logger implements \TYPO3\CMS\Core\SingletonInterface {
                        (isset($GLOBALS['BE_USER']->user['uid'])) ? $GLOBALS['BE_USER']->user['uid'] : 0
                );
                $entry->setIp(
-                       GeneralUtility::getIndpEnv('REMOTE_ADDR')
+                       $logData['ip']
                );
 
                // Get information about the place where this method was called from
@@ -156,16 +158,34 @@ class Logger implements \TYPO3\CMS\Core\SingletonInterface {
         * @return bool
         */
        public function isEntryAccepted($logData) {
-               $accepted = TRUE;
                // Skip entry if severity is below minimum level
                if ($logData['severity'] < $this->extensionConfiguration['minimumLogLevel']) {
-                       $accepted = FALSE;
+                       return FALSE;
                }
                // Skip entry if key is in excluded list
                if (GeneralUtility::inList($this->extensionConfiguration['excludeKeys'], $logData['extKey'])) {
-                       $accepted = FALSE;
+                       return FALSE;
                }
-               return $accepted;
+               // Skip entry if referrer does not match IP mask
+               if (!$this->isIpAddressAccepted($logData['ip'])) {
+                       return FALSE;
+               }
+               return TRUE;
+       }
+
+       /**
+        * Checks if given IP address is acceptable.
+        *
+        * @param string $ipAddress IP address to check
+        * @return bool
+        */
+       public function isIpAddressAccepted($ipAddress) {
+               $ipFilter = $this->extensionConfiguration['ipFilter'];
+               // Re-use global IP mask if so defined
+               if (strtolower($ipFilter) === 'devipmask') {
+                       $ipFilter = $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'];
+               }
+               return GeneralUtility::cmpIP($ipAddress, $ipFilter);
        }
 
        /**
index 80284f6..35f3416 100644 (file)
@@ -27,6 +27,9 @@
                        <trans-unit id="excluded_keys">
                                <source>Excluded keys: Comma-separated list of (extension) keys whose entries should not be written to the logs.</source>
                        </trans-unit>
+                       <trans-unit id="ip_filter">
+                               <source>Restricted IP addresses: Only requests coming from the given IP addresses will be logged. An empty value will block everything, a "*" will allow everything. Use "devIPmask" (not case-sensitive) to reuse devIPmask value.</source>
+                       </trans-unit>
                        <trans-unit id="log_file_path">
                                <source>Path: Full path to the log file (may be relative to the web root, or use the EXT: syntax).</source>
                        </trans-unit>
index 0aa8a8f..de1d128 100644 (file)
@@ -32,7 +32,8 @@ class LoggerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
         */
        protected $testConfiguration = array(
                'minimumLogLevel' => 1,
-               'excludeKeys' => 'foo,bar'
+               'excludeKeys' => 'foo,bar',
+               'ipFilter' => '127.0.0.1,::1'
        );
 
        protected function setUp() {
@@ -55,32 +56,43 @@ class LoggerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                        $this->subject->isEntryAccepted(
                                array(
                                        'severity' => 2,
-                                       'extKey' => 'whatever'
+                                       'extKey' => 'whatever',
+                                       'ip' => '127.0.0.1'
                                )
                        )
                );
        }
 
-       public function wrongEntriesProvider() {
+       public function validEntriesProvider() {
                return array(
                        'Severity too low' => array(
                                array(
                                        'severity' => 0,
-                                       'extKey' => 'whatever'
+                                       'extKey' => 'whatever',
+                                       'ip' => '127.0.0.1'
                                )
                        ),
                        'Excluded extension key' => array(
                                array(
                                        'severity' => 3,
-                                       'extKey' => 'foo'
+                                       'extKey' => 'foo',
+                                       'ip' => '127.0.0.1'
+                               )
+                       ),
+                       'IP does not match' => array(
+                               array(
+                                       'severity' => 3,
+                                       'extKey' => 'whatever',
+                                       'ip' => '192.168.1.1'
                                )
                        )
                );
        }
 
        /**
+        * @param array $entry Log entry data
         * @test
-        * @dataProvider wrongEntriesProvider
+        * @dataProvider validEntriesProvider
         * @covers \Devlog\Devlog\Utility\Logger::isEntryAccepted
         */
        public function entryIsRefused($entry) {
@@ -91,6 +103,85 @@ class LoggerTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                );
        }
 
+       public function ipAddressesProvider() {
+               return array(
+                       'Valid IP v4' => array(
+                               '127.0.0.1',
+                               '',
+                               '',
+                               TRUE
+                       ),
+                       'Valid IP v6' => array(
+                               '::1',
+                               '',
+                               '',
+                               TRUE
+                       ),
+                       'Valid with devIPMask' => array(
+                               '192.168.1.67',
+                               'devIPMask',
+                               '192.168.1.*',
+                               TRUE
+                       ),
+                       'IP v4' => array(
+                               '192.168.1.1',
+                               '',
+                               '',
+                               FALSE
+                       ),
+                       'IP v6' => array(
+                               '2001:db8::ff00:42:8329',
+                               '',
+                               '',
+                               FALSE
+                       ),
+                       'devIPMask' => array(
+                               '80.58.212.14',
+                               'devIPMask',
+                               '192.168.1.*',
+                               FALSE
+                       )
+               );
+       }
+
+       /**
+        * @param string $testValue IP address to test
+        * @param string $configurationOverride Override IP filter in extension configuration
+        * @param string $devIpMask Value for overriding $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask']
+        * @param boolean $result TRUE or FALSE, depending on IP address validity
+        * @test
+        * @dataProvider ipAddressesProvider
+        * @covers \Devlog\Devlog\Utility\Logger::isIpAddressAccepted
+        */
+       public function isIpAddressValid($testValue, $configurationOverride, $devIpMask, $result) {
+               $savedIpMask = $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'];
+               // Override devIPmask
+               if (!empty($devIpMask)) {
+                       $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] = $devIpMask;
+               }
+               // Override extension configuration
+               if (!empty($configurationOverride)) {
+                       $specialConfiguration = $this->testConfiguration;
+                       $specialConfiguration['ipFilter'] = $configurationOverride;
+                       $this->subject->setExtensionConfiguration(
+                               $specialConfiguration
+                       );
+               }
+               // Perform the actual test
+               $this->assertSame(
+                       $result,
+                       $this->subject->isIpAddressAccepted(
+                               $testValue
+                       )
+               );
+               // Restore extension configuration
+               $this->subject->setExtensionConfiguration(
+                       $this->testConfiguration
+               );
+               // Restore devIPmask
+               $GLOBALS['TYPO3_CONF_VARS']['SYS']['devIPmask'] = $savedIpMask;
+       }
+
        /**
         * @test
         * @covers \Devlog\Devlog\Utility\Logger::getExtensionConfiguration
index 44a3d25..73b424d 100644 (file)
@@ -12,6 +12,9 @@ minimumLogLevel = -1
 # cat=general/filtering/; type=string; label=LLL:EXT:devlog/Resources/Private/Language/locallang_configuration.xlf:excluded_keys
 excludeKeys =
 
+# cat=general/filtering/; type=string; label=LLL:EXT:devlog/Resources/Private/Language/locallang_configuration.xlf:ip_filter
+ipFilter =
+
 # cat=general/display/; type=integer; label=Autorefresh frequency: Set the number of seconds between each refresh, when using the autorefresh feature
 refreshFrequency = 2