From a203e6322b16823b126f0538313d79855ced7560 Mon Sep 17 00:00:00 2001 From: El RIDO Date: Wed, 9 Jun 2021 07:47:40 +0200 Subject: [PATCH] implementing key/value store of Persistance in Database storage --- CHANGELOG.md | 1 + lib/Data/AbstractData.php | 20 +++++++++- lib/Data/Database.php | 71 +++++++++++++++++++-------------- lib/Data/Filesystem.php | 30 -------------- lib/Data/GoogleCloudStorage.php | 9 +++-- tst/ControllerTest.php | 1 + tst/ControllerWithDbTest.php | 4 ++ tst/Data/DatabaseTest.php | 15 +++++++ 8 files changed, 86 insertions(+), 65 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c546bd1..d964a57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ * CHANGED: Upgrading libraries to: random_compat 2.0.20 * CHANGED: Removed automatic `.ini` configuration file migration (#808) * CHANGED: Removed configurable `dir` for `traffic` & `purge` limiters (#419) + * CHANGED: Server salt, traffic and purge limiter now stored in the storage backend (#419) * **1.3.5 (2021-04-05)** * ADDED: Translation for Hebrew, Lithuanian, Indonesian and Catalan * ADDED: Make the project info configurable (#681) diff --git a/lib/Data/AbstractData.php b/lib/Data/AbstractData.php index 3de4bab..6f2dee5 100644 --- a/lib/Data/AbstractData.php +++ b/lib/Data/AbstractData.php @@ -28,6 +28,15 @@ abstract class AbstractData */ protected static $_instance = null; + /** + * cache for the traffic limiter + * + * @access private + * @static + * @var array + */ + protected static $_traffic_limiter_cache = array(); + /** * Enforce singleton, disable constructor * @@ -138,7 +147,16 @@ abstract class AbstractData * @param int $time * @return void */ - abstract public function purgeValues($namespace, $time); + public function purgeValues($namespace, $time) + { + if ($namespace === 'traffic_limiter') { + foreach (self::$_traffic_limiter_cache as $key => $last_access) { + if ($last_access <= $time) { + unset(self::$_traffic_limiter_cache[$key]); + } + } + } + } /** * Save a value. diff --git a/lib/Data/Database.php b/lib/Data/Database.php index 2a1bbcb..ca0cc79 100644 --- a/lib/Data/Database.php +++ b/lib/Data/Database.php @@ -423,23 +423,6 @@ class Database extends AbstractData ); } - /** - * Purge outdated entries. - * - * @access public - * @param string $namespace - * @param int $time - * @return void - */ - public function purgeValues($namespace, $time) - { - switch ($namespace) { - case 'traffic_limiter': - ; - break; - } - } - /** * Save a value. * @@ -451,20 +434,19 @@ class Database extends AbstractData */ public function setValue($value, $namespace, $key = '') { - switch ($namespace) { - case 'purge_limiter': - ; - break; - case 'salt': - ; - break; - case 'traffic_limiter': - ; - break; - default: + if ($namespace === 'traffic_limiter') { + self::$_traffic_limiter_cache[$key] = $value; + try { + $value = Json::encode(self::$_traffic_limiter_cache); + } catch (Exception $e) { return false; - break; + } } + return self::_exec( + 'UPDATE ' . self::_sanitizeIdentifier('config') . + ' SET value = ? WHERE id = ?', + array($value, strtoupper($namespace)) + ); } /** @@ -477,7 +459,36 @@ class Database extends AbstractData */ public function getValue($namespace, $key = '') { + $configKey = strtoupper($namespace); + $value = $this->_getConfig($configKey); + if ($value === '') { + // initialize the row, so that setValue can rely on UPDATE queries + self::_exec( + 'INSERT INTO ' . self::_sanitizeIdentifier('config') . + ' VALUES(?,?)', + array($configKey, '') + ); + // migrate filesystem based salt into database + $file = 'data' . DIRECTORY_SEPARATOR . 'salt.php'; + if ($namespace === 'salt' && is_readable($file)) { + $value = Filesystem::getInstance(array('dir' => 'data'))->getValue('salt'); + $this->setValue($value, 'salt'); + @unlink($file); + return $value; + } + } + if ($value && $namespace === 'traffic_limiter') { + try { + self::$_traffic_limiter_cache = Json::decode($value); + } catch (Exception $e) { + self::$_traffic_limiter_cache = array(); + } + if (array_key_exists($key, self::$_traffic_limiter_cache)) { + return self::$_traffic_limiter_cache[$key]; + } + } + return (string) $value; } /** @@ -629,7 +640,7 @@ class Database extends AbstractData 'SELECT value FROM ' . self::_sanitizeIdentifier('config') . ' WHERE id = ?', array($key), true ); - return $row['value']; + return $row ? $row['value']: ''; } /** diff --git a/lib/Data/Filesystem.php b/lib/Data/Filesystem.php index 8b443ad..384c72d 100644 --- a/lib/Data/Filesystem.php +++ b/lib/Data/Filesystem.php @@ -38,15 +38,6 @@ class Filesystem extends AbstractData */ private static $_path = 'data'; - /** - * cache for the traffic limiter - * - * @access private - * @static - * @var array - */ - private static $_traffic_limiter_cache = array(); - /** * get instance of singleton * @@ -249,27 +240,6 @@ class Filesystem extends AbstractData ); } - /** - * Purge outdated entries. - * - * @access public - * @param string $namespace - * @param int $time - * @return void - */ - public function purgeValues($namespace, $time) - { - switch ($namespace) { - case 'traffic_limiter': - foreach (self::$_traffic_limiter_cache as $key => $last_access) { - if ($last_access <= $time) { - unset(self::$_traffic_limiter_cache[$key]); - } - } - break; - } - } - /** * Save a value. * diff --git a/lib/Data/GoogleCloudStorage.php b/lib/Data/GoogleCloudStorage.php index f6fc489..2521957 100644 --- a/lib/Data/GoogleCloudStorage.php +++ b/lib/Data/GoogleCloudStorage.php @@ -227,10 +227,11 @@ class GoogleCloudStorage extends AbstractData */ public function purgeValues($namespace, $time) { - switch ($namespace) { - case 'traffic_limiter': - ; - break; + if ($namespace === 'traffic_limiter') { + // TODO implement purging of keys in namespace that are <= $time + // if GCS has no easy way to iterate all keys, consider using the + // self::$_traffic_limiter_cache in a similar way as the other + // implementations. } } diff --git a/tst/ControllerTest.php b/tst/ControllerTest.php index 165ae08..0aa7d79 100644 --- a/tst/ControllerTest.php +++ b/tst/ControllerTest.php @@ -17,6 +17,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase /* Setup Routine */ $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data'; $this->_data = Filesystem::getInstance(array('dir' => $this->_path)); + ServerSalt::setStore($this->_data); TrafficLimiter::setStore($this->_data); $this->reset(); } diff --git a/tst/ControllerWithDbTest.php b/tst/ControllerWithDbTest.php index 90dee92..bc8cd7b 100644 --- a/tst/ControllerWithDbTest.php +++ b/tst/ControllerWithDbTest.php @@ -1,6 +1,8 @@ _options['dsn'] = 'sqlite:' . $this->_path . DIRECTORY_SEPARATOR . 'tst.sq3'; $this->_data = Database::getInstance($this->_options); + ServerSalt::setStore($this->_data); + TrafficLimiter::setStore($this->_data); $this->reset(); } diff --git a/tst/Data/DatabaseTest.php b/tst/Data/DatabaseTest.php index 0d48eb6..f9cd265 100644 --- a/tst/Data/DatabaseTest.php +++ b/tst/Data/DatabaseTest.php @@ -2,6 +2,8 @@ use PrivateBin\Controller; use PrivateBin\Data\Database; +use PrivateBin\Data\Filesystem; +use PrivateBin\Persistence\ServerSalt; class DatabaseTest extends PHPUnit_Framework_TestCase { @@ -31,6 +33,19 @@ class DatabaseTest extends PHPUnit_Framework_TestCase } } + public function testSaltMigration() + { + ServerSalt::setStore(Filesystem::getInstance(array('dir' => 'data'))); + $salt = ServerSalt::get(); + $file = 'data' . DIRECTORY_SEPARATOR . 'salt.php'; + $this->assertFileExists($file, 'ServerSalt got initialized and stored on disk'); + $this->assertNotEquals($salt, ''); + ServerSalt::setStore($this->_model); + ServerSalt::get(); + $this->assertFileNotExists($file, 'legacy ServerSalt got removed'); + $this->assertEquals($salt, ServerSalt::get(), 'ServerSalt got preserved & migrated'); + } + public function testDatabaseBasedDataStoreWorks() { $this->_model->delete(Helper::getPasteId());