Merge branch 'persistence-into-data'
This commit is contained in:
commit
5f2daa5cd6
@ -10,6 +10,8 @@
|
|||||||
* CHANGED: Language selection cookie only transmitted over HTTPS (#472)
|
* CHANGED: Language selection cookie only transmitted over HTTPS (#472)
|
||||||
* 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: 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)
|
||||||
|
@ -143,9 +143,6 @@ limit = 10
|
|||||||
; set the HTTP header containing the visitors IP address, i.e. X_FORWARDED_FOR
|
; set the HTTP header containing the visitors IP address, i.e. X_FORWARDED_FOR
|
||||||
; header = "X_FORWARDED_FOR"
|
; header = "X_FORWARDED_FOR"
|
||||||
|
|
||||||
; directory to store the traffic limits in
|
|
||||||
dir = PATH "data"
|
|
||||||
|
|
||||||
[purge]
|
[purge]
|
||||||
; minimum time limit between two purgings of expired pastes, it is only
|
; minimum time limit between two purgings of expired pastes, it is only
|
||||||
; triggered when pastes are created
|
; triggered when pastes are created
|
||||||
@ -157,9 +154,6 @@ limit = 300
|
|||||||
; site
|
; site
|
||||||
batchsize = 10
|
batchsize = 10
|
||||||
|
|
||||||
; directory to store the purge limit in
|
|
||||||
dir = PATH "data"
|
|
||||||
|
|
||||||
[model]
|
[model]
|
||||||
; name of data model class to load and directory for storage
|
; name of data model class to load and directory for storage
|
||||||
; the default model "Filesystem" stores everything in the filesystem
|
; the default model "Filesystem" stores everything in the filesystem
|
||||||
|
@ -80,13 +80,11 @@ class Configuration
|
|||||||
'traffic' => array(
|
'traffic' => array(
|
||||||
'limit' => 10,
|
'limit' => 10,
|
||||||
'header' => null,
|
'header' => null,
|
||||||
'dir' => 'data',
|
|
||||||
'exemptedIp' => null,
|
'exemptedIp' => null,
|
||||||
),
|
),
|
||||||
'purge' => array(
|
'purge' => array(
|
||||||
'limit' => 300,
|
'limit' => 300,
|
||||||
'batchsize' => 10,
|
'batchsize' => 10,
|
||||||
'dir' => 'data',
|
|
||||||
),
|
),
|
||||||
'model' => array(
|
'model' => array(
|
||||||
'class' => 'Filesystem',
|
'class' => 'Filesystem',
|
||||||
|
@ -162,7 +162,6 @@ class Controller
|
|||||||
$this->_model = new Model($this->_conf);
|
$this->_model = new Model($this->_conf);
|
||||||
$this->_request = new Request;
|
$this->_request = new Request;
|
||||||
$this->_urlBase = $this->_request->getRequestUri();
|
$this->_urlBase = $this->_request->getRequestUri();
|
||||||
ServerSalt::setPath($this->_conf->getKey('dir', 'traffic'));
|
|
||||||
|
|
||||||
// set default language
|
// set default language
|
||||||
$lang = $this->_conf->getKey('languagedefault');
|
$lang = $this->_conf->getKey('languagedefault');
|
||||||
@ -196,9 +195,10 @@ class Controller
|
|||||||
*/
|
*/
|
||||||
private function _create()
|
private function _create()
|
||||||
{
|
{
|
||||||
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());
|
||||||
if (!TrafficLimiter::canPass()) {
|
if (!TrafficLimiter::canPass()) {
|
||||||
$this->_return_message(
|
$this->_return_message(
|
||||||
1, I18n::_(
|
1, I18n::_(
|
||||||
@ -208,10 +208,6 @@ class Controller
|
|||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
|
||||||
$this->_return_message(1, I18n::_($e->getMessage()));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = $this->_request->getData();
|
$data = $this->_request->getData();
|
||||||
$isComment = array_key_exists('pasteid', $data) &&
|
$isComment = array_key_exists('pasteid', $data) &&
|
||||||
|
@ -15,12 +15,12 @@ namespace PrivateBin\Data;
|
|||||||
/**
|
/**
|
||||||
* AbstractData
|
* AbstractData
|
||||||
*
|
*
|
||||||
* Abstract model for PrivateBin data access, implemented as a singleton.
|
* Abstract model for data access, implemented as a singleton.
|
||||||
*/
|
*/
|
||||||
abstract class AbstractData
|
abstract class AbstractData
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* singleton instance
|
* Singleton instance
|
||||||
*
|
*
|
||||||
* @access protected
|
* @access protected
|
||||||
* @static
|
* @static
|
||||||
@ -29,9 +29,18 @@ abstract class AbstractData
|
|||||||
protected static $_instance = null;
|
protected static $_instance = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enforce singleton, disable constructor
|
* cache for the traffic limiter
|
||||||
*
|
*
|
||||||
* Instantiate using {@link getInstance()}, privatebin is a singleton object.
|
* @access private
|
||||||
|
* @static
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
protected static $_last_cache = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enforce singleton, disable constructor
|
||||||
|
*
|
||||||
|
* Instantiate using {@link getInstance()}, this object implements the singleton pattern.
|
||||||
*
|
*
|
||||||
* @access protected
|
* @access protected
|
||||||
*/
|
*/
|
||||||
@ -40,9 +49,9 @@ abstract class AbstractData
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enforce singleton, disable cloning
|
* Enforce singleton, disable cloning
|
||||||
*
|
*
|
||||||
* Instantiate using {@link getInstance()}, privatebin is a singleton object.
|
* Instantiate using {@link getInstance()}, this object implements the singleton pattern.
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
*/
|
*/
|
||||||
@ -51,7 +60,7 @@ abstract class AbstractData
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get instance of singleton
|
* Get instance of singleton
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @static
|
* @static
|
||||||
@ -130,6 +139,46 @@ abstract class AbstractData
|
|||||||
*/
|
*/
|
||||||
abstract public function existsComment($pasteid, $parentid, $commentid);
|
abstract public function existsComment($pasteid, $parentid, $commentid);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Purge outdated entries.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $namespace
|
||||||
|
* @param int $time
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function purgeValues($namespace, $time)
|
||||||
|
{
|
||||||
|
if ($namespace === 'traffic_limiter') {
|
||||||
|
foreach (self::$_last_cache as $key => $last_submission) {
|
||||||
|
if ($last_submission <= $time) {
|
||||||
|
unset(self::$_last_cache[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a value.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $value
|
||||||
|
* @param string $namespace
|
||||||
|
* @param string $key
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
abstract public function setValue($value, $namespace, $key = '');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a value.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $namespace
|
||||||
|
* @param string $key
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
abstract public function getValue($namespace, $key = '');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns up to batch size number of paste ids that have expired
|
* Returns up to batch size number of paste ids that have expired
|
||||||
*
|
*
|
||||||
|
@ -198,6 +198,7 @@ class Database extends AbstractData
|
|||||||
$opendiscussion = $paste['adata'][2];
|
$opendiscussion = $paste['adata'][2];
|
||||||
$burnafterreading = $paste['adata'][3];
|
$burnafterreading = $paste['adata'][3];
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
return self::_exec(
|
return self::_exec(
|
||||||
'INSERT INTO ' . self::_sanitizeIdentifier('paste') .
|
'INSERT INTO ' . self::_sanitizeIdentifier('paste') .
|
||||||
' VALUES(?,?,?,?,?,?,?,?,?)',
|
' VALUES(?,?,?,?,?,?,?,?,?)',
|
||||||
@ -213,6 +214,9 @@ class Database extends AbstractData
|
|||||||
$attachmentname,
|
$attachmentname,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -229,11 +233,14 @@ class Database extends AbstractData
|
|||||||
}
|
}
|
||||||
|
|
||||||
self::$_cache[$pasteid] = false;
|
self::$_cache[$pasteid] = false;
|
||||||
|
try {
|
||||||
$paste = self::_select(
|
$paste = self::_select(
|
||||||
'SELECT * FROM ' . self::_sanitizeIdentifier('paste') .
|
'SELECT * FROM ' . self::_sanitizeIdentifier('paste') .
|
||||||
' WHERE dataid = ?', array($pasteid), true
|
' WHERE dataid = ?', array($pasteid), true
|
||||||
);
|
);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
$paste = false;
|
||||||
|
}
|
||||||
if ($paste === false) {
|
if ($paste === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -348,6 +355,7 @@ class Database extends AbstractData
|
|||||||
$meta[$key] = null;
|
$meta[$key] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
return self::_exec(
|
return self::_exec(
|
||||||
'INSERT INTO ' . self::_sanitizeIdentifier('comment') .
|
'INSERT INTO ' . self::_sanitizeIdentifier('comment') .
|
||||||
' VALUES(?,?,?,?,?,?,?)',
|
' VALUES(?,?,?,?,?,?,?)',
|
||||||
@ -361,6 +369,9 @@ class Database extends AbstractData
|
|||||||
$meta[$createdKey],
|
$meta[$createdKey],
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -416,11 +427,83 @@ class Database extends AbstractData
|
|||||||
*/
|
*/
|
||||||
public function existsComment($pasteid, $parentid, $commentid)
|
public function existsComment($pasteid, $parentid, $commentid)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
return (bool) self::_select(
|
return (bool) self::_select(
|
||||||
'SELECT dataid FROM ' . self::_sanitizeIdentifier('comment') .
|
'SELECT dataid FROM ' . self::_sanitizeIdentifier('comment') .
|
||||||
' WHERE pasteid = ? AND parentid = ? AND dataid = ?',
|
' WHERE pasteid = ? AND parentid = ? AND dataid = ?',
|
||||||
array($pasteid, $parentid, $commentid), true
|
array($pasteid, $parentid, $commentid), true
|
||||||
);
|
);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a value.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $value
|
||||||
|
* @param string $namespace
|
||||||
|
* @param string $key
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function setValue($value, $namespace, $key = '')
|
||||||
|
{
|
||||||
|
if ($namespace === 'traffic_limiter') {
|
||||||
|
self::$_last_cache[$key] = $value;
|
||||||
|
try {
|
||||||
|
$value = Json::encode(self::$_last_cache);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self::_exec(
|
||||||
|
'UPDATE ' . self::_sanitizeIdentifier('config') .
|
||||||
|
' SET value = ? WHERE id = ?',
|
||||||
|
array($value, strtoupper($namespace))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a value.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $namespace
|
||||||
|
* @param string $key
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
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::$_last_cache = Json::decode($value);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
self::$_last_cache = array();
|
||||||
|
}
|
||||||
|
if (array_key_exists($key, self::$_last_cache)) {
|
||||||
|
return self::$_last_cache[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (string) $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -563,16 +646,19 @@ class Database extends AbstractData
|
|||||||
* @access private
|
* @access private
|
||||||
* @static
|
* @static
|
||||||
* @param string $key
|
* @param string $key
|
||||||
* @throws PDOException
|
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private static function _getConfig($key)
|
private static function _getConfig($key)
|
||||||
{
|
{
|
||||||
|
try {
|
||||||
$row = self::_select(
|
$row = self::_select(
|
||||||
'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'];
|
} catch (PDOException $e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return $row ? $row['value'] : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,7 +12,8 @@
|
|||||||
|
|
||||||
namespace PrivateBin\Data;
|
namespace PrivateBin\Data;
|
||||||
|
|
||||||
use PrivateBin\Persistence\DataStore;
|
use Exception;
|
||||||
|
use PrivateBin\Json;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Filesystem
|
* Filesystem
|
||||||
@ -21,6 +22,29 @@ use PrivateBin\Persistence\DataStore;
|
|||||||
*/
|
*/
|
||||||
class Filesystem extends AbstractData
|
class Filesystem extends AbstractData
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* first line in paste or comment files, to protect their contents from browsing exposed data directories
|
||||||
|
*
|
||||||
|
* @const string
|
||||||
|
*/
|
||||||
|
const PROTECTION_LINE = '<?php http_response_code(403); /*';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* line in generated .htaccess files, to protect exposed directories from being browsable on apache web servers
|
||||||
|
*
|
||||||
|
* @const string
|
||||||
|
*/
|
||||||
|
const HTACCESS_LINE = 'Require all denied';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* path in which to persist something
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @static
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private static $_path = 'data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get instance of singleton
|
* get instance of singleton
|
||||||
*
|
*
|
||||||
@ -40,7 +64,7 @@ class Filesystem extends AbstractData
|
|||||||
is_array($options) &&
|
is_array($options) &&
|
||||||
array_key_exists('dir', $options)
|
array_key_exists('dir', $options)
|
||||||
) {
|
) {
|
||||||
DataStore::setPath($options['dir']);
|
self::$_path = $options['dir'];
|
||||||
}
|
}
|
||||||
return self::$_instance;
|
return self::$_instance;
|
||||||
}
|
}
|
||||||
@ -63,7 +87,7 @@ class Filesystem extends AbstractData
|
|||||||
if (!is_dir($storagedir)) {
|
if (!is_dir($storagedir)) {
|
||||||
mkdir($storagedir, 0700, true);
|
mkdir($storagedir, 0700, true);
|
||||||
}
|
}
|
||||||
return DataStore::store($file, $paste);
|
return self::_store($file, $paste);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,12 +99,13 @@ class Filesystem extends AbstractData
|
|||||||
*/
|
*/
|
||||||
public function read($pasteid)
|
public function read($pasteid)
|
||||||
{
|
{
|
||||||
if (!$this->exists($pasteid)) {
|
if (
|
||||||
|
!$this->exists($pasteid) ||
|
||||||
|
!$paste = self::_get(self::_dataid2path($pasteid) . $pasteid . '.php')
|
||||||
|
) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return self::upgradePreV1Format(
|
return self::upgradePreV1Format($paste);
|
||||||
DataStore::get(self::_dataid2path($pasteid) . $pasteid . '.php')
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -127,7 +152,7 @@ class Filesystem extends AbstractData
|
|||||||
$pastePath = $basePath . '.php';
|
$pastePath = $basePath . '.php';
|
||||||
// convert to PHP protected files if needed
|
// convert to PHP protected files if needed
|
||||||
if (is_readable($basePath)) {
|
if (is_readable($basePath)) {
|
||||||
DataStore::prependRename($basePath, $pastePath);
|
self::_prependRename($basePath, $pastePath);
|
||||||
|
|
||||||
// convert comments, too
|
// convert comments, too
|
||||||
$discdir = self::_dataid2discussionpath($pasteid);
|
$discdir = self::_dataid2discussionpath($pasteid);
|
||||||
@ -136,7 +161,7 @@ class Filesystem extends AbstractData
|
|||||||
while (false !== ($filename = $dir->read())) {
|
while (false !== ($filename = $dir->read())) {
|
||||||
if (substr($filename, -4) !== '.php' && strlen($filename) >= 16) {
|
if (substr($filename, -4) !== '.php' && strlen($filename) >= 16) {
|
||||||
$commentFilename = $discdir . $filename . '.php';
|
$commentFilename = $discdir . $filename . '.php';
|
||||||
DataStore::prependRename($discdir . $filename, $commentFilename);
|
self::_prependRename($discdir . $filename, $commentFilename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$dir->close();
|
$dir->close();
|
||||||
@ -165,7 +190,7 @@ class Filesystem extends AbstractData
|
|||||||
if (!is_dir($storagedir)) {
|
if (!is_dir($storagedir)) {
|
||||||
mkdir($storagedir, 0700, true);
|
mkdir($storagedir, 0700, true);
|
||||||
}
|
}
|
||||||
return DataStore::store($file, $comment);
|
return self::_store($file, $comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -187,7 +212,7 @@ class Filesystem extends AbstractData
|
|||||||
// - commentid is the comment identifier itself.
|
// - commentid is the comment identifier itself.
|
||||||
// - parentid is the comment this comment replies to (It can be pasteid)
|
// - parentid is the comment this comment replies to (It can be pasteid)
|
||||||
if (is_file($discdir . $filename)) {
|
if (is_file($discdir . $filename)) {
|
||||||
$comment = DataStore::get($discdir . $filename);
|
$comment = self::_get($discdir . $filename);
|
||||||
$items = explode('.', $filename);
|
$items = explode('.', $filename);
|
||||||
// Add some meta information not contained in file.
|
// Add some meta information not contained in file.
|
||||||
$comment['id'] = $items[1];
|
$comment['id'] = $items[1];
|
||||||
@ -223,6 +248,97 @@ class Filesystem extends AbstractData
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a value.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $value
|
||||||
|
* @param string $namespace
|
||||||
|
* @param string $key
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function setValue($value, $namespace, $key = '')
|
||||||
|
{
|
||||||
|
switch ($namespace) {
|
||||||
|
case 'purge_limiter':
|
||||||
|
return self::_storeString(
|
||||||
|
self::$_path . DIRECTORY_SEPARATOR . 'purge_limiter.php',
|
||||||
|
'<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . $value . ';'
|
||||||
|
);
|
||||||
|
case 'salt':
|
||||||
|
return self::_storeString(
|
||||||
|
self::$_path . DIRECTORY_SEPARATOR . 'salt.php',
|
||||||
|
'<?php # |' . $value . '|'
|
||||||
|
);
|
||||||
|
case 'traffic_limiter':
|
||||||
|
self::$_last_cache[$key] = $value;
|
||||||
|
return self::_storeString(
|
||||||
|
self::$_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php',
|
||||||
|
'<?php' . PHP_EOL . '$GLOBALS[\'traffic_limiter\'] = ' . var_export(self::$_last_cache, true) . ';'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a value.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @param string $namespace
|
||||||
|
* @param string $key
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getValue($namespace, $key = '')
|
||||||
|
{
|
||||||
|
switch ($namespace) {
|
||||||
|
case 'purge_limiter':
|
||||||
|
$file = self::$_path . DIRECTORY_SEPARATOR . 'purge_limiter.php';
|
||||||
|
if (is_readable($file)) {
|
||||||
|
require $file;
|
||||||
|
return $GLOBALS['purge_limiter'];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
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;
|
||||||
|
case 'traffic_limiter':
|
||||||
|
$file = self::$_path . DIRECTORY_SEPARATOR . 'traffic_limiter.php';
|
||||||
|
if (is_readable($file)) {
|
||||||
|
require $file;
|
||||||
|
self::$_last_cache = $GLOBALS['traffic_limiter'];
|
||||||
|
if (array_key_exists($key, self::$_last_cache)) {
|
||||||
|
return self::$_last_cache[$key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the data
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
* @param string $filename
|
||||||
|
* @return array|false $data
|
||||||
|
*/
|
||||||
|
private static function _get($filename)
|
||||||
|
{
|
||||||
|
return Json::decode(
|
||||||
|
substr(
|
||||||
|
file_get_contents($filename),
|
||||||
|
strlen(self::PROTECTION_LINE . PHP_EOL)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns up to batch size number of paste ids that have expired
|
* Returns up to batch size number of paste ids that have expired
|
||||||
*
|
*
|
||||||
@ -233,9 +349,8 @@ class Filesystem extends AbstractData
|
|||||||
protected function _getExpiredPastes($batchsize)
|
protected function _getExpiredPastes($batchsize)
|
||||||
{
|
{
|
||||||
$pastes = array();
|
$pastes = array();
|
||||||
$mainpath = DataStore::getPath();
|
|
||||||
$firstLevel = array_filter(
|
$firstLevel = array_filter(
|
||||||
scandir($mainpath),
|
scandir(self::$_path),
|
||||||
'self::_isFirstLevelDir'
|
'self::_isFirstLevelDir'
|
||||||
);
|
);
|
||||||
if (count($firstLevel) > 0) {
|
if (count($firstLevel) > 0) {
|
||||||
@ -243,7 +358,7 @@ class Filesystem extends AbstractData
|
|||||||
for ($i = 0, $max = $batchsize * 10; $i < $max; ++$i) {
|
for ($i = 0, $max = $batchsize * 10; $i < $max; ++$i) {
|
||||||
$firstKey = array_rand($firstLevel);
|
$firstKey = array_rand($firstLevel);
|
||||||
$secondLevel = array_filter(
|
$secondLevel = array_filter(
|
||||||
scandir($mainpath . DIRECTORY_SEPARATOR . $firstLevel[$firstKey]),
|
scandir(self::$_path . DIRECTORY_SEPARATOR . $firstLevel[$firstKey]),
|
||||||
'self::_isSecondLevelDir'
|
'self::_isSecondLevelDir'
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -254,7 +369,7 @@ class Filesystem extends AbstractData
|
|||||||
}
|
}
|
||||||
|
|
||||||
$secondKey = array_rand($secondLevel);
|
$secondKey = array_rand($secondLevel);
|
||||||
$path = $mainpath . DIRECTORY_SEPARATOR .
|
$path = self::$_path . DIRECTORY_SEPARATOR .
|
||||||
$firstLevel[$firstKey] . DIRECTORY_SEPARATOR .
|
$firstLevel[$firstKey] . DIRECTORY_SEPARATOR .
|
||||||
$secondLevel[$secondKey];
|
$secondLevel[$secondKey];
|
||||||
if (!is_dir($path)) {
|
if (!is_dir($path)) {
|
||||||
@ -314,10 +429,9 @@ class Filesystem extends AbstractData
|
|||||||
*/
|
*/
|
||||||
private static function _dataid2path($dataid)
|
private static function _dataid2path($dataid)
|
||||||
{
|
{
|
||||||
return DataStore::getPath(
|
return self::$_path . DIRECTORY_SEPARATOR .
|
||||||
substr($dataid, 0, 2) . DIRECTORY_SEPARATOR .
|
substr($dataid, 0, 2) . DIRECTORY_SEPARATOR .
|
||||||
substr($dataid, 2, 2) . DIRECTORY_SEPARATOR
|
substr($dataid, 2, 2) . DIRECTORY_SEPARATOR;
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -347,7 +461,7 @@ class Filesystem extends AbstractData
|
|||||||
private static function _isFirstLevelDir($element)
|
private static function _isFirstLevelDir($element)
|
||||||
{
|
{
|
||||||
return self::_isSecondLevelDir($element) &&
|
return self::_isSecondLevelDir($element) &&
|
||||||
is_dir(DataStore::getPath($element));
|
is_dir(self::$_path . DIRECTORY_SEPARATOR . $element);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -362,4 +476,97 @@ class Filesystem extends AbstractData
|
|||||||
{
|
{
|
||||||
return (bool) preg_match('/^[a-f0-9]{2}$/', $element);
|
return (bool) preg_match('/^[a-f0-9]{2}$/', $element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* store the data
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
* @param string $filename
|
||||||
|
* @param array $data
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private static function _store($filename, array $data)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return self::_storeString(
|
||||||
|
$filename,
|
||||||
|
self::PROTECTION_LINE . PHP_EOL . Json::encode($data)
|
||||||
|
);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* store a string
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
* @param string $filename
|
||||||
|
* @param string $data
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
private static function _storeString($filename, $data)
|
||||||
|
{
|
||||||
|
// Create storage directory if it does not exist.
|
||||||
|
if (!is_dir(self::$_path)) {
|
||||||
|
if (!@mkdir(self::$_path, 0700)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess';
|
||||||
|
if (!is_file($file)) {
|
||||||
|
$writtenBytes = 0;
|
||||||
|
if ($fileCreated = @touch($file)) {
|
||||||
|
$writtenBytes = @file_put_contents(
|
||||||
|
$file,
|
||||||
|
self::HTACCESS_LINE . PHP_EOL,
|
||||||
|
LOCK_EX
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
$fileCreated === false ||
|
||||||
|
$writtenBytes === false ||
|
||||||
|
$writtenBytes < strlen(self::HTACCESS_LINE . PHP_EOL)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$fileCreated = true;
|
||||||
|
$writtenBytes = 0;
|
||||||
|
if (!is_file($filename)) {
|
||||||
|
$fileCreated = @touch($filename);
|
||||||
|
}
|
||||||
|
if ($fileCreated) {
|
||||||
|
$writtenBytes = @file_put_contents($filename, $data, LOCK_EX);
|
||||||
|
}
|
||||||
|
if ($fileCreated === false || $writtenBytes === false || $writtenBytes < strlen($data)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
@chmod($filename, 0640); // protect file from access by other users on the host
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* rename a file, prepending the protection line at the beginning
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
* @param string $srcFile
|
||||||
|
* @param string $destFile
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private static function _prependRename($srcFile, $destFile)
|
||||||
|
{
|
||||||
|
// don't overwrite already converted file
|
||||||
|
if (!is_readable($destFile)) {
|
||||||
|
$handle = fopen($srcFile, 'r', false, stream_context_create());
|
||||||
|
file_put_contents($destFile, self::PROTECTION_LINE . PHP_EOL);
|
||||||
|
file_put_contents($destFile, $handle, FILE_APPEND);
|
||||||
|
fclose($handle);
|
||||||
|
}
|
||||||
|
unlink($srcFile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,39 @@ namespace PrivateBin\Data;
|
|||||||
|
|
||||||
use Exception;
|
use Exception;
|
||||||
use Google\Cloud\Core\Exception\NotFoundException;
|
use Google\Cloud\Core\Exception\NotFoundException;
|
||||||
|
use Google\Cloud\Storage\Bucket;
|
||||||
use Google\Cloud\Storage\StorageClient;
|
use Google\Cloud\Storage\StorageClient;
|
||||||
use PrivateBin\Json;
|
use PrivateBin\Json;
|
||||||
|
|
||||||
class GoogleCloudStorage extends AbstractData
|
class GoogleCloudStorage extends AbstractData
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* GCS client
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @static
|
||||||
|
* @var StorageClient
|
||||||
|
*/
|
||||||
|
private static $_client = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GCS bucket
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @static
|
||||||
|
* @var Bucket
|
||||||
|
*/
|
||||||
|
private static $_bucket = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* object prefix
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
|
* @static
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
private static $_prefix = 'pastes';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns a Google Cloud Storage data backend.
|
* returns a Google Cloud Storage data backend.
|
||||||
*
|
*
|
||||||
@ -19,10 +47,12 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
*/
|
*/
|
||||||
public static function getInstance(array $options)
|
public static function getInstance(array $options)
|
||||||
{
|
{
|
||||||
$client = null;
|
// if needed initialize the singleton
|
||||||
$bucket = null;
|
if (!(self::$_instance instanceof self)) {
|
||||||
$prefix = 'pastes';
|
self::$_instance = new self;
|
||||||
|
}
|
||||||
|
|
||||||
|
$bucket = null;
|
||||||
if (getenv('PRIVATEBIN_GCS_BUCKET')) {
|
if (getenv('PRIVATEBIN_GCS_BUCKET')) {
|
||||||
$bucket = getenv('PRIVATEBIN_GCS_BUCKET');
|
$bucket = getenv('PRIVATEBIN_GCS_BUCKET');
|
||||||
}
|
}
|
||||||
@ -30,53 +60,36 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
$bucket = $options['bucket'];
|
$bucket = $options['bucket'];
|
||||||
}
|
}
|
||||||
if (is_array($options) && array_key_exists('prefix', $options)) {
|
if (is_array($options) && array_key_exists('prefix', $options)) {
|
||||||
$prefix = $options['prefix'];
|
self::$_prefix = $options['prefix'];
|
||||||
}
|
|
||||||
if (is_array($options) && array_key_exists('client', $options)) {
|
|
||||||
$client = $options['client'];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(self::$_instance instanceof self)) {
|
if (empty(self::$_client)) {
|
||||||
self::$_instance = new self($bucket, $prefix, $client);
|
self::$_client = class_exists('StorageClientStub', false) ?
|
||||||
|
new \StorageClientStub(array()) :
|
||||||
|
new StorageClient(array('suppressKeyFileNotice' => true));
|
||||||
}
|
}
|
||||||
|
self::$_bucket = self::$_client->bucket($bucket);
|
||||||
|
|
||||||
return self::$_instance;
|
return self::$_instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected $_client = null;
|
|
||||||
protected $_bucket = null;
|
|
||||||
protected $_prefix = 'pastes';
|
|
||||||
|
|
||||||
public function __construct($bucket, $prefix, $client = null)
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
if ($client == null) {
|
|
||||||
$this->_client = new StorageClient(array('suppressKeyFileNotice' => true));
|
|
||||||
} else {
|
|
||||||
// use given client for test purposes
|
|
||||||
$this->_client = $client;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_bucket = $this->_client->bucket($bucket);
|
|
||||||
if ($prefix != null) {
|
|
||||||
$this->_prefix = $prefix;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns the google storage object key for $pasteid in $this->_bucket.
|
* returns the google storage object key for $pasteid in self::$_bucket.
|
||||||
|
*
|
||||||
|
* @access private
|
||||||
* @param $pasteid string to get the key for
|
* @param $pasteid string to get the key for
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
private function _getKey($pasteid)
|
private function _getKey($pasteid)
|
||||||
{
|
{
|
||||||
if ($this->_prefix != '') {
|
if (self::$_prefix != '') {
|
||||||
return $this->_prefix . '/' . $pasteid;
|
return self::$_prefix . '/' . $pasteid;
|
||||||
}
|
}
|
||||||
return $pasteid;
|
return $pasteid;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Uploads the payload in the $this->_bucket under the specified key.
|
* Uploads the payload in the self::$_bucket under the specified key.
|
||||||
* The entire payload is stored as a JSON document. The metadata is replicated
|
* The entire payload is stored as a JSON document. The metadata is replicated
|
||||||
* as the GCS object's metadata except for the fields attachment, attachmentname
|
* as the GCS object's metadata except for the fields attachment, attachmentname
|
||||||
* and salt.
|
* and salt.
|
||||||
@ -85,7 +98,7 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
* @param $payload array to store
|
* @param $payload array to store
|
||||||
* @return bool true if successful, otherwise false.
|
* @return bool true if successful, otherwise false.
|
||||||
*/
|
*/
|
||||||
private function upload($key, $payload)
|
private function _upload($key, $payload)
|
||||||
{
|
{
|
||||||
$metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array();
|
$metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array();
|
||||||
unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']);
|
unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']);
|
||||||
@ -93,7 +106,7 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
$metadata[$k] = strval($v);
|
$metadata[$k] = strval($v);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
$this->_bucket->upload(Json::encode($payload), array(
|
self::$_bucket->upload(Json::encode($payload), array(
|
||||||
'name' => $key,
|
'name' => $key,
|
||||||
'chunkSize' => 262144,
|
'chunkSize' => 262144,
|
||||||
'predefinedAcl' => 'private',
|
'predefinedAcl' => 'private',
|
||||||
@ -103,7 +116,7 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
),
|
),
|
||||||
));
|
));
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
error_log('failed to upload ' . $key . ' to ' . $this->_bucket->name() . ', ' .
|
error_log('failed to upload ' . $key . ' to ' . self::$_bucket->name() . ', ' .
|
||||||
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
|
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -119,7 +132,7 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->upload($this->_getKey($pasteid), $paste);
|
return $this->_upload($this->_getKey($pasteid), $paste);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -128,13 +141,13 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
public function read($pasteid)
|
public function read($pasteid)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$o = $this->_bucket->object($this->_getKey($pasteid));
|
$o = self::$_bucket->object($this->_getKey($pasteid));
|
||||||
$data = $o->downloadAsString();
|
$data = $o->downloadAsString();
|
||||||
return Json::decode($data);
|
return Json::decode($data);
|
||||||
} catch (NotFoundException $e) {
|
} catch (NotFoundException $e) {
|
||||||
return false;
|
return false;
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
error_log('failed to read ' . $pasteid . ' from ' . $this->_bucket->name() . ', ' .
|
error_log('failed to read ' . $pasteid . ' from ' . self::$_bucket->name() . ', ' .
|
||||||
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
|
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -148,9 +161,9 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
$name = $this->_getKey($pasteid);
|
$name = $this->_getKey($pasteid);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
foreach ($this->_bucket->objects(array('prefix' => $name . '/discussion/')) as $comment) {
|
foreach (self::$_bucket->objects(array('prefix' => $name . '/discussion/')) as $comment) {
|
||||||
try {
|
try {
|
||||||
$this->_bucket->object($comment->name())->delete();
|
self::$_bucket->object($comment->name())->delete();
|
||||||
} catch (NotFoundException $e) {
|
} catch (NotFoundException $e) {
|
||||||
// ignore if already deleted.
|
// ignore if already deleted.
|
||||||
}
|
}
|
||||||
@ -160,7 +173,7 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$this->_bucket->object($name)->delete();
|
self::$_bucket->object($name)->delete();
|
||||||
} catch (NotFoundException $e) {
|
} catch (NotFoundException $e) {
|
||||||
// ignore if already deleted
|
// ignore if already deleted
|
||||||
}
|
}
|
||||||
@ -171,7 +184,7 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
*/
|
*/
|
||||||
public function exists($pasteid)
|
public function exists($pasteid)
|
||||||
{
|
{
|
||||||
$o = $this->_bucket->object($this->_getKey($pasteid));
|
$o = self::$_bucket->object($this->_getKey($pasteid));
|
||||||
return $o->exists();
|
return $o->exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,7 +197,7 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$key = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid;
|
$key = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid;
|
||||||
return $this->upload($key, $comment);
|
return $this->_upload($key, $comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,8 +208,8 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
$comments = array();
|
$comments = array();
|
||||||
$prefix = $this->_getKey($pasteid) . '/discussion/';
|
$prefix = $this->_getKey($pasteid) . '/discussion/';
|
||||||
try {
|
try {
|
||||||
foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $key) {
|
foreach (self::$_bucket->objects(array('prefix' => $prefix)) as $key) {
|
||||||
$comment = JSON::decode($this->_bucket->object($key->name())->downloadAsString());
|
$comment = JSON::decode(self::$_bucket->object($key->name())->downloadAsString());
|
||||||
$comment['id'] = basename($key->name());
|
$comment['id'] = basename($key->name());
|
||||||
$slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']);
|
$slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']);
|
||||||
$comments[$slot] = $comment;
|
$comments[$slot] = $comment;
|
||||||
@ -213,10 +226,92 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
public function existsComment($pasteid, $parentid, $commentid)
|
public function existsComment($pasteid, $parentid, $commentid)
|
||||||
{
|
{
|
||||||
$name = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid;
|
$name = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid;
|
||||||
$o = $this->_bucket->object($name);
|
$o = self::$_bucket->object($name);
|
||||||
return $o->exists();
|
return $o->exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function purgeValues($namespace, $time)
|
||||||
|
{
|
||||||
|
$path = 'config/' . $namespace;
|
||||||
|
try {
|
||||||
|
foreach (self::$_bucket->objects(array('prefix' => $path)) as $object) {
|
||||||
|
$name = $object->name();
|
||||||
|
if (strlen($name) > strlen($path) && substr($name, strlen($path), 1) !== '/') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$info = $object->info();
|
||||||
|
if (key_exists('metadata', $info) && key_exists('value', $info['metadata'])) {
|
||||||
|
$value = $info['metadata']['value'];
|
||||||
|
if (is_numeric($value) && intval($value) < $time) {
|
||||||
|
try {
|
||||||
|
$object->delete();
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
// deleted by another instance.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
// no objects in the bucket yet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* For GoogleCloudStorage, the value will also be stored in the metadata for the
|
||||||
|
* namespaces traffic_limiter and purge_limiter.
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function setValue($value, $namespace, $key = '')
|
||||||
|
{
|
||||||
|
if ($key === '') {
|
||||||
|
$key = 'config/' . $namespace;
|
||||||
|
} else {
|
||||||
|
$key = 'config/' . $namespace . '/' . $key;
|
||||||
|
}
|
||||||
|
|
||||||
|
$metadata = array('namespace' => $namespace);
|
||||||
|
if ($namespace != 'salt') {
|
||||||
|
$metadata['value'] = strval($value);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
self::$_bucket->upload($value, array(
|
||||||
|
'name' => $key,
|
||||||
|
'chunkSize' => 262144,
|
||||||
|
'predefinedAcl' => 'private',
|
||||||
|
'metadata' => array(
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'metadata' => $metadata,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('failed to set key ' . $key . ' to ' . self::$_bucket->name() . ', ' .
|
||||||
|
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function getValue($namespace, $key = '')
|
||||||
|
{
|
||||||
|
if ($key === '') {
|
||||||
|
$key = 'config/' . $namespace;
|
||||||
|
} else {
|
||||||
|
$key = 'config/' . $namespace . '/' . $key;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$o = self::$_bucket->object($key);
|
||||||
|
return $o->downloadAsString();
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @inheritDoc
|
* @inheritDoc
|
||||||
*/
|
*/
|
||||||
@ -225,12 +320,12 @@ class GoogleCloudStorage extends AbstractData
|
|||||||
$expired = array();
|
$expired = array();
|
||||||
|
|
||||||
$now = time();
|
$now = time();
|
||||||
$prefix = $this->_prefix;
|
$prefix = self::$_prefix;
|
||||||
if ($prefix != '') {
|
if ($prefix != '') {
|
||||||
$prefix = $prefix . '/';
|
$prefix .= '/';
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $object) {
|
foreach (self::$_bucket->objects(array('prefix' => $prefix)) as $object) {
|
||||||
$metadata = $object->info()['metadata'];
|
$metadata = $object->info()['metadata'];
|
||||||
if ($metadata != null && array_key_exists('expire_date', $metadata)) {
|
if ($metadata != null && array_key_exists('expire_date', $metadata)) {
|
||||||
$expire_at = intval($metadata['expire_date']);
|
$expire_at = intval($metadata['expire_date']);
|
||||||
|
@ -52,6 +52,11 @@ class FormatV2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure adata is an array.
|
||||||
|
if (!is_array($message['adata'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$cipherParams = $isComment ? $message['adata'] : $message['adata'][0];
|
$cipherParams = $isComment ? $message['adata'] : $message['adata'][0];
|
||||||
|
|
||||||
// Make sure some fields are base64 data:
|
// Make sure some fields are base64 data:
|
||||||
|
@ -44,13 +44,13 @@ class Json
|
|||||||
* @static
|
* @static
|
||||||
* @param string $input
|
* @param string $input
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
* @return array
|
* @return mixed
|
||||||
*/
|
*/
|
||||||
public static function decode($input)
|
public static function decode($input)
|
||||||
{
|
{
|
||||||
$array = json_decode($input, true);
|
$output = json_decode($input, true);
|
||||||
self::_detectError();
|
self::_detectError();
|
||||||
return $array;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -54,7 +54,7 @@ class Model
|
|||||||
*/
|
*/
|
||||||
public function getPaste($pasteId = null)
|
public function getPaste($pasteId = null)
|
||||||
{
|
{
|
||||||
$paste = new Paste($this->_conf, $this->_getStore());
|
$paste = new Paste($this->_conf, $this->getStore());
|
||||||
if ($pasteId !== null) {
|
if ($pasteId !== null) {
|
||||||
$paste->setId($pasteId);
|
$paste->setId($pasteId);
|
||||||
}
|
}
|
||||||
@ -67,8 +67,9 @@ class Model
|
|||||||
public function purge()
|
public function purge()
|
||||||
{
|
{
|
||||||
PurgeLimiter::setConfiguration($this->_conf);
|
PurgeLimiter::setConfiguration($this->_conf);
|
||||||
|
PurgeLimiter::setStore($this->getStore());
|
||||||
if (PurgeLimiter::canPurge()) {
|
if (PurgeLimiter::canPurge()) {
|
||||||
$this->_getStore()->purge($this->_conf->getKey('batchsize', 'purge'));
|
$this->getStore()->purge($this->_conf->getKey('batchsize', 'purge'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -77,7 +78,7 @@ class Model
|
|||||||
*
|
*
|
||||||
* @return Data\AbstractData
|
* @return Data\AbstractData
|
||||||
*/
|
*/
|
||||||
private function _getStore()
|
public function getStore()
|
||||||
{
|
{
|
||||||
if ($this->_store === null) {
|
if ($this->_store === null) {
|
||||||
$this->_store = forward_static_call(
|
$this->_store = forward_static_call(
|
||||||
|
@ -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 (
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
namespace PrivateBin\Persistence;
|
namespace PrivateBin\Persistence;
|
||||||
|
|
||||||
use Exception;
|
use PrivateBin\Data\AbstractData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AbstractPersistence
|
* AbstractPersistence
|
||||||
@ -22,114 +22,23 @@ use Exception;
|
|||||||
abstract class AbstractPersistence
|
abstract class AbstractPersistence
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* path in which to persist something
|
* data storage to use to persist something
|
||||||
*
|
*
|
||||||
* @access private
|
* @access private
|
||||||
* @static
|
* @static
|
||||||
* @var string
|
* @var AbstractData
|
||||||
*/
|
*/
|
||||||
private static $_path = 'data';
|
protected static $_store;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the path
|
* set the path
|
||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @static
|
* @static
|
||||||
* @param string $path
|
* @param AbstractData $store
|
||||||
*/
|
*/
|
||||||
public static function setPath($path)
|
public static function setStore(AbstractData $store)
|
||||||
{
|
{
|
||||||
self::$_path = $path;
|
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,96 +0,0 @@
|
|||||||
<?php
|
|
||||||
/**
|
|
||||||
* PrivateBin
|
|
||||||
*
|
|
||||||
* a zero-knowledge paste bin
|
|
||||||
*
|
|
||||||
* @link https://github.com/PrivateBin/PrivateBin
|
|
||||||
* @copyright 2012 Sébastien SAUVAGE (sebsauvage.net)
|
|
||||||
* @license https://www.opensource.org/licenses/zlib-license.php The zlib/libpng License
|
|
||||||
* @version 1.3.5
|
|
||||||
*/
|
|
||||||
|
|
||||||
namespace PrivateBin\Persistence;
|
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use PrivateBin\Json;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DataStore
|
|
||||||
*
|
|
||||||
* Handles data storage for Data\Filesystem.
|
|
||||||
*/
|
|
||||||
class DataStore extends AbstractPersistence
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* first line in file, to protect its contents
|
|
||||||
*
|
|
||||||
* @const string
|
|
||||||
*/
|
|
||||||
const PROTECTION_LINE = '<?php http_response_code(403); /*';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* store the data
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @static
|
|
||||||
* @param string $filename
|
|
||||||
* @param array $data
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public static function store($filename, $data)
|
|
||||||
{
|
|
||||||
$path = self::getPath();
|
|
||||||
if (strpos($filename, $path) === 0) {
|
|
||||||
$filename = substr($filename, strlen($path));
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
self::_store(
|
|
||||||
$filename,
|
|
||||||
self::PROTECTION_LINE . PHP_EOL . Json::encode($data)
|
|
||||||
);
|
|
||||||
return true;
|
|
||||||
} catch (Exception $e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* get the data
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @static
|
|
||||||
* @param string $filename
|
|
||||||
* @return array|false $data
|
|
||||||
*/
|
|
||||||
public static function get($filename)
|
|
||||||
{
|
|
||||||
return Json::decode(
|
|
||||||
substr(
|
|
||||||
file_get_contents($filename),
|
|
||||||
strlen(self::PROTECTION_LINE . PHP_EOL)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* rename a file, prepending the protection line at the beginning
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @static
|
|
||||||
* @param string $srcFile
|
|
||||||
* @param string $destFile
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
public static function prependRename($srcFile, $destFile)
|
|
||||||
{
|
|
||||||
// don't overwrite already converted file
|
|
||||||
if (!is_readable($destFile)) {
|
|
||||||
$handle = fopen($srcFile, 'r', false, stream_context_create());
|
|
||||||
file_put_contents($destFile, self::PROTECTION_LINE . PHP_EOL);
|
|
||||||
file_put_contents($destFile, $handle, FILE_APPEND);
|
|
||||||
fclose($handle);
|
|
||||||
}
|
|
||||||
unlink($srcFile);
|
|
||||||
}
|
|
||||||
}
|
|
@ -52,7 +52,6 @@ class PurgeLimiter extends AbstractPersistence
|
|||||||
public static function setConfiguration(Configuration $conf)
|
public static function setConfiguration(Configuration $conf)
|
||||||
{
|
{
|
||||||
self::setLimit($conf->getKey('limit', 'purge'));
|
self::setLimit($conf->getKey('limit', 'purge'));
|
||||||
self::setPath($conf->getKey('dir', 'purge'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,7 +59,6 @@ class PurgeLimiter extends AbstractPersistence
|
|||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @static
|
* @static
|
||||||
* @throws \Exception
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function canPurge()
|
public static function canPurge()
|
||||||
@ -71,17 +69,14 @@ class PurgeLimiter extends AbstractPersistence
|
|||||||
}
|
}
|
||||||
|
|
||||||
$now = time();
|
$now = time();
|
||||||
$file = 'purge_limiter.php';
|
$pl = (int) self::$_store->getValue('purge_limiter');
|
||||||
if (self::_exists($file)) {
|
|
||||||
require self::getPath($file);
|
|
||||||
$pl = $GLOBALS['purge_limiter'];
|
|
||||||
if ($pl + self::$_limit >= $now) {
|
if ($pl + self::$_limit >= $now) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
$hasStored = self::$_store->setValue((string) $now, 'purge_limiter');
|
||||||
|
if (!$hasStored) {
|
||||||
|
error_log('failed to store the purge limiter, skipping purge cycle to avoid getting stuck in a purge loop');
|
||||||
}
|
}
|
||||||
|
return $hasStored;
|
||||||
$content = '<?php' . PHP_EOL . '$GLOBALS[\'purge_limiter\'] = ' . $now . ';';
|
|
||||||
self::_store($file, $content);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
namespace PrivateBin\Persistence;
|
namespace PrivateBin\Persistence;
|
||||||
|
|
||||||
use Exception;
|
use PrivateBin\Data\AbstractData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ServerSalt
|
* ServerSalt
|
||||||
@ -26,15 +26,6 @@ use Exception;
|
|||||||
*/
|
*/
|
||||||
class ServerSalt extends AbstractPersistence
|
class ServerSalt extends AbstractPersistence
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* file where salt is saved to
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @static
|
|
||||||
* @var string
|
|
||||||
*/
|
|
||||||
private static $_file = 'salt.php';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* generated salt
|
* generated salt
|
||||||
*
|
*
|
||||||
@ -53,8 +44,7 @@ class ServerSalt extends AbstractPersistence
|
|||||||
*/
|
*/
|
||||||
public static function generate()
|
public static function generate()
|
||||||
{
|
{
|
||||||
$randomSalt = bin2hex(random_bytes(256));
|
return bin2hex(random_bytes(256));
|
||||||
return $randomSalt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -62,7 +52,6 @@ class ServerSalt extends AbstractPersistence
|
|||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @static
|
* @static
|
||||||
* @throws Exception
|
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
public static function get()
|
public static function get()
|
||||||
@ -71,20 +60,14 @@ 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(
|
if (!self::$_store->setValue(self::$_salt, 'salt')) {
|
||||||
self::$_file,
|
error_log('failed to store the server salt, delete tokens, traffic limiter and user icons won\'t work');
|
||||||
'<?php # |' . self::$_salt . '|'
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return self::$_salt;
|
return self::$_salt;
|
||||||
}
|
}
|
||||||
@ -94,11 +77,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,6 @@
|
|||||||
|
|
||||||
namespace PrivateBin\Persistence;
|
namespace PrivateBin\Persistence;
|
||||||
|
|
||||||
use Exception;
|
|
||||||
use IPLib\Factory;
|
use IPLib\Factory;
|
||||||
use PrivateBin\Configuration;
|
use PrivateBin\Configuration;
|
||||||
|
|
||||||
@ -85,7 +84,6 @@ class TrafficLimiter extends AbstractPersistence
|
|||||||
public static function setConfiguration(Configuration $conf)
|
public static function setConfiguration(Configuration $conf)
|
||||||
{
|
{
|
||||||
self::setLimit($conf->getKey('limit', 'traffic'));
|
self::setLimit($conf->getKey('limit', 'traffic'));
|
||||||
self::setPath($conf->getKey('dir', 'traffic'));
|
|
||||||
self::setExemptedIp($conf->getKey('exemptedIp', 'traffic'));
|
self::setExemptedIp($conf->getKey('exemptedIp', 'traffic'));
|
||||||
|
|
||||||
if (($option = $conf->getKey('header', 'traffic')) !== null) {
|
if (($option = $conf->getKey('header', 'traffic')) !== null) {
|
||||||
@ -134,13 +132,7 @@ class TrafficLimiter extends AbstractPersistence
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ip-lib throws an exception when something goes wrong, if so we want to catch it and set contained to false
|
|
||||||
try {
|
|
||||||
return $address->matches($range);
|
return $address->matches($range);
|
||||||
} catch (Exception $e) {
|
|
||||||
// If something is wrong with matching the ip, we assume it doesn't match
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -150,7 +142,6 @@ class TrafficLimiter extends AbstractPersistence
|
|||||||
*
|
*
|
||||||
* @access public
|
* @access public
|
||||||
* @static
|
* @static
|
||||||
* @throws Exception
|
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public static function canPass()
|
public static function canPass()
|
||||||
@ -170,35 +161,20 @@ class TrafficLimiter extends AbstractPersistence
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$file = 'traffic_limiter.php';
|
|
||||||
if (self::_exists($file)) {
|
|
||||||
require self::getPath($file);
|
|
||||||
$tl = $GLOBALS['traffic_limiter'];
|
|
||||||
} else {
|
|
||||||
$tl = array();
|
|
||||||
}
|
|
||||||
|
|
||||||
// purge file of expired hashes to keep it small
|
|
||||||
$now = time();
|
|
||||||
foreach ($tl as $key => $time) {
|
|
||||||
if ($time + self::$_limit < $now) {
|
|
||||||
unset($tl[$key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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');
|
||||||
if (array_key_exists($hash, $tl) && ($tl[$hash] + self::$_limit >= $now)) {
|
$now = time();
|
||||||
|
$tl = (int) self::$_store->getValue('traffic_limiter', $hash);
|
||||||
|
self::$_store->purgeValues('traffic_limiter', $now - self::$_limit);
|
||||||
|
if ($tl > 0 && ($tl + self::$_limit >= $now)) {
|
||||||
$result = false;
|
$result = false;
|
||||||
} else {
|
} else {
|
||||||
$tl[$hash] = time();
|
$tl = time();
|
||||||
$result = true;
|
$result = true;
|
||||||
}
|
}
|
||||||
self::_store(
|
if (!self::$_store->setValue((string) $tl, 'traffic_limiter', $hash)) {
|
||||||
$file,
|
error_log('failed to store the traffic limiter, it probably contains outdated information');
|
||||||
'<?php' . PHP_EOL .
|
}
|
||||||
'$GLOBALS[\'traffic_limiter\'] = ' . var_export($tl, true) . ';'
|
|
||||||
);
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,6 +108,8 @@ class Request
|
|||||||
case 'DELETE':
|
case 'DELETE':
|
||||||
case 'PUT':
|
case 'PUT':
|
||||||
case 'POST':
|
case 'POST':
|
||||||
|
// it might be a creation or a deletion, the latter is detected below
|
||||||
|
$this->_operation = 'create';
|
||||||
$this->_params = Json::decode(
|
$this->_params = Json::decode(
|
||||||
file_get_contents(self::$_inputStream)
|
file_get_contents(self::$_inputStream)
|
||||||
);
|
);
|
||||||
@ -125,15 +127,10 @@ class Request
|
|||||||
}
|
}
|
||||||
|
|
||||||
// prepare operation, depending on current parameters
|
// prepare operation, depending on current parameters
|
||||||
if (
|
if (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid'])) {
|
||||||
array_key_exists('ct', $this->_params) &&
|
|
||||||
!empty($this->_params['ct'])
|
|
||||||
) {
|
|
||||||
$this->_operation = 'create';
|
|
||||||
} elseif (array_key_exists('pasteid', $this->_params) && !empty($this->_params['pasteid'])) {
|
|
||||||
if (array_key_exists('deletetoken', $this->_params) && !empty($this->_params['deletetoken'])) {
|
if (array_key_exists('deletetoken', $this->_params) && !empty($this->_params['deletetoken'])) {
|
||||||
$this->_operation = 'delete';
|
$this->_operation = 'delete';
|
||||||
} else {
|
} elseif ($this->_operation != 'create') {
|
||||||
$this->_operation = 'read';
|
$this->_operation = 'read';
|
||||||
}
|
}
|
||||||
} elseif (array_key_exists('jsonld', $this->_params) && !empty($this->_params['jsonld'])) {
|
} elseif (array_key_exists('jsonld', $this->_params) && !empty($this->_params['jsonld'])) {
|
||||||
@ -172,7 +169,7 @@ class Request
|
|||||||
$data['meta'] = $meta;
|
$data['meta'] = $meta;
|
||||||
}
|
}
|
||||||
foreach ($required_keys as $key) {
|
foreach ($required_keys as $key) {
|
||||||
$data[$key] = $this->getParam($key);
|
$data[$key] = $this->getParam($key, $key == 'v' ? 1 : '');
|
||||||
}
|
}
|
||||||
// forcing a cast to int or float
|
// forcing a cast to int or float
|
||||||
$data['v'] = $data['v'] + 0;
|
$data['v'] = $data['v'] + 0;
|
||||||
|
@ -1,5 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use Google\Cloud\Core\Exception\BadRequestException;
|
||||||
|
use Google\Cloud\Core\Exception\NotFoundException;
|
||||||
|
use Google\Cloud\Storage\Bucket;
|
||||||
|
use Google\Cloud\Storage\Connection\ConnectionInterface;
|
||||||
|
use Google\Cloud\Storage\StorageClient;
|
||||||
|
use Google\Cloud\Storage\StorageObject;
|
||||||
use PrivateBin\Persistence\ServerSalt;
|
use PrivateBin\Persistence\ServerSalt;
|
||||||
|
|
||||||
error_reporting(E_ALL | E_STRICT);
|
error_reporting(E_ALL | E_STRICT);
|
||||||
@ -21,6 +27,586 @@ if (!defined('CONF_SAMPLE')) {
|
|||||||
require PATH . 'vendor/autoload.php';
|
require PATH . 'vendor/autoload.php';
|
||||||
Helper::updateSubresourceIntegrity();
|
Helper::updateSubresourceIntegrity();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class StorageClientStub provides a limited stub for performing the unit test
|
||||||
|
*/
|
||||||
|
class StorageClientStub extends StorageClient
|
||||||
|
{
|
||||||
|
private $_config = null;
|
||||||
|
private $_connection = null;
|
||||||
|
private $_buckets = array();
|
||||||
|
|
||||||
|
public function __construct(array $config = array())
|
||||||
|
{
|
||||||
|
$this->_config = $config;
|
||||||
|
$this->_connection = new ConnectionInterfaceStub();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function bucket($name, $userProject = false)
|
||||||
|
{
|
||||||
|
if (!key_exists($name, $this->_buckets)) {
|
||||||
|
$b = new BucketStub($this->_connection, $name, array(), $this);
|
||||||
|
$this->_buckets[$name] = $b;
|
||||||
|
}
|
||||||
|
return $this->_buckets[$name];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws \Google\Cloud\Core\Exception\NotFoundException
|
||||||
|
*/
|
||||||
|
public function deleteBucket($name)
|
||||||
|
{
|
||||||
|
if (key_exists($name, $this->_buckets)) {
|
||||||
|
unset($this->_buckets[$name]);
|
||||||
|
} else {
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function buckets(array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function registerStreamWrapper($protocol = null)
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unregisterStreamWrapper($protocol = null)
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function signedUrlUploader($uri, $data, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function timestamp(\DateTimeInterface $timestamp, $nanoSeconds = null)
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getServiceAccount(array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hmacKeys(array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hmacKey($accessId, $projectId = null, array $metadata = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createHmacKey($serviceAccountEmail, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createBucket($name, array $options = array())
|
||||||
|
{
|
||||||
|
if (key_exists($name, $this->_buckets)) {
|
||||||
|
throw new BadRequestException('already exists');
|
||||||
|
}
|
||||||
|
$b = new BucketStub($this->_connection, $name, array(), $this);
|
||||||
|
$this->_buckets[$name] = $b;
|
||||||
|
return $b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class BucketStub stubs a GCS bucket.
|
||||||
|
*/
|
||||||
|
class BucketStub extends Bucket
|
||||||
|
{
|
||||||
|
public $_objects;
|
||||||
|
private $_name;
|
||||||
|
private $_info;
|
||||||
|
private $_connection;
|
||||||
|
private $_client;
|
||||||
|
|
||||||
|
public function __construct(ConnectionInterface $connection, $name, array $info = array(), $client = null)
|
||||||
|
{
|
||||||
|
$this->_name = $name;
|
||||||
|
$this->_info = $info;
|
||||||
|
$this->_connection = $connection;
|
||||||
|
$this->_objects = array();
|
||||||
|
$this->_client = $client;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function acl()
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function defaultAcl()
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exists()
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function upload($data, array $options = array())
|
||||||
|
{
|
||||||
|
if (!is_string($data) || !key_exists('name', $options)) {
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = $options['name'];
|
||||||
|
$generation = '1';
|
||||||
|
$o = new StorageObjectStub($this->_connection, $name, $this, $generation, $options);
|
||||||
|
$this->_objects[$options['name']] = $o;
|
||||||
|
$o->setData($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function uploadAsync($data, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResumableUploader($data, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStreamableUploader($data, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function object($name, array $options = array())
|
||||||
|
{
|
||||||
|
if (key_exists($name, $this->_objects)) {
|
||||||
|
return $this->_objects[$name];
|
||||||
|
} else {
|
||||||
|
return new StorageObjectStub($this->_connection, $name, $this, null, $options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function objects(array $options = array())
|
||||||
|
{
|
||||||
|
$prefix = key_exists('prefix', $options) ? $options['prefix'] : '';
|
||||||
|
|
||||||
|
return new CallbackFilterIterator(
|
||||||
|
new ArrayIterator($this->_objects),
|
||||||
|
function ($current, $key, $iterator) use ($prefix) {
|
||||||
|
return substr($key, 0, strlen($prefix)) == $prefix;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createNotification($topic, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notification($id)
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function notifications(array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function delete(array $options = array())
|
||||||
|
{
|
||||||
|
$this->_client->deleteBucket($this->_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function compose(array $sourceObjects, $name, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function info(array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reload(array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function name()
|
||||||
|
{
|
||||||
|
return $this->_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function lifecycle(array $lifecycle = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function currentLifecycle(array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isWritable($file = null)
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function iam()
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lockRetentionPolicy(array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function signedUrl($expires, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateSignedPostPolicyV4($objectName, $expires, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class StorageObjectStub stubs a GCS storage object.
|
||||||
|
*/
|
||||||
|
class StorageObjectStub extends StorageObject
|
||||||
|
{
|
||||||
|
private $_name;
|
||||||
|
private $_data;
|
||||||
|
private $_info;
|
||||||
|
private $_bucket;
|
||||||
|
private $_generation;
|
||||||
|
private $_exists = false;
|
||||||
|
private $_connection;
|
||||||
|
|
||||||
|
public function __construct(ConnectionInterface $connection, $name, $bucket, $generation = null, array $info = array(), $encryptionKey = null, $encryptionKeySHA256 = null)
|
||||||
|
{
|
||||||
|
$this->_name = $name;
|
||||||
|
$this->_bucket = $bucket;
|
||||||
|
$this->_generation = $generation;
|
||||||
|
$this->_info = $info;
|
||||||
|
$this->_connection = $connection;
|
||||||
|
$timeCreated = new Datetime();
|
||||||
|
$this->_info['metadata']['timeCreated'] = $timeCreated->format('Y-m-d\TH:i:s.u\Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function acl()
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function exists(array $options = array())
|
||||||
|
{
|
||||||
|
return key_exists($this->_name, $this->_bucket->_objects);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NotFoundException
|
||||||
|
*/
|
||||||
|
public function delete(array $options = array())
|
||||||
|
{
|
||||||
|
if (key_exists($this->_name, $this->_bucket->_objects)) {
|
||||||
|
unset($this->_bucket->_objects[$this->_name]);
|
||||||
|
} else {
|
||||||
|
throw new NotFoundException('key ' . $this->_name . ' not found.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NotFoundException
|
||||||
|
*/
|
||||||
|
public function update(array $metadata, array $options = array())
|
||||||
|
{
|
||||||
|
if (!$this->_exists) {
|
||||||
|
throw new NotFoundException('key ' . $this->_name . ' not found.');
|
||||||
|
}
|
||||||
|
$this->_info = $metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function copy($destination, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rewrite($destination, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rename($name, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @throws NotFoundException
|
||||||
|
*/
|
||||||
|
public function downloadAsString(array $options = array())
|
||||||
|
{
|
||||||
|
if (!$this->_exists) {
|
||||||
|
throw new NotFoundException('key ' . $this->_name . ' not found.');
|
||||||
|
}
|
||||||
|
return $this->_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadToFile($path, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadAsStream(array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadAsStreamAsync(array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function signedUrl($expires, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function signedUploadUrl($expires, array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function beginSignedUploadSession(array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function info(array $options = array())
|
||||||
|
{
|
||||||
|
return key_exists('metadata',$this->_info) ? $this->_info['metadata'] : array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reload(array $options = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function name()
|
||||||
|
{
|
||||||
|
return $this->_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function identity()
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function gcsUri()
|
||||||
|
{
|
||||||
|
return sprintf(
|
||||||
|
'gs://%s/%s',
|
||||||
|
$this->_bucket->name(),
|
||||||
|
$this->_name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setData($data)
|
||||||
|
{
|
||||||
|
$this->_data = $data;
|
||||||
|
$this->_exists = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class ConnectionInterfaceStub required for the stubs.
|
||||||
|
*/
|
||||||
|
class ConnectionInterfaceStub implements ConnectionInterface
|
||||||
|
{
|
||||||
|
public function deleteAcl(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAcl(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listAcl(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function insertAcl(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function patchAcl(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteBucket(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBucket(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listBuckets(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function insertBucket(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBucketIamPolicy(array $args)
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setBucketIamPolicy(array $args)
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testBucketIamPermissions(array $args)
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function patchBucket(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteObject(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function copyObject(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rewriteObject(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function composeObject(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getObject(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listObjects(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function patchObject(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function downloadObject(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function insertObject(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNotification(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteNotification(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function insertNotification(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listNotifications(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getServiceAccount(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function lockRetentionPolicy(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createHmacKey(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteHmacKey(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHmacKey(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateHmacKey(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function listHmacKeys(array $args = array())
|
||||||
|
{
|
||||||
|
throw new BadMethodCallException('not supported by this stub');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Helper provides unit tests pastes and comments of various formats
|
||||||
|
*/
|
||||||
class Helper
|
class Helper
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
@ -155,7 +741,11 @@ class Helper
|
|||||||
public static function getPastePost($version = 2, array $meta = array())
|
public static function getPastePost($version = 2, array $meta = array())
|
||||||
{
|
{
|
||||||
$example = self::getPaste($version, $meta);
|
$example = self::getPaste($version, $meta);
|
||||||
|
if ($version == 2) {
|
||||||
$example['meta'] = array('expire' => $example['meta']['expire']);
|
$example['meta'] = array('expire' => $example['meta']['expire']);
|
||||||
|
} else {
|
||||||
|
unset($example['meta']['postdate']);
|
||||||
|
}
|
||||||
return $example;
|
return $example;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,8 +17,6 @@ class ConfigurationTest extends PHPUnit_Framework_TestCase
|
|||||||
$this->_minimalConfig = '[main]' . PHP_EOL . '[model]' . PHP_EOL . '[model_options]';
|
$this->_minimalConfig = '[main]' . PHP_EOL . '[model]' . PHP_EOL . '[model_options]';
|
||||||
$this->_options = Configuration::getDefaults();
|
$this->_options = Configuration::getDefaults();
|
||||||
$this->_options['model_options']['dir'] = PATH . $this->_options['model_options']['dir'];
|
$this->_options['model_options']['dir'] = PATH . $this->_options['model_options']['dir'];
|
||||||
$this->_options['traffic']['dir'] = PATH . $this->_options['traffic']['dir'];
|
|
||||||
$this->_options['purge']['dir'] = PATH . $this->_options['purge']['dir'];
|
|
||||||
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_cfg';
|
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_cfg';
|
||||||
if (!is_dir($this->_path)) {
|
if (!is_dir($this->_path)) {
|
||||||
mkdir($this->_path);
|
mkdir($this->_path);
|
||||||
|
@ -428,8 +428,6 @@ class ConfigurationCombinationsTest extends PHPUnit_Framework_TestCase
|
|||||||
Helper::confBackup();
|
Helper::confBackup();
|
||||||
$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);
|
|
||||||
TrafficLimiter::setPath($this->_path);
|
|
||||||
$this->reset();
|
$this->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -449,8 +447,6 @@ class ConfigurationCombinationsTest extends PHPUnit_Framework_TestCase
|
|||||||
if ($this->_model->exists(Helper::getPasteId()))
|
if ($this->_model->exists(Helper::getPasteId()))
|
||||||
$this->_model->delete(Helper::getPasteId());
|
$this->_model->delete(Helper::getPasteId());
|
||||||
$configuration['model_options']['dir'] = $this->_path;
|
$configuration['model_options']['dir'] = $this->_path;
|
||||||
$configuration['traffic']['dir'] = $this->_path;
|
|
||||||
$configuration['purge']['dir'] = $this->_path;
|
|
||||||
Helper::createIniFile(CONF, $configuration);
|
Helper::createIniFile(CONF, $configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,8 @@ 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);
|
||||||
$this->reset();
|
$this->reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -37,11 +39,8 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
|||||||
$this->_data->delete(Helper::getPasteId());
|
$this->_data->delete(Helper::getPasteId());
|
||||||
}
|
}
|
||||||
$options = parse_ini_file(CONF_SAMPLE, true);
|
$options = parse_ini_file(CONF_SAMPLE, true);
|
||||||
$options['purge']['dir'] = $this->_path;
|
|
||||||
$options['traffic']['dir'] = $this->_path;
|
|
||||||
$options['model_options']['dir'] = $this->_path;
|
$options['model_options']['dir'] = $this->_path;
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
ServerSalt::setPath($this->_path);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -49,6 +48,8 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
|||||||
*/
|
*/
|
||||||
public function testView()
|
public function testView()
|
||||||
{
|
{
|
||||||
|
$_SERVER['QUERY_STRING'] = Helper::getPasteId();
|
||||||
|
$_GET[Helper::getPasteId()] = '';
|
||||||
ob_start();
|
ob_start();
|
||||||
new Controller;
|
new Controller;
|
||||||
$content = ob_get_contents();
|
$content = ob_get_contents();
|
||||||
@ -127,28 +128,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
|
||||||
@ -493,6 +472,29 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
|||||||
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @runInSeparateProcess
|
||||||
|
*/
|
||||||
|
public function testCreateInvalidFormat()
|
||||||
|
{
|
||||||
|
$options = parse_ini_file(CONF, true);
|
||||||
|
$options['traffic']['limit'] = 0;
|
||||||
|
Helper::createIniFile(CONF, $options);
|
||||||
|
$file = tempnam(sys_get_temp_dir(), 'FOO');
|
||||||
|
file_put_contents($file, Helper::getPasteJson(1));
|
||||||
|
Request::setInputStream($file);
|
||||||
|
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'JSONHttpRequest';
|
||||||
|
$_SERVER['REQUEST_METHOD'] = 'POST';
|
||||||
|
$_SERVER['REMOTE_ADDR'] = '::1';
|
||||||
|
ob_start();
|
||||||
|
new Controller;
|
||||||
|
$content = ob_get_contents();
|
||||||
|
ob_end_clean();
|
||||||
|
$response = json_decode($content, true);
|
||||||
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
|
$this->assertFalse($this->_data->exists(Helper::getPasteId()), 'paste exists after posting data');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @runInSeparateProcess
|
* @runInSeparateProcess
|
||||||
*/
|
*/
|
||||||
@ -541,7 +543,7 @@ class ControllerTest extends PHPUnit_Framework_TestCase
|
|||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
$response = json_decode($content, true);
|
$response = json_decode($content, true);
|
||||||
$this->assertEquals(1, $response['status'], 'outputs error status');
|
$this->assertEquals(1, $response['status'], 'outputs error status');
|
||||||
$this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'paste exists after posting data');
|
$this->assertFalse($this->_data->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after posting data');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
59
tst/ControllerWithGcsTest.php
Normal file
59
tst/ControllerWithGcsTest.php
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Google\Auth\HttpHandler\HttpHandlerFactory;
|
||||||
|
use GuzzleHttp\Client;
|
||||||
|
use PrivateBin\Data\GoogleCloudStorage;
|
||||||
|
use PrivateBin\Persistence\ServerSalt;
|
||||||
|
use PrivateBin\Persistence\TrafficLimiter;
|
||||||
|
|
||||||
|
require_once 'ControllerTest.php';
|
||||||
|
|
||||||
|
class ControllerWithGcsTest extends ControllerTest
|
||||||
|
{
|
||||||
|
private static $_client;
|
||||||
|
private static $_bucket;
|
||||||
|
private $_options = array();
|
||||||
|
|
||||||
|
public static function setUpBeforeClass()
|
||||||
|
{
|
||||||
|
$httpClient = new Client(array('debug'=>false));
|
||||||
|
$handler = HttpHandlerFactory::build($httpClient);
|
||||||
|
|
||||||
|
$name = 'pb-';
|
||||||
|
$alphabet = 'abcdefghijklmnopqrstuvwxyz';
|
||||||
|
for ($i = 0; $i < 29; ++$i) {
|
||||||
|
$name .= $alphabet[rand(0, strlen($alphabet) - 1)];
|
||||||
|
}
|
||||||
|
self::$_client = new StorageClientStub(array());
|
||||||
|
self::$_bucket = self::$_client->createBucket($name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUp()
|
||||||
|
{
|
||||||
|
/* Setup Routine */
|
||||||
|
$this->_path = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'privatebin_data';
|
||||||
|
if (!is_dir($this->_path)) {
|
||||||
|
mkdir($this->_path);
|
||||||
|
}
|
||||||
|
$this->_options = array(
|
||||||
|
'bucket' => self::$_bucket->name(),
|
||||||
|
'prefix' => 'pastes',
|
||||||
|
);
|
||||||
|
$this->_data = GoogleCloudStorage::getInstance($this->_options);
|
||||||
|
ServerSalt::setStore($this->_data);
|
||||||
|
TrafficLimiter::setStore($this->_data);
|
||||||
|
$this->reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reset()
|
||||||
|
{
|
||||||
|
parent::reset();
|
||||||
|
// but then inject a db config
|
||||||
|
$options = parse_ini_file(CONF, true);
|
||||||
|
$options['model'] = array(
|
||||||
|
'class' => 'GoogleCloudStorage',
|
||||||
|
);
|
||||||
|
$options['model_options'] = $this->_options;
|
||||||
|
Helper::createIniFile(CONF, $options);
|
||||||
|
}
|
||||||
|
}
|
@ -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());
|
||||||
@ -287,6 +302,48 @@ class DatabaseTest extends PHPUnit_Framework_TestCase
|
|||||||
Helper::rmDir($this->_path);
|
Helper::rmDir($this->_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCorruptMeta()
|
||||||
|
{
|
||||||
|
mkdir($this->_path);
|
||||||
|
$path = $this->_path . DIRECTORY_SEPARATOR . 'meta-test.sq3';
|
||||||
|
if (is_file($path)) {
|
||||||
|
unlink($path);
|
||||||
|
}
|
||||||
|
$this->_options['dsn'] = 'sqlite:' . $path;
|
||||||
|
$this->_options['tbl'] = 'baz_';
|
||||||
|
$model = Database::getInstance($this->_options);
|
||||||
|
$paste = Helper::getPaste(1, array('expire_date' => 1344803344));
|
||||||
|
unset($paste['meta']['formatter'], $paste['meta']['opendiscussion'], $paste['meta']['salt']);
|
||||||
|
$model->delete(Helper::getPasteId());
|
||||||
|
|
||||||
|
$db = new PDO(
|
||||||
|
$this->_options['dsn'],
|
||||||
|
$this->_options['usr'],
|
||||||
|
$this->_options['pwd'],
|
||||||
|
$this->_options['opt']
|
||||||
|
);
|
||||||
|
$statement = $db->prepare('INSERT INTO baz_paste VALUES(?,?,?,?,?,?,?,?,?)');
|
||||||
|
$statement->execute(
|
||||||
|
array(
|
||||||
|
Helper::getPasteId(),
|
||||||
|
$paste['data'],
|
||||||
|
$paste['meta']['postdate'],
|
||||||
|
$paste['meta']['expire_date'],
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
'{',
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$statement->closeCursor();
|
||||||
|
|
||||||
|
$this->assertTrue($model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
||||||
|
$this->assertEquals($paste, $model->read(Helper::getPasteId()));
|
||||||
|
|
||||||
|
Helper::rmDir($this->_path);
|
||||||
|
}
|
||||||
|
|
||||||
public function testTableUpgrade()
|
public function testTableUpgrade()
|
||||||
{
|
{
|
||||||
mkdir($this->_path);
|
mkdir($this->_path);
|
||||||
|
@ -117,6 +117,7 @@ class FilesystemTest extends PHPUnit_Framework_TestCase
|
|||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
|
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
|
||||||
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store broken paste');
|
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store broken paste');
|
||||||
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does still not exist');
|
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does still not exist');
|
||||||
|
$this->assertFalse($this->_model->setValue('foo', 'non existing namespace'), 'rejects setting value in non existing namespace');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCommentErrorDetection()
|
public function testCommentErrorDetection()
|
||||||
|
@ -1,12 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Google\Auth\HttpHandler\HttpHandlerFactory;
|
use Google\Auth\HttpHandler\HttpHandlerFactory;
|
||||||
use Google\Cloud\Core\Exception\BadRequestException;
|
|
||||||
use Google\Cloud\Core\Exception\NotFoundException;
|
|
||||||
use Google\Cloud\Storage\Bucket;
|
|
||||||
use Google\Cloud\Storage\Connection\ConnectionInterface;
|
|
||||||
use Google\Cloud\Storage\StorageClient;
|
|
||||||
use Google\Cloud\Storage\StorageObject;
|
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
use PrivateBin\Data\GoogleCloudStorage;
|
use PrivateBin\Data\GoogleCloudStorage;
|
||||||
|
|
||||||
@ -31,14 +25,11 @@ class GoogleCloudStorageTest extends PHPUnit_Framework_TestCase
|
|||||||
|
|
||||||
public function setUp()
|
public function setUp()
|
||||||
{
|
{
|
||||||
// do not report E_NOTICE as fsouza/fake-gcs-server does not return a `generation` value in the response
|
|
||||||
// which the Google Cloud Storage PHP library expects.
|
|
||||||
error_reporting(E_ERROR | E_WARNING | E_PARSE);
|
|
||||||
ini_set('error_log', stream_get_meta_data(tmpfile())['uri']);
|
ini_set('error_log', stream_get_meta_data(tmpfile())['uri']);
|
||||||
$this->_model = GoogleCloudStorage::getInstance(array(
|
$this->_model = GoogleCloudStorage::getInstance(array(
|
||||||
'bucket' => self::$_bucket->name(),
|
'bucket' => self::$_bucket->name(),
|
||||||
'prefix' => 'pastes',
|
'prefix' => 'pastes',
|
||||||
'client' => self::$_client, ));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown()
|
public function tearDown()
|
||||||
@ -46,7 +37,6 @@ class GoogleCloudStorageTest extends PHPUnit_Framework_TestCase
|
|||||||
foreach (self::$_bucket->objects() as $object) {
|
foreach (self::$_bucket->objects() as $object) {
|
||||||
$object->delete();
|
$object->delete();
|
||||||
}
|
}
|
||||||
error_reporting(E_ALL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function tearDownAfterClass()
|
public static function tearDownAfterClass()
|
||||||
@ -138,579 +128,55 @@ class GoogleCloudStorageTest extends PHPUnit_Framework_TestCase
|
|||||||
$this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'unable to store broken comment');
|
$this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), $comment), 'unable to store broken comment');
|
||||||
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does still not exist');
|
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does still not exist');
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class StorageClientStub provides a limited stub for performing the unit test
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
class StorageClientStub extends StorageClient
|
public function testKeyValueStore()
|
||||||
{
|
|
||||||
private $_config = null;
|
|
||||||
private $_connection = null;
|
|
||||||
private $_buckets = array();
|
|
||||||
|
|
||||||
public function __construct(array $config = array())
|
|
||||||
{
|
{
|
||||||
$this->_config = $config;
|
$salt = bin2hex(random_bytes(256));
|
||||||
$this->_connection = new ConnectionInterfaceStub();
|
$this->_model->setValue($salt, 'salt', '');
|
||||||
}
|
$storedSalt = $this->_model->getValue('salt', '');
|
||||||
|
$this->assertEquals($salt, $storedSalt);
|
||||||
|
$this->_model->purgeValues('salt', time() + 60);
|
||||||
|
$this->assertEquals('', $this->_model->getValue('salt', 'master'));
|
||||||
|
|
||||||
public function bucket($name, $userProject = false)
|
$client = hash_hmac('sha512', '127.0.0.1', $salt);
|
||||||
{
|
$expire = time();
|
||||||
if (!key_exists($name, $this->_buckets)) {
|
$this->_model->setValue(strval($expire), 'traffic_limiter', $client);
|
||||||
$b = new BucketStub($this->_connection, $name, array(), $this);
|
$storedExpired = $this->_model->getValue('traffic_limiter', $client);
|
||||||
$this->_buckets[$name] = $b;
|
$this->assertEquals(strval($expire), $storedExpired);
|
||||||
}
|
|
||||||
return $this->_buckets[$name];
|
$this->_model->purgeValues('traffic_limiter', time() - 60);
|
||||||
|
$this->assertEquals($storedExpired, $this->_model->getValue('traffic_limiter', $client));
|
||||||
|
$this->_model->purgeValues('traffic_limiter', time() + 60);
|
||||||
|
$this->assertEquals('', $this->_model->getValue('traffic_limiter', $client));
|
||||||
|
|
||||||
|
$purgeAt = $expire + (15 * 60);
|
||||||
|
$this->_model->setValue(strval($purgeAt), 'purge_limiter', '');
|
||||||
|
$storedPurgedAt = $this->_model->getValue('purge_limiter', '');
|
||||||
|
$this->assertEquals(strval($purgeAt), $storedPurgedAt);
|
||||||
|
$this->_model->purgeValues('purge_limiter', $purgeAt + 60);
|
||||||
|
$this->assertEquals('', $this->_model->getValue('purge_limiter', ''));
|
||||||
|
$this->assertEquals('', $this->_model->getValue('purge_limiter', 'at'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws \Google\Cloud\Core\Exception\NotFoundException
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function deleteBucket($name)
|
public function testKeyValuePurgeTrafficLimiter()
|
||||||
{
|
{
|
||||||
if (key_exists($name, $this->_buckets)) {
|
$salt = bin2hex(random_bytes(256));
|
||||||
unset($this->_buckets[$name]);
|
$client = hash_hmac('sha512', '127.0.0.1', $salt);
|
||||||
} else {
|
$expire = time();
|
||||||
throw new NotFoundException();
|
$this->_model->setValue(strval($expire), 'traffic_limiter', $client);
|
||||||
}
|
$storedExpired = $this->_model->getValue('traffic_limiter', $client);
|
||||||
}
|
$this->assertEquals(strval($expire), $storedExpired);
|
||||||
|
|
||||||
public function buckets(array $options = array())
|
$this->_model->purgeValues('traffic_limiter', time() - 60);
|
||||||
{
|
$this->assertEquals($storedExpired, $this->_model->getValue('traffic_limiter', $client));
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function registerStreamWrapper($protocol = null)
|
$this->_model->purgeValues('traffic_limiter', time() + 60);
|
||||||
{
|
$this->assertEquals('', $this->_model->getValue('traffic_limiter', $client));
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function unregisterStreamWrapper($protocol = null)
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function signedUrlUploader($uri, $data, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function timestamp(\DateTimeInterface $timestamp, $nanoSeconds = null)
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getServiceAccount(array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hmacKeys(array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function hmacKey($accessId, $projectId = null, array $metadata = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createHmacKey($serviceAccountEmail, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createBucket($name, array $options = array())
|
|
||||||
{
|
|
||||||
if (key_exists($name, $this->_buckets)) {
|
|
||||||
throw new BadRequestException('already exists');
|
|
||||||
}
|
|
||||||
$b = new BucketStub($this->_connection, $name, array(), $this);
|
|
||||||
$this->_buckets[$name] = $b;
|
|
||||||
return $b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class BucketStub stubs a GCS bucket.
|
|
||||||
*/
|
|
||||||
class BucketStub extends Bucket
|
|
||||||
{
|
|
||||||
public $_objects;
|
|
||||||
private $_name;
|
|
||||||
private $_info;
|
|
||||||
private $_connection;
|
|
||||||
private $_client;
|
|
||||||
|
|
||||||
public function __construct(ConnectionInterface $connection, $name, array $info = array(), $client = null)
|
|
||||||
{
|
|
||||||
$this->_name = $name;
|
|
||||||
$this->_info = $info;
|
|
||||||
$this->_connection = $connection;
|
|
||||||
$this->_objects = array();
|
|
||||||
$this->_client = $client;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function acl()
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function defaultAcl()
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function exists()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function upload($data, array $options = array())
|
|
||||||
{
|
|
||||||
if (!is_string($data) || !key_exists('name', $options)) {
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
$name = $options['name'];
|
|
||||||
$generation = '1';
|
|
||||||
$o = new StorageObjectStub($this->_connection, $name, $this, $generation, $options);
|
|
||||||
$this->_objects[$options['name']] = $o;
|
|
||||||
$o->setData($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function uploadAsync($data, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getResumableUploader($data, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getStreamableUploader($data, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function object($name, array $options = array())
|
|
||||||
{
|
|
||||||
if (key_exists($name, $this->_objects)) {
|
|
||||||
return $this->_objects[$name];
|
|
||||||
} else {
|
|
||||||
return new StorageObjectStub($this->_connection, $name, $this, null, $options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function objects(array $options = array())
|
|
||||||
{
|
|
||||||
$prefix = key_exists('prefix', $options) ? $options['prefix'] : '';
|
|
||||||
|
|
||||||
return new CallbackFilterIterator(
|
|
||||||
new ArrayIterator($this->_objects),
|
|
||||||
function ($current, $key, $iterator) use ($prefix) {
|
|
||||||
return substr($key, 0, strlen($prefix)) == $prefix;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createNotification($topic, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function notification($id)
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function notifications(array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function delete(array $options = array())
|
|
||||||
{
|
|
||||||
$this->_client->deleteBucket($this->_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function update(array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function compose(array $sourceObjects, $name, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function info(array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reload(array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function name()
|
|
||||||
{
|
|
||||||
return $this->_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function lifecycle(array $lifecycle = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function currentLifecycle(array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isWritable($file = null)
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function iam()
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function lockRetentionPolicy(array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function signedUrl($expires, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function generateSignedPostPolicyV4($objectName, $expires, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class StorageObjectStub stubs a GCS storage object.
|
|
||||||
*/
|
|
||||||
class StorageObjectStub extends StorageObject
|
|
||||||
{
|
|
||||||
private $_name;
|
|
||||||
private $_data;
|
|
||||||
private $_info;
|
|
||||||
private $_bucket;
|
|
||||||
private $_generation;
|
|
||||||
private $_exists = false;
|
|
||||||
private $_connection;
|
|
||||||
|
|
||||||
public function __construct(ConnectionInterface $connection, $name, $bucket, $generation = null, array $info = array(), $encryptionKey = null, $encryptionKeySHA256 = null)
|
|
||||||
{
|
|
||||||
$this->_name = $name;
|
|
||||||
$this->_bucket = $bucket;
|
|
||||||
$this->_generation = $generation;
|
|
||||||
$this->_info = $info;
|
|
||||||
$this->_connection = $connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function acl()
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function exists(array $options = array())
|
|
||||||
{
|
|
||||||
return key_exists($this->_name, $this->_bucket->_objects);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws NotFoundException
|
|
||||||
*/
|
|
||||||
public function delete(array $options = array())
|
|
||||||
{
|
|
||||||
if (key_exists($this->_name, $this->_bucket->_objects)) {
|
|
||||||
unset($this->_bucket->_objects[$this->_name]);
|
|
||||||
} else {
|
|
||||||
throw new NotFoundException('key ' . $this->_name . ' not found.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws NotFoundException
|
|
||||||
*/
|
|
||||||
public function update(array $metadata, array $options = array())
|
|
||||||
{
|
|
||||||
if (!$this->_exists) {
|
|
||||||
throw new NotFoundException('key ' . $this->_name . ' not found.');
|
|
||||||
}
|
|
||||||
$this->_info = $metadata;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function copy($destination, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rewrite($destination, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rename($name, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @throws NotFoundException
|
|
||||||
*/
|
|
||||||
public function downloadAsString(array $options = array())
|
|
||||||
{
|
|
||||||
if (!$this->_exists) {
|
|
||||||
throw new NotFoundException('key ' . $this->_name . ' not found.');
|
|
||||||
}
|
|
||||||
return $this->_data;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function downloadToFile($path, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function downloadAsStream(array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function downloadAsStreamAsync(array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function signedUrl($expires, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function signedUploadUrl($expires, array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function beginSignedUploadSession(array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function info(array $options = array())
|
|
||||||
{
|
|
||||||
return key_exists('metadata',$this->_info) ? $this->_info['metadata'] : array();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function reload(array $options = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function name()
|
|
||||||
{
|
|
||||||
return $this->_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function identity()
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function gcsUri()
|
|
||||||
{
|
|
||||||
return sprintf(
|
|
||||||
'gs://%s/%s',
|
|
||||||
$this->_bucket->name(),
|
|
||||||
$this->_name
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setData($data)
|
|
||||||
{
|
|
||||||
$this->_data = $data;
|
|
||||||
$this->_exists = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class ConnectionInterfaceStub required for the stubs.
|
|
||||||
*/
|
|
||||||
class ConnectionInterfaceStub implements ConnectionInterface
|
|
||||||
{
|
|
||||||
public function deleteAcl(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAcl(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function listAcl(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function insertAcl(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function patchAcl(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deleteBucket(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getBucket(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function listBuckets(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function insertBucket(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getBucketIamPolicy(array $args)
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setBucketIamPolicy(array $args)
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testBucketIamPermissions(array $args)
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function patchBucket(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deleteObject(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function copyObject(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function rewriteObject(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function composeObject(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getObject(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function listObjects(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function patchObject(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function downloadObject(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function insertObject(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getNotification(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deleteNotification(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function insertNotification(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function listNotifications(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getServiceAccount(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function lockRetentionPolicy(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function createHmacKey(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deleteHmacKey(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getHmacKey(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function updateHmacKey(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function listHmacKeys(array $args = array())
|
|
||||||
{
|
|
||||||
throw new BadMethodCallException('not supported by this stub');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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();
|
||||||
@ -25,8 +25,6 @@ class JsonApiTest extends PHPUnit_Framework_TestCase
|
|||||||
$this->_model->delete(Helper::getPasteId());
|
$this->_model->delete(Helper::getPasteId());
|
||||||
}
|
}
|
||||||
$options = parse_ini_file(CONF_SAMPLE, true);
|
$options = parse_ini_file(CONF_SAMPLE, true);
|
||||||
$options['purge']['dir'] = $this->_path;
|
|
||||||
$options['traffic']['dir'] = $this->_path;
|
|
||||||
$options['model_options']['dir'] = $this->_path;
|
$options['model_options']['dir'] = $this->_path;
|
||||||
Helper::confBackup();
|
Helper::confBackup();
|
||||||
Helper::createIniFile(CONF, $options);
|
Helper::createIniFile(CONF, $options);
|
||||||
|
@ -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';
|
||||||
@ -102,6 +102,58 @@ class ModelTest extends PHPUnit_Framework_TestCase
|
|||||||
$this->assertEquals(array(), $paste->getComments(), 'comment was deleted with paste');
|
$this->assertEquals(array(), $paste->getComments(), 'comment was deleted with paste');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testPasteV1()
|
||||||
|
{
|
||||||
|
$pasteData = Helper::getPaste(1);
|
||||||
|
unset($pasteData['meta']['formatter']);
|
||||||
|
|
||||||
|
$path = $this->_path . DIRECTORY_SEPARATOR . 'v1-test.sq3';
|
||||||
|
if (is_file($path)) {
|
||||||
|
unlink($path);
|
||||||
|
}
|
||||||
|
$options = parse_ini_file(CONF_SAMPLE, true);
|
||||||
|
$options['purge']['limit'] = 0;
|
||||||
|
$options['model'] = array(
|
||||||
|
'class' => 'Database',
|
||||||
|
);
|
||||||
|
$options['model_options'] = array(
|
||||||
|
'dsn' => 'sqlite:' . $path,
|
||||||
|
'usr' => null,
|
||||||
|
'pwd' => null,
|
||||||
|
'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
|
||||||
|
);
|
||||||
|
Helper::createIniFile(CONF, $options);
|
||||||
|
$model = new Model(new Configuration);
|
||||||
|
$model->getPaste('0000000000000000')->exists(); // triggers database table creation
|
||||||
|
$model->getPaste(Helper::getPasteId())->delete(); // deletes the cache
|
||||||
|
|
||||||
|
$db = new PDO(
|
||||||
|
$options['model_options']['dsn'],
|
||||||
|
$options['model_options']['usr'],
|
||||||
|
$options['model_options']['pwd'],
|
||||||
|
$options['model_options']['opt']
|
||||||
|
);
|
||||||
|
$statement = $db->prepare('INSERT INTO paste VALUES(?,?,?,?,?,?,?,?,?)');
|
||||||
|
$statement->execute(
|
||||||
|
array(
|
||||||
|
Helper::getPasteId(),
|
||||||
|
$pasteData['data'],
|
||||||
|
$pasteData['meta']['postdate'],
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
json_encode($pasteData['meta']),
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$statement->closeCursor();
|
||||||
|
|
||||||
|
$paste = $model->getPaste(Helper::getPasteId());
|
||||||
|
$this->assertNotEmpty($paste->getDeleteToken(), 'excercise the condition to load the data from storage');
|
||||||
|
$this->assertEquals('plaintext', $paste->get()['meta']['formatter'], 'paste got created with default formatter');
|
||||||
|
}
|
||||||
|
|
||||||
public function testCommentDefaults()
|
public function testCommentDefaults()
|
||||||
{
|
{
|
||||||
$comment = new Comment(
|
$comment = new Comment(
|
||||||
@ -133,6 +185,97 @@ class ModelTest extends PHPUnit_Framework_TestCase
|
|||||||
$paste->store();
|
$paste->store();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Exception
|
||||||
|
* @expectedExceptionCode 76
|
||||||
|
*/
|
||||||
|
public function testStoreFail()
|
||||||
|
{
|
||||||
|
$path = $this->_path . DIRECTORY_SEPARATOR . 'model-store-test.sq3';
|
||||||
|
if (is_file($path)) {
|
||||||
|
unlink($path);
|
||||||
|
}
|
||||||
|
$options = parse_ini_file(CONF_SAMPLE, true);
|
||||||
|
$options['purge']['limit'] = 0;
|
||||||
|
$options['model'] = array(
|
||||||
|
'class' => 'Database',
|
||||||
|
);
|
||||||
|
$options['model_options'] = array(
|
||||||
|
'dsn' => 'sqlite:' . $path,
|
||||||
|
'usr' => null,
|
||||||
|
'pwd' => null,
|
||||||
|
'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
|
||||||
|
);
|
||||||
|
Helper::createIniFile(CONF, $options);
|
||||||
|
$model = new Model(new Configuration);
|
||||||
|
|
||||||
|
$pasteData = Helper::getPastePost();
|
||||||
|
$model->getPaste(Helper::getPasteId())->delete();
|
||||||
|
$model->getPaste(Helper::getPasteId())->exists();
|
||||||
|
|
||||||
|
$db = new PDO(
|
||||||
|
$options['model_options']['dsn'],
|
||||||
|
$options['model_options']['usr'],
|
||||||
|
$options['model_options']['pwd'],
|
||||||
|
$options['model_options']['opt']
|
||||||
|
);
|
||||||
|
$statement = $db->prepare('DROP TABLE paste');
|
||||||
|
$statement->execute();
|
||||||
|
$statement->closeCursor();
|
||||||
|
|
||||||
|
$paste = $model->getPaste();
|
||||||
|
$paste->setData($pasteData);
|
||||||
|
$paste->store();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Exception
|
||||||
|
* @expectedExceptionCode 70
|
||||||
|
*/
|
||||||
|
public function testCommentStoreFail()
|
||||||
|
{
|
||||||
|
$path = $this->_path . DIRECTORY_SEPARATOR . 'model-test.sq3';
|
||||||
|
if (is_file($path)) {
|
||||||
|
unlink($path);
|
||||||
|
}
|
||||||
|
$options = parse_ini_file(CONF_SAMPLE, true);
|
||||||
|
$options['purge']['limit'] = 0;
|
||||||
|
$options['model'] = array(
|
||||||
|
'class' => 'Database',
|
||||||
|
);
|
||||||
|
$options['model_options'] = array(
|
||||||
|
'dsn' => 'sqlite:' . $path,
|
||||||
|
'usr' => null,
|
||||||
|
'pwd' => null,
|
||||||
|
'opt' => array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION),
|
||||||
|
);
|
||||||
|
Helper::createIniFile(CONF, $options);
|
||||||
|
$model = new Model(new Configuration);
|
||||||
|
|
||||||
|
$pasteData = Helper::getPastePost();
|
||||||
|
$commentData = Helper::getCommentPost();
|
||||||
|
$model->getPaste(Helper::getPasteId())->delete();
|
||||||
|
|
||||||
|
$paste = $model->getPaste();
|
||||||
|
$paste->setData($pasteData);
|
||||||
|
$paste->store();
|
||||||
|
$paste->exists();
|
||||||
|
|
||||||
|
$db = new PDO(
|
||||||
|
$options['model_options']['dsn'],
|
||||||
|
$options['model_options']['usr'],
|
||||||
|
$options['model_options']['pwd'],
|
||||||
|
$options['model_options']['opt']
|
||||||
|
);
|
||||||
|
$statement = $db->prepare('DROP TABLE comment');
|
||||||
|
$statement->execute();
|
||||||
|
$statement->closeCursor();
|
||||||
|
|
||||||
|
$comment = $paste->getComment(Helper::getPasteId());
|
||||||
|
$comment->setData($commentData);
|
||||||
|
$comment->store();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException Exception
|
* @expectedException Exception
|
||||||
* @expectedExceptionCode 69
|
* @expectedExceptionCode 69
|
||||||
@ -195,6 +338,18 @@ class ModelTest extends PHPUnit_Framework_TestCase
|
|||||||
$paste->get();
|
$paste->get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException Exception
|
||||||
|
* @expectedExceptionCode 75
|
||||||
|
*/
|
||||||
|
public function testInvalidPasteFormat()
|
||||||
|
{
|
||||||
|
$pasteData = Helper::getPastePost();
|
||||||
|
$pasteData['adata'][1] = 'format does not exist';
|
||||||
|
$paste = $this->_model->getPaste();
|
||||||
|
$paste->setData($pasteData);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException Exception
|
* @expectedException Exception
|
||||||
* @expectedExceptionCode 60
|
* @expectedExceptionCode 60
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use PrivateBin\Data\Filesystem;
|
||||||
use PrivateBin\Persistence\PurgeLimiter;
|
use PrivateBin\Persistence\PurgeLimiter;
|
||||||
|
|
||||||
class PurgeLimiterTest extends PHPUnit_Framework_TestCase
|
class PurgeLimiterTest extends PHPUnit_Framework_TestCase
|
||||||
@ -13,7 +14,9 @@ class PurgeLimiterTest extends PHPUnit_Framework_TestCase
|
|||||||
if (!is_dir($this->_path)) {
|
if (!is_dir($this->_path)) {
|
||||||
mkdir($this->_path);
|
mkdir($this->_path);
|
||||||
}
|
}
|
||||||
PurgeLimiter::setPath($this->_path);
|
PurgeLimiter::setStore(
|
||||||
|
Filesystem::getInstance(array('dir' => $this->_path))
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tearDown()
|
public function tearDown()
|
||||||
|
@ -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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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');
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user