folding Persistance\ServerSalt into Data\Filesystem

This commit is contained in:
El RIDO 2021-06-08 22:01:29 +02:00
parent b5a6ce323e
commit 7901ec74a7
No known key found for this signature in database
GPG key ID: 0F5C940A6BD81F92
12 changed files with 80 additions and 196 deletions

View file

@ -197,6 +197,7 @@ class Controller
{ {
try { try {
// Ensure last paste from visitors IP address was more than configured amount of seconds ago. // Ensure last paste from visitors IP address was more than configured amount of seconds ago.
ServerSalt::setStore($this->_model->getStore());
TrafficLimiter::setConfiguration($this->_conf); TrafficLimiter::setConfiguration($this->_conf);
TrafficLimiter::setStore($this->_model->getStore()); TrafficLimiter::setStore($this->_model->getStore());
if (!TrafficLimiter::canPass()) { if (!TrafficLimiter::canPass()) {

View file

@ -287,17 +287,17 @@ class Filesystem extends AbstractData
self::$_path . DIRECTORY_SEPARATOR . 'purge_limiter.php', self::$_path . DIRECTORY_SEPARATOR . 'purge_limiter.php',
'<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . $value . ';' '<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . $value . ';'
); );
break;
case 'salt': case 'salt':
; return self::_storeString(
break; self::$_path . DIRECTORY_SEPARATOR . 'salt.php',
'<?php # |' . $value . '|'
);
case 'traffic_limiter': case 'traffic_limiter':
self::$_traffic_limiter_cache[$key] = $value; self::$_traffic_limiter_cache[$key] = $value;
return self::_storeString( return self::_storeString(
self::$_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php', self::$_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php',
'<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . var_export(self::$_traffic_limiter_cache, true) . ';' '<?php' . PHP_EOL . '$GLOBALS[\'traffic_limiter\'] = ' . var_export(self::$_traffic_limiter_cache, true) . ';'
); );
break;
} }
return false; return false;
} }
@ -315,17 +315,23 @@ class Filesystem extends AbstractData
switch ($namespace) { switch ($namespace) {
case 'purge_limiter': case 'purge_limiter':
$file = self::$_path . DIRECTORY_SEPARATOR . 'purge_limiter.php'; $file = self::$_path . DIRECTORY_SEPARATOR . 'purge_limiter.php';
if (is_file($file)) { if (is_readable($file)) {
require $file; require $file;
return $GLOBALS['purge_limiter']; return $GLOBALS['purge_limiter'];
} }
break; break;
case 'salt': case 'salt':
; $file = self::$_path . DIRECTORY_SEPARATOR . 'salt.php';
if (is_readable($file)) {
$items = explode('|', file_get_contents($file));
if (is_array($items) && count($items) == 3) {
return $items[1];
}
}
break; break;
case 'traffic_limiter': case 'traffic_limiter':
$file = self::$_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php'; $file = self::$_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php';
if (is_file($file)) { if (is_readable($file)) {
require $file; require $file;
self::$_traffic_limiter_cache = $GLOBALS['traffic_limiter']; self::$_traffic_limiter_cache = $GLOBALS['traffic_limiter'];
if (array_key_exists($key, self::$_traffic_limiter_cache)) { if (array_key_exists($key, self::$_traffic_limiter_cache)) {

View file

@ -93,7 +93,7 @@ class Paste extends AbstractModel
} }
$this->_data['meta']['created'] = time(); $this->_data['meta']['created'] = time();
$this->_data['meta']['salt'] = serversalt::generate(); $this->_data['meta']['salt'] = ServerSalt::generate();
// store paste // store paste
if ( if (

View file

@ -12,7 +12,6 @@
namespace PrivateBin\Persistence; namespace PrivateBin\Persistence;
use Exception;
use PrivateBin\Data\AbstractData; use PrivateBin\Data\AbstractData;
/** /**
@ -22,15 +21,6 @@ use PrivateBin\Data\AbstractData;
*/ */
abstract class AbstractPersistence abstract class AbstractPersistence
{ {
/**
* path in which to persist something
*
* @access private
* @static
* @var string
*/
private static $_path = 'data';
/** /**
* data storage to use to persist something * data storage to use to persist something
* *
@ -40,18 +30,6 @@ abstract class AbstractPersistence
*/ */
protected static $_store; protected static $_store;
/**
* set the path
*
* @access public
* @static
* @param string $path
*/
public static function setPath($path)
{
self::$_path = $path;
}
/** /**
* set the path * set the path
* *
@ -63,95 +41,4 @@ abstract class AbstractPersistence
{ {
self::$_store = $store; self::$_store = $store;
} }
/**
* get the path
*
* @access public
* @static
* @param string $filename
* @return string
*/
public static function getPath($filename = null)
{
if (strlen($filename)) {
return self::$_path . DIRECTORY_SEPARATOR . $filename;
} else {
return self::$_path;
}
}
/**
* checks if the file exists
*
* @access protected
* @static
* @param string $filename
* @return bool
*/
protected static function _exists($filename)
{
self::_initialize();
return is_file(self::$_path . DIRECTORY_SEPARATOR . $filename);
}
/**
* prepares path for storage
*
* @access protected
* @static
* @throws Exception
*/
protected static function _initialize()
{
// Create storage directory if it does not exist.
if (!is_dir(self::$_path)) {
if (!@mkdir(self::$_path, 0700)) {
throw new Exception('unable to create directory ' . self::$_path, 10);
}
}
$file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess';
if (!is_file($file)) {
$writtenBytes = 0;
if ($fileCreated = @touch($file)) {
$writtenBytes = @file_put_contents(
$file,
'Require all denied' . PHP_EOL,
LOCK_EX
);
}
if ($fileCreated === false || $writtenBytes === false || $writtenBytes < 19) {
throw new Exception('unable to write to file ' . $file, 11);
}
}
}
/**
* store the data
*
* @access protected
* @static
* @param string $filename
* @param string $data
* @throws Exception
* @return string
*/
protected static function _store($filename, $data)
{
self::_initialize();
$file = self::$_path . DIRECTORY_SEPARATOR . $filename;
$fileCreated = true;
$writtenBytes = 0;
if (!is_file($file)) {
$fileCreated = @touch($file);
}
if ($fileCreated) {
$writtenBytes = @file_put_contents($file, $data, LOCK_EX);
}
if ($fileCreated === false || $writtenBytes === false || $writtenBytes < strlen($data)) {
throw new Exception('unable to write to file ' . $file, 13);
}
@chmod($file, 0640); // protect file access
return $file;
}
} }

View file

@ -13,6 +13,7 @@
namespace PrivateBin\Persistence; namespace PrivateBin\Persistence;
use Exception; use Exception;
use PrivateBin\Data\AbstractData;
/** /**
* ServerSalt * ServerSalt
@ -71,20 +72,12 @@ class ServerSalt extends AbstractPersistence
return self::$_salt; return self::$_salt;
} }
if (self::_exists(self::$_file)) { $salt = self::$_store->getValue('salt');
if (is_readable(self::getPath(self::$_file))) { if ($salt) {
$items = explode('|', file_get_contents(self::getPath(self::$_file))); self::$_salt = $salt;
}
if (!isset($items) || !is_array($items) || count($items) != 3) {
throw new Exception('unable to read file ' . self::getPath(self::$_file), 20);
}
self::$_salt = $items[1];
} else { } else {
self::$_salt = self::generate(); self::$_salt = self::generate();
self::_store( self::$_store->setValue(self::$_salt, 'salt');
self::$_file,
'<?php # |' . self::$_salt . '|'
);
} }
return self::$_salt; return self::$_salt;
} }
@ -94,11 +87,11 @@ class ServerSalt extends AbstractPersistence
* *
* @access public * @access public
* @static * @static
* @param string $path * @param AbstractData $store
*/ */
public static function setPath($path) public static function setStore(AbstractData $store)
{ {
self::$_salt = ''; self::$_salt = '';
parent::setPath($path); parent::setStore($store);
} }
} }

View file

@ -172,7 +172,7 @@ class TrafficLimiter extends AbstractPersistence
// this hash is used as an array key, hence a shorter algo is used // this hash is used as an array key, hence a shorter algo is used
$hash = self::getHash('sha256'); $hash = self::getHash('sha256');
$now = time(); $now = time();
$tl = self::$_store->getValue('traffic_limiter', $hash); $tl = (int) self::$_store->getValue('traffic_limiter', $hash);
self::$_store->purgeValues('traffic_limiter', $now - self::$_limit); self::$_store->purgeValues('traffic_limiter', $now - self::$_limit);
if ($tl > 0 && ($tl + self::$_limit >= $now)) { if ($tl > 0 && ($tl + self::$_limit >= $now)) {
$result = false; $result = false;
@ -180,7 +180,7 @@ class TrafficLimiter extends AbstractPersistence
$tl = time(); $tl = time();
$result = true; $result = true;
} }
self::$_store->setValue((string) $tl, 'traffic_limiter'); self::$_store->setValue((string) $tl, 'traffic_limiter', $hash);
return $result; return $result;
} }
} }

View file

@ -125,28 +125,6 @@ class ControllerTest extends PHPUnit_Framework_TestCase
); );
} }
/**
* @runInSeparateProcess
*/
public function testHtaccess()
{
$htaccess = $this->_path . DIRECTORY_SEPARATOR . '.htaccess';
@unlink($htaccess);
$paste = Helper::getPasteJson();
$file = tempnam(sys_get_temp_dir(), 'FOO');
file_put_contents($file, $paste);
Request::setInputStream($file);
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
$_SERVER['REQUEST_METHOD'] = 'POST';
$_SERVER['REMOTE_ADDR'] = '::1';
ob_start();
new Controller;
ob_end_clean();
$this->assertFileExists($htaccess, 'htaccess recreated');
}
/** /**
* @expectedException Exception * @expectedException Exception
* @expectedExceptionCode 2 * @expectedExceptionCode 2

View file

@ -16,7 +16,7 @@ class JsonApiTest 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->_model = Filesystem::getInstance(array('dir' => $this->_path)); $this->_model = Filesystem::getInstance(array('dir' => $this->_path));
ServerSalt::setPath($this->_path); ServerSalt::setStore($this->_model);
$_POST = array(); $_POST = array();
$_GET = array(); $_GET = array();

View file

@ -25,7 +25,6 @@ class ModelTest extends PHPUnit_Framework_TestCase
if (!is_dir($this->_path)) { if (!is_dir($this->_path)) {
mkdir($this->_path); mkdir($this->_path);
} }
ServerSalt::setPath($this->_path);
$options = parse_ini_file(CONF_SAMPLE, true); $options = parse_ini_file(CONF_SAMPLE, true);
$options['purge']['limit'] = 0; $options['purge']['limit'] = 0;
$options['model'] = array( $options['model'] = array(
@ -39,6 +38,7 @@ class ModelTest extends PHPUnit_Framework_TestCase
); );
Helper::confBackup(); Helper::confBackup();
Helper::createIniFile(CONF, $options); Helper::createIniFile(CONF, $options);
ServerSalt::setStore(Database::getInstance($options['model_options']));
$this->_conf = new Configuration; $this->_conf = new Configuration;
$this->_model = new Model($this->_conf); $this->_model = new Model($this->_conf);
$_SERVER['REMOTE_ADDR'] = '::1'; $_SERVER['REMOTE_ADDR'] = '::1';

View file

@ -1,5 +1,6 @@
<?php <?php
use PrivateBin\Data\Filesystem;
use PrivateBin\Persistence\ServerSalt; use PrivateBin\Persistence\ServerSalt;
class ServerSaltTest extends PHPUnit_Framework_TestCase class ServerSaltTest extends PHPUnit_Framework_TestCase
@ -19,7 +20,9 @@ class ServerSaltTest extends PHPUnit_Framework_TestCase
if (!is_dir($this->_path)) { if (!is_dir($this->_path)) {
mkdir($this->_path); mkdir($this->_path);
} }
ServerSalt::setPath($this->_path); ServerSalt::setStore(
Filesystem::getInstance(array('dir' => $this->_path))
);
$this->_otherPath = $this->_path . DIRECTORY_SEPARATOR . 'foo'; $this->_otherPath = $this->_path . DIRECTORY_SEPARATOR . 'foo';
@ -40,46 +43,46 @@ class ServerSaltTest extends PHPUnit_Framework_TestCase
public function testGeneration() public function testGeneration()
{ {
// generating new salt // generating new salt
ServerSalt::setPath($this->_path); ServerSalt::setStore(
Filesystem::getInstance(array('dir' => $this->_path))
);
$salt = ServerSalt::get(); $salt = ServerSalt::get();
// try setting a different path and resetting it // try setting a different path and resetting it
ServerSalt::setPath($this->_otherPath); ServerSalt::setStore(
Filesystem::getInstance(array('dir' => $this->_otherPath))
);
$this->assertNotEquals($salt, ServerSalt::get()); $this->assertNotEquals($salt, ServerSalt::get());
ServerSalt::setPath($this->_path); ServerSalt::setStore(
Filesystem::getInstance(array('dir' => $this->_path))
);
$this->assertEquals($salt, ServerSalt::get()); $this->assertEquals($salt, ServerSalt::get());
} }
/**
* @expectedException Exception
* @expectedExceptionCode 11
*/
public function testPathShenanigans() public function testPathShenanigans()
{ {
// try setting an invalid path // try setting an invalid path
chmod($this->_invalidPath, 0000); chmod($this->_invalidPath, 0000);
ServerSalt::setPath($this->_invalidPath); $store = Filesystem::getInstance(array('dir' => $this->_invalidPath));
ServerSalt::get(); ServerSalt::setStore($store);
$salt = ServerSalt::get();
ServerSalt::setStore($store);
$this->assertNotEquals($salt, ServerSalt::get());
} }
/**
* @expectedException Exception
* @expectedExceptionCode 20
*/
public function testFileRead() public function testFileRead()
{ {
// try setting an invalid file // try setting an invalid file
chmod($this->_invalidPath, 0700); chmod($this->_invalidPath, 0700);
file_put_contents($this->_invalidFile, ''); file_put_contents($this->_invalidFile, '');
chmod($this->_invalidFile, 0000); chmod($this->_invalidFile, 0000);
ServerSalt::setPath($this->_invalidPath); $store = Filesystem::getInstance(array('dir' => $this->_invalidPath));
ServerSalt::get(); ServerSalt::setStore($store);
$salt = ServerSalt::get();
ServerSalt::setStore($store);
$this->assertNotEquals($salt, ServerSalt::get());
} }
/**
* @expectedException Exception
* @expectedExceptionCode 13
*/
public function testFileWrite() public function testFileWrite()
{ {
// try setting an invalid file // try setting an invalid file
@ -90,19 +93,24 @@ class ServerSaltTest extends PHPUnit_Framework_TestCase
} }
file_put_contents($this->_invalidPath . DIRECTORY_SEPARATOR . '.htaccess', ''); file_put_contents($this->_invalidPath . DIRECTORY_SEPARATOR . '.htaccess', '');
chmod($this->_invalidPath, 0500); chmod($this->_invalidPath, 0500);
ServerSalt::setPath($this->_invalidPath); $store = Filesystem::getInstance(array('dir' => $this->_invalidPath));
ServerSalt::get(); ServerSalt::setStore($store);
$salt = ServerSalt::get();
ServerSalt::setStore($store);
$this->assertNotEquals($salt, ServerSalt::get());
} }
/**
* @expectedException Exception
* @expectedExceptionCode 10
*/
public function testPermissionShenanigans() public function testPermissionShenanigans()
{ {
// try creating an invalid path // try creating an invalid path
chmod($this->_invalidPath, 0000); chmod($this->_invalidPath, 0000);
ServerSalt::setPath($this->_invalidPath . DIRECTORY_SEPARATOR . 'baz'); ServerSalt::setStore(
ServerSalt::get(); Filesystem::getInstance(array('dir' => $this->_invalidPath . DIRECTORY_SEPARATOR . 'baz'))
);
$store = Filesystem::getInstance(array('dir' => $this->_invalidPath));
ServerSalt::setStore($store);
$salt = ServerSalt::get();
ServerSalt::setStore($store);
$this->assertNotEquals($salt, ServerSalt::get());
} }
} }

