implementing key/value store of Persistance in Database storage

This commit is contained in:
El RIDO 2021-06-09 07:47:40 +02:00
parent 7901ec74a7
commit a203e6322b
No known key found for this signature in database
GPG Key ID: 0F5C940A6BD81F92
8 changed files with 86 additions and 65 deletions

View File

@ -11,6 +11,7 @@
* CHANGED: Upgrading libraries to: random_compat 2.0.20 * CHANGED: Upgrading libraries to: random_compat 2.0.20
* CHANGED: Removed automatic `.ini` configuration file migration (#808) * CHANGED: Removed automatic `.ini` configuration file migration (#808)
* CHANGED: Removed configurable `dir` for `traffic` & `purge` limiters (#419) * 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)** * **1.3.5 (2021-04-05)**
* ADDED: Translation for Hebrew, Lithuanian, Indonesian and Catalan * ADDED: Translation for Hebrew, Lithuanian, Indonesian and Catalan
* ADDED: Make the project info configurable (#681) * ADDED: Make the project info configurable (#681)

View File

@ -28,6 +28,15 @@ abstract class AbstractData
*/ */
protected static $_instance = null; protected static $_instance = null;
/**
* cache for the traffic limiter
*
* @access private
* @static
* @var array
*/
protected static $_traffic_limiter_cache = array();
/** /**
* Enforce singleton, disable constructor * Enforce singleton, disable constructor
* *
@ -138,7 +147,16 @@ abstract class AbstractData
* @param int $time * @param int $time
* @return void * @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. * Save a value.

View File

@ -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. * Save a value.
* *
@ -451,20 +434,19 @@ class Database extends AbstractData
*/ */
public function setValue($value, $namespace, $key = '') public function setValue($value, $namespace, $key = '')
{ {
switch ($namespace) { if ($namespace === 'traffic_limiter') {
case 'purge_limiter': self::$_traffic_limiter_cache[$key] = $value;
; try {
break; $value = Json::encode(self::$_traffic_limiter_cache);
case 'salt': } catch (Exception $e) {
;
break;
case 'traffic_limiter':
;
break;
default:
return false; 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 = '') 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') . 'SELECT value FROM ' . self::_sanitizeIdentifier('config') .
' WHERE id = ?', array($key), true ' WHERE id = ?', array($key), true
); );
return $row['value']; return $row ? $row['value']: '';
} }
/** /**

View File

@ -38,15 +38,6 @@ class Filesystem extends AbstractData
*/ */
private static $_path = 'data'; private static $_path = 'data';
/**
* cache for the traffic limiter
*
* @access private
* @static
* @var array
*/
private static $_traffic_limiter_cache = array();
/** /**
* get instance of singleton * 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. * Save a value.
* *

View File

@ -227,10 +227,11 @@ class GoogleCloudStorage extends AbstractData
*/ */
public function purgeValues($namespace, $time) public function purgeValues($namespace, $time)
{ {
switch ($namespace) { if ($namespace === 'traffic_limiter') {
case '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
break; // self::$_traffic_limiter_cache in a similar way as the other
// implementations.
} }
} }

View File

@ -17,6 +17,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
/* Setup Routine */ /* Setup Routine */
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data'; $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
$this->_data = Filesystem::getInstance(array('dir' => $this->_path)); $this->_data = Filesystem::getInstance(array('dir' => $this->_path));
ServerSalt::setStore($this->_data);
TrafficLimiter::setStore($this->_data); TrafficLimiter::setStore($this->_data);
$this->reset(); $this->reset();
} }

View File

@ -1,6 +1,8 @@
<?php <?php
use PrivateBin\Data\Database; use PrivateBin\Data\Database;
use PrivateBin\Persistence\ServerSalt;
use PrivateBin\Persistence\TrafficLimiter;
require_once 'ControllerTest.php'; require_once 'ControllerTest.php';
@ -24,6 +26,8 @@ class ControllerWithDbTest extends ControllerTest
} }
$this->_options['dsn'] = 'sqlite:' . $this->_path . DIRECTORY_SEPARATOR . 'tst.sq3'; $this->_options['dsn'] = 'sqlite:' . $this->_path . DIRECTORY_SEPARATOR . 'tst.sq3';
$this->_data = Database::getInstance($this->_options); $this->_data = Database::getInstance($this->_options);
ServerSalt::setStore($this->_data);
TrafficLimiter::setStore($this->_data);
$this->reset(); $this->reset();
} }

View File

@ -2,6 +2,8 @@
use PrivateBin\Controller; use PrivateBin\Controller;
use PrivateBin\Data\Database; use PrivateBin\Data\Database;
use PrivateBin\Data\Filesystem;
use PrivateBin\Persistence\ServerSalt;
class DatabaseTest extends PHPUnit_Framework_TestCase 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() public function testDatabaseBasedDataStoreWorks()
{ {
$this->_model->delete(Helper::getPasteId()); $this->_model->delete(Helper::getPasteId());