[BUGFIX] Apc Cache backend has side effects 70/17470/2
authorDaniel Pötzinger <poetzinger@aoemedia.de>
Sun, 17 Jun 2012 12:09:41 +0000 (14:09 +0200)
committerHelmut Hummel <helmut.hummel@typo3.org>
Fri, 11 Jan 2013 22:05:36 +0000 (23:05 +0100)
If two caches use the APC cache backend,
one cache can write and get entries from the
 other by just using the same key!
So the backend should also use cache id as prefix

Change-Id: I46f7a04386427efb2e1753bef6dfa588508d076c
Fixes: #38135
Releases: 4.5,4.6,4.7,6.0
Reviewed-on: https://review.typo3.org/17470
Reviewed-by: Helmut Hummel
Tested-by: Helmut Hummel
typo3/sysext/core/Classes/Cache/Backend/ApcBackend.php
typo3/sysext/core/Tests/Unit/Cache/Backend/ApcBackendTest.php

index 39324f8..7f45fca 100644 (file)
@@ -61,6 +61,24 @@ class ApcBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implement
        protected $identifierPrefix;
 
        /**
+        * Set the cache identifier prefix.
+        *
+        * @param string $identifierPrefix
+        */
+       protected function setIdentifierPrefix($identifierPrefix) {
+               $this->identifierPrefix = $identifierPrefix;
+       }
+
+       /**
+        * Retrieves the cache identifier prefix.
+        *
+        * @return string
+        */
+       protected function getIdentifierPrefix() {
+               return $this->identifierPrefix;
+       }
+
+       /**
         * Constructs this backend
         *
         * @param string $context FLOW3's application context
@@ -82,9 +100,28 @@ class ApcBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implement
         */
        public function setCache(\TYPO3\CMS\Core\Cache\Frontend\FrontendInterface $cache) {
                parent::setCache($cache);
-               $processUser = extension_loaded('posix') ? posix_getpwuid(posix_geteuid()) : array('name' => 'default');
-               $pathHash = \TYPO3\CMS\Core\Utility\GeneralUtility::shortMD5(PATH_site . $processUser['name'] . $this->context, 12);
-               $this->identifierPrefix = 'TYPO3_' . $pathHash;
+               $processUser = $this->getCurrentUserData();
+               $pathHash = \TYPO3\CMS\Core\Utility\GeneralUtility::shortMD5($this->getPathSite() . $processUser['name'] . $this->context . $cache->getIdentifier(), 12);
+               $this->setIdentifierPrefix('TYPO3_' . $pathHash);
+       }
+
+       /**
+        * Returns the current user data with posix_getpwuid or a default structure when
+        * posix_getpwuid is not available.
+        *
+        * @return array
+        */
+       protected function getCurrentUserData() {
+               return extension_loaded('posix') ? posix_getpwuid(posix_geteuid()) : array('name' => 'default');
+       }
+
+       /**
+        * Returns the PATH_site constant.
+        *
+        * @return string
+        */
+       protected function getPathSite() {
+               return PATH_site;
        }
 
        /**
@@ -108,7 +145,7 @@ class ApcBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implement
                }
                $tags[] = '%APCBE%' . $this->cacheIdentifier;
                $expiration = $lifetime !== NULL ? $lifetime : $this->defaultLifetime;
-               $success = apc_store($this->identifierPrefix . $entryIdentifier, $data, $expiration);
+               $success = apc_store($this->getIdentifierPrefix() . $entryIdentifier, $data, $expiration);
                if ($success === TRUE) {
                        $this->removeIdentifierFromAllTags($entryIdentifier);
                        $this->addIdentifierToTags($entryIdentifier, $tags);
@@ -126,7 +163,7 @@ class ApcBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implement
         */
        public function get($entryIdentifier) {
                $success = FALSE;
-               $value = apc_fetch($this->identifierPrefix . $entryIdentifier, $success);
+               $value = apc_fetch($this->getIdentifierPrefix() . $entryIdentifier, $success);
                return $success ? $value : $success;
        }
 
@@ -139,7 +176,7 @@ class ApcBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implement
         */
        public function has($entryIdentifier) {
                $success = FALSE;
-               apc_fetch($this->identifierPrefix . $entryIdentifier, $success);
+               apc_fetch($this->getIdentifierPrefix() . $entryIdentifier, $success);
                return $success;
        }
 