View file

@ -1,5 +1,7 @@
<?php <?php
use PrivateBin\Data\Filesystem;
use PrivateBin\Persistence\ServerSalt;
use PrivateBin\Persistence\TrafficLimiter; use PrivateBin\Persistence\TrafficLimiter;
class TrafficLimiterTest extends PHPUnit_Framework_TestCase class TrafficLimiterTest extends PHPUnit_Framework_TestCase
@ -10,7 +12,9 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase
{ {
/* Setup Routine */ /* Setup Routine */
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'trafficlimit'; $this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'trafficlimit';
TrafficLimiter::setPath($this->_path); $store = Filesystem::getInstance(array('dir' => $this->_path));
ServerSalt::setStore($store);
TrafficLimiter::setStore($store);
} }
public function tearDown() public function tearDown()
@ -19,11 +23,17 @@ class TrafficLimiterTest extends PHPUnit_Framework_TestCase
Helper::rmDir($this->_path . DIRECTORY_SEPARATOR); Helper::rmDir($this->_path . DIRECTORY_SEPARATOR);
} }
public function testHtaccess()
{
$htaccess = $this->_path . DIRECTORY_SEPARATOR . '.htaccess';
@unlink($htaccess);
$_SERVER['REMOTE_ADDR'] = 'foobar';
TrafficLimiter::canPass();
$this->assertFileExists($htaccess, 'htaccess recreated');
}
public function testTrafficGetsLimited() public function testTrafficGetsLimited()
{ {
$this->assertEquals($this->_path, TrafficLimiter::getPath());
$file = 'baz';
$this->assertEquals($this->_path . DIRECTORY_SEPARATOR . $file, TrafficLimiter::getPath($file));
TrafficLimiter::setLimit(4); TrafficLimiter::setLimit(4);
$_SERVER['REMOTE_ADDR'] = '127.0.0.1'; $_SERVER['REMOTE_ADDR'] = '127.0.0.1';
$this->assertTrue(TrafficLimiter::canPass(), 'first request may pass'); $this->assertTrue(TrafficLimiter::canPass(), 'first request may pass');

View file

@ -1,5 +1,6 @@
<?php <?php
use PrivateBin\Data\Filesystem;
use PrivateBin\Persistence\ServerSalt; use PrivateBin\Persistence\ServerSalt;
use PrivateBin\Vizhash16x16; use PrivateBin\Vizhash16x16;
@ -17,7 +18,7 @@ class Vizhash16x16Test extends PHPUnit_Framework_TestCase
mkdir($this->_path); mkdir($this->_path);
} }
$this->_file = $this->_path . DIRECTORY_SEPARATOR . 'vizhash.png'; $this->_file = $this->_path . DIRECTORY_SEPARATOR . 'vizhash.png';
ServerSalt::setPath($this->_path); ServerSalt::setStore(Filesystem::getInstance(array('dir' => $this->_path)));
} }
public function tearDown() public function tearDown()