@@ -154,7 +191,7 @@ class ApcBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implement
         */
        public function remove($entryIdentifier) {
                $this->removeIdentifierFromAllTags($entryIdentifier);
-               return apc_delete($this->identifierPrefix . $entryIdentifier);
+               return apc_delete($this->getIdentifierPrefix() . $entryIdentifier);
        }
 
        /**
@@ -167,7 +204,7 @@ class ApcBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implement
         */
        public function findIdentifiersByTag($tag) {
                $success = FALSE;
-               $identifiers = apc_fetch($this->identifierPrefix . 'tag_' . $tag, $success);
+               $identifiers = apc_fetch($this->getIdentifierPrefix() . 'tag_' . $tag, $success);
                if ($success === FALSE) {
                        return array();
                } else {
@@ -184,7 +221,7 @@ class ApcBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implement
         */
        protected function findTagsByIdentifier($identifier) {
                $success = FALSE;
-               $tags = apc_fetch($this->identifierPrefix . 'ident_' . $identifier, $success);
+               $tags = apc_fetch($this->getIdentifierPrefix() . 'ident_' . $identifier, $success);
                return $success ? (array) $tags : array();
        }
 
@@ -229,12 +266,12 @@ class ApcBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implement
                        $identifiers = $this->findIdentifiersByTag($tag);
                        if (array_search($entryIdentifier, $identifiers) === FALSE) {
                                $identifiers[] = $entryIdentifier;
-                               apc_store($this->identifierPrefix . 'tag_' . $tag, $identifiers);
+                               apc_store($this->getIdentifierPrefix() . 'tag_' . $tag, $identifiers);
                        }
                        // Update identifier-to-tag index
                        $existingTags = $this->findTagsByIdentifier($entryIdentifier);
                        if (array_search($entryIdentifier, $existingTags) === FALSE) {
-                               apc_store($this->identifierPrefix . 'ident_' . $entryIdentifier, array_merge($existingTags, $tags));
+                               apc_store($this->getIdentifierPrefix() . 'ident_' . $entryIdentifier, array_merge($existingTags, $tags));
                        }
                }
        }
@@ -259,14 +296,14 @@ class ApcBackend extends \TYPO3\CMS\Core\Cache\Backend\AbstractBackend implement
                        if (($key = array_search($entryIdentifier, $identifiers)) !== FALSE) {
                                unset($identifiers[$key]);
                                if (count($identifiers)) {
-                                       apc_store($this->identifierPrefix . 'tag_' . $tag, $identifiers);
+                                       apc_store($this->getIdentifierPrefix() . 'tag_' . $tag, $identifiers);
                                } else {
-                                       apc_delete($this->identifierPrefix . 'tag_' . $tag);
+                                       apc_delete($this->getIdentifierPrefix() . 'tag_' . $tag);
                                }
                        }
                }
                // Clear reverse tag index for this identifier
-               apc_delete($this->identifierPrefix . 'ident_' . $entryIdentifier);
+               apc_delete($this->getIdentifierPrefix() . 'ident_' . $entryIdentifier);
        }
 
        /**
index 871ab24..06b082b 100644 (file)
@@ -25,7 +25,10 @@ namespace TYPO3\CMS\Core\Tests\Unit\Cache\Backend;
  ***************************************************************/
 
 /**
- * Testcase for the APC cache backend
+ * Testcase for the APC cache backend.
+ *
+ * NOTE: If you want to execute these tests you need to enable apc in
+ * cli context (apc.enable_cli = 1)
  *
  * This file is a backport from FLOW3
  *
@@ -139,6 +142,33 @@ class ApcBackendTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
        /**
         * @test
         */
+       public function setCacheIsSettingIdentifierPrefixWithCacheIdentifier() {
+               $cacheMock = $this->getMock('TYPO3\\CMS\\Core\\Cache\\Frontend\\FrontendInterface', array(), array(), '', FALSE);
+               $cacheMock->expects($this->any())->method('getIdentifier')->will($this->returnValue(
+                       'testidentifier'
+               ));
+
+               /** @var $backendMock \TYPO3\CMS\Core\Cache\Backend\ApcBackend */
+               $backendMock = $this->getMock('TYPO3\\CMS\\Core\\Cache\\Backend\\ApcBackend',
+                                                                               array('setIdentifierPrefix','getCurrentUserData','getPathSite'),
+                                                                               array('testcontext'));
+
+               $backendMock->expects($this->once())->method('getCurrentUserData')->will(
+                       $this->returnValue(array('name' => 'testname'))
+               );
+
+               $backendMock->expects($this->once())->method('getPathSite')->will(
+                       $this->returnValue('testpath')
+               );
+
+               $expectedIdentifier = 'TYPO3_' . \TYPO3\CMS\Core\Utility\GeneralUtility::shortMD5('testpath' . 'testname' . 'testcontext' . 'testidentifier', 12);
+               $backendMock->expects($this->once())->method('setIdentifierPrefix')->with($expectedIdentifier);
+               $backendMock->setCache($cacheMock);
+       }
+
+       /**
+        * @test
+        */
        public function hasReturnsFalseIfTheEntryDoesntExist() {
                $backend = $this->setUpBackend();
                $identifier = 'NonExistingIdentifier' . md5(uniqid(mt_rand(), TRUE));