Merge branch 'master' into crowdin-translation
This commit is contained in:
commit
d699c41e26
19 changed files with 1074 additions and 23 deletions
29
.github/workflows/snyk-scan.yml
vendored
Normal file
29
.github/workflows/snyk-scan.yml
vendored
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
# This is a basic workflow to help you get started with Actions
|
||||||
|
|
||||||
|
name: Snyk scan
|
||||||
|
|
||||||
|
on:
|
||||||
|
# Triggers the workflow on push or pull request events but only for the master branch
|
||||||
|
push:
|
||||||
|
branches: [ master ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ master ]
|
||||||
|
jobs:
|
||||||
|
# https://github.com/snyk/actions/tree/master/php
|
||||||
|
snyk-php:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@master
|
||||||
|
- name: Install Google Cloud Storage
|
||||||
|
run: composer require --no-update google/cloud-storage && composer update --no-dev
|
||||||
|
- name: Run Snyk to check for vulnerabilities
|
||||||
|
uses: snyk/actions/php@master
|
||||||
|
continue-on-error: true # To make sure that SARIF upload gets called
|
||||||
|
env:
|
||||||
|
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
|
||||||
|
with:
|
||||||
|
args: --sarif-file-output=snyk.sarif
|
||||||
|
- name: Upload result to GitHub Code Scanning
|
||||||
|
uses: github/codeql-action/upload-sarif@v1
|
||||||
|
with:
|
||||||
|
sarif_file: snyk.sarif
|
4
.github/workflows/tests.yml
vendored
4
.github/workflows/tests.yml
vendored
|
@ -10,7 +10,7 @@ jobs:
|
||||||
- name: Validate composer.json and composer.lock
|
- name: Validate composer.json and composer.lock
|
||||||
run: composer validate
|
run: composer validate
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: /usr/bin/php7.4 $(which composer) install --prefer-dist --no-suggest
|
run: composer install --prefer-dist --no-dev
|
||||||
PHPunit:
|
PHPunit:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
|
@ -29,6 +29,8 @@ jobs:
|
||||||
run: rm composer.lock
|
run: rm composer.lock
|
||||||
- name: Setup PHPunit
|
- name: Setup PHPunit
|
||||||
run: composer install -n
|
run: composer install -n
|
||||||
|
- name: Install Google Cloud Storage
|
||||||
|
run: composer require google/cloud-storage
|
||||||
- name: Run unit tests
|
- name: Run unit tests
|
||||||
run: ../vendor/bin/phpunit --no-coverage
|
run: ../vendor/bin/phpunit --no-coverage
|
||||||
working-directory: tst
|
working-directory: tst
|
||||||
|
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -6,7 +6,7 @@ cfg/*
|
||||||
!cfg/.htaccess
|
!cfg/.htaccess
|
||||||
|
|
||||||
# Ignore data/
|
# Ignore data/
|
||||||
data/
|
/data/
|
||||||
|
|
||||||
# Ignore PhpDoc
|
# Ignore PhpDoc
|
||||||
doc/*
|
doc/*
|
||||||
|
@ -36,3 +36,5 @@ tst/ConfigurationCombinationsTest.php
|
||||||
.project
|
.project
|
||||||
.externalToolBuilders
|
.externalToolBuilders
|
||||||
.c9
|
.c9
|
||||||
|
/.idea/
|
||||||
|
*.iml
|
||||||
|
|
|
@ -21,7 +21,7 @@ build:
|
||||||
tests:
|
tests:
|
||||||
override:
|
override:
|
||||||
-
|
-
|
||||||
command: 'cd tst && ../vendor/bin/phpunit'
|
command: 'composer require google/cloud-storage && cd tst && ../vendor/bin/phpunit'
|
||||||
coverage:
|
coverage:
|
||||||
file: 'tst/log/coverage-clover.xml'
|
file: 'tst/log/coverage-clover.xml'
|
||||||
format: 'clover'
|
format: 'clover'
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
* ADDED: new HTTP headers improving security (#765)
|
* ADDED: new HTTP headers improving security (#765)
|
||||||
* ADDED: Download button for paste text (#774)
|
* ADDED: Download button for paste text (#774)
|
||||||
* ADDED: Opt-out of federated learning of cohorts (FLoC) (#776)
|
* ADDED: Opt-out of federated learning of cohorts (FLoC) (#776)
|
||||||
* ADDED: Configuration option to exempt ips from the rate-limiter (#787)
|
* ADDED: Configuration option to exempt IPs from the rate-limiter (#787)
|
||||||
|
* ADDED: Google Cloud Storage backend support (#795)
|
||||||
* 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
|
||||||
* **1.3.5 (2021-04-05)**
|
* **1.3.5 (2021-04-05)**
|
||||||
|
|
|
@ -13,7 +13,7 @@ Sébastien Sauvage - original idea and main developer
|
||||||
* Alexey Gladkov - syntax highlighting
|
* Alexey Gladkov - syntax highlighting
|
||||||
* Greg Knaddison - robots.txt
|
* Greg Knaddison - robots.txt
|
||||||
* MrKooky - HTML5 markup, CSS cleanup
|
* MrKooky - HTML5 markup, CSS cleanup
|
||||||
* Simon Rupf - WebCrypto, unit tests, current docker containers, MVC, configuration, i18n
|
* Simon Rupf - WebCrypto, unit tests, containers images, database backend, MVC, configuration, i18n
|
||||||
* Hexalyse - Password protection
|
* Hexalyse - Password protection
|
||||||
* Viktor Stanchev - File upload support
|
* Viktor Stanchev - File upload support
|
||||||
* azlux - Tab character input support
|
* azlux - Tab character input support
|
||||||
|
@ -28,6 +28,7 @@ Sébastien Sauvage - original idea and main developer
|
||||||
* Haocen - lots of bugfixes and UI improvements
|
* Haocen - lots of bugfixes and UI improvements
|
||||||
* Lucas Savva - configurable config file location, NixOS packaging
|
* Lucas Savva - configurable config file location, NixOS packaging
|
||||||
* rodehoed - option to exempt ips from the rate-limiter
|
* rodehoed - option to exempt ips from the rate-limiter
|
||||||
|
* Mark van Holsteijn - Google Cloud Storage backend
|
||||||
|
|
||||||
## Translations
|
## Translations
|
||||||
* Hexalyse - French
|
* Hexalyse - French
|
||||||
|
|
19
INSTALL.md
19
INSTALL.md
|
@ -190,4 +190,21 @@ CREATE TABLE prefix_config (
|
||||||
INSERT INTO prefix_config VALUES('VERSION', '1.3.5');
|
INSERT INTO prefix_config VALUES('VERSION', '1.3.5');
|
||||||
```
|
```
|
||||||
|
|
||||||
In **PostgreSQL**, the data, attachment, nickname and vizhash columns needs to be TEXT and not BLOB or MEDIUMBLOB.
|
In **PostgreSQL**, the data, attachment, nickname and vizhash columns needs to
|
||||||
|
be TEXT and not BLOB or MEDIUMBLOB.
|
||||||
|
|
||||||
|
### Using Google Cloud Storage
|
||||||
|
If you want to deploy PrivateBin in a serverless manner in the Google Cloud, you
|
||||||
|
can choose the `GoogleCloudStorage` as backend. To use this backend, you create
|
||||||
|
a GCS bucket and specify the name as the model option `bucket`. Alternatively,
|
||||||
|
you can set the name through the environment variable PASTEBIN_GCS_BUCKET.
|
||||||
|
|
||||||
|
The default prefix for pastes stored in the bucket is `pastes`. To change the
|
||||||
|
prefix, specify the option `prefix`.
|
||||||
|
|
||||||
|
Google Cloud Storage buckets may be significantly slower than a `FileSystem` or
|
||||||
|
`Database` backend. The big advantage is that the deployment on Google Cloud
|
||||||
|
Platform using Google Cloud Run is easy and cheap.
|
||||||
|
|
||||||
|
To use the Google Cloud Storage backend you have to install the suggested
|
||||||
|
library using the command `composer require google/cloud-storage`.
|
||||||
|
|
2
Makefile
2
Makefile
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
CURRENT_VERSION = 1.3.5
|
CURRENT_VERSION = 1.3.5
|
||||||
VERSION ?= 1.3.6
|
VERSION ?= 1.3.6
|
||||||
VERSION_FILES = index.php cfg/ *.md css/ i18n/ img/ js/privatebin.js lib/ Makefile tpl/ tst/
|
VERSION_FILES = index.php cfg/ *.md css/ i18n/ img/ js/package.json js/privatebin.js lib/ Makefile tpl/ tst/
|
||||||
REGEX_CURRENT_VERSION := $(shell echo $(CURRENT_VERSION) | sed "s/\./\\\./g")
|
REGEX_CURRENT_VERSION := $(shell echo $(CURRENT_VERSION) | sed "s/\./\\\./g")
|
||||||
REGEX_VERSION := $(shell echo $(VERSION) | sed "s/\./\\\./g")
|
REGEX_VERSION := $(shell echo $(VERSION) | sed "s/\./\\\./g")
|
||||||
|
|
||||||
|
|
|
@ -167,6 +167,13 @@ class = Filesystem
|
||||||
[model_options]
|
[model_options]
|
||||||
dir = PATH "data"
|
dir = PATH "data"
|
||||||
|
|
||||||
|
[model]
|
||||||
|
; example of a Google Cloud Storage configuration
|
||||||
|
;class = GoogleCloudStorage
|
||||||
|
;[model_options]
|
||||||
|
;bucket = "my-private-bin"
|
||||||
|
;prefix = "pastes"
|
||||||
|
|
||||||
;[model]
|
;[model]
|
||||||
; example of DB configuration for MySQL
|
; example of DB configuration for MySQL
|
||||||
;class = Database
|
;class = Database
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
"yzalis/identicon" : "2.0.0",
|
"yzalis/identicon" : "2.0.0",
|
||||||
"mlocati/ip-lib" : "1.14.0"
|
"mlocati/ip-lib" : "1.14.0"
|
||||||
},
|
},
|
||||||
|
"suggest" : {
|
||||||
|
"google/cloud-storage" : "1.23.1"
|
||||||
|
},
|
||||||
"require-dev" : {
|
"require-dev" : {
|
||||||
"phpunit/phpunit" : "^4.6 || ^5.0"
|
"phpunit/phpunit" : "^4.6 || ^5.0"
|
||||||
},
|
},
|
||||||
|
|
12
composer.lock
generated
12
composer.lock
generated
|
@ -1419,16 +1419,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/polyfill-ctype",
|
"name": "symfony/polyfill-ctype",
|
||||||
"version": "v1.22.1",
|
"version": "v1.23.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/symfony/polyfill-ctype.git",
|
"url": "https://github.com/symfony/polyfill-ctype.git",
|
||||||
"reference": "c6c942b1ac76c82448322025e084cadc56048b4e"
|
"reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/c6c942b1ac76c82448322025e084cadc56048b4e",
|
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/46cd95797e9df938fdd2b03693b5fca5e64b01ce",
|
||||||
"reference": "c6c942b1ac76c82448322025e084cadc56048b4e",
|
"reference": "46cd95797e9df938fdd2b03693b5fca5e64b01ce",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -1440,7 +1440,7 @@
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"extra": {
|
"extra": {
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.22-dev"
|
"dev-main": "1.23-dev"
|
||||||
},
|
},
|
||||||
"thanks": {
|
"thanks": {
|
||||||
"name": "symfony/polyfill",
|
"name": "symfony/polyfill",
|
||||||
|
@ -1491,7 +1491,7 @@
|
||||||
"type": "tidelift"
|
"type": "tidelift"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2021-01-07T16:49:33+00:00"
|
"time": "2021-02-19T12:13:01+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/yaml",
|
"name": "symfony/yaml",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "privatebin",
|
"name": "privatebin",
|
||||||
"version": "1.3.0",
|
"version": "1.3.5",
|
||||||
"description": "PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bit AES in Galois Counter mode (GCM).",
|
"description": "PrivateBin is a minimalist, open source online pastebin where the server has zero knowledge of pasted data. Data is encrypted/decrypted in the browser using 256 bit AES in Galois Counter mode (GCM).",
|
||||||
"main": "privatebin.js",
|
"main": "privatebin.js",
|
||||||
"directories": {
|
"directories": {
|
||||||
|
|
|
@ -153,6 +153,16 @@ class Configuration
|
||||||
'pwd' => null,
|
'pwd' => null,
|
||||||
'opt' => array(PDO::ATTR_PERSISTENT => true),
|
'opt' => array(PDO::ATTR_PERSISTENT => true),
|
||||||
);
|
);
|
||||||
|
} elseif (
|
||||||
|
$section == 'model_options' && in_array(
|
||||||
|
$this->_configuration['model']['class'],
|
||||||
|
array('GoogleCloudStorage')
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
$values = array(
|
||||||
|
'bucket' => getenv('PRIVATEBIN_GCS_BUCKET') ? getenv('PRIVATEBIN_GCS_BUCKET') : null,
|
||||||
|
'prefix' => 'pastes',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// "*_options" sections don't require all defaults to be set
|
// "*_options" sections don't require all defaults to be set
|
||||||
|
|
251
lib/Data/GoogleCloudStorage.php
Normal file
251
lib/Data/GoogleCloudStorage.php
Normal file
|
@ -0,0 +1,251 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace PrivateBin\Data;
|
||||||
|
|
||||||
|
use Exception;
|
||||||
|
use Google\Cloud\Core\Exception\NotFoundException;
|
||||||
|
use Google\Cloud\Storage\StorageClient;
|
||||||
|
use PrivateBin\Json;
|
||||||
|
|
||||||
|
class GoogleCloudStorage extends AbstractData
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* returns a Google Cloud Storage data backend.
|
||||||
|
*
|
||||||
|
* @access public
|
||||||
|
* @static
|
||||||
|
* @param array $options
|
||||||
|
* @return GoogleCloudStorage
|
||||||
|
*/
|
||||||
|
public static function getInstance(array $options)
|
||||||
|
{
|
||||||
|
$client = null;
|
||||||
|
$bucket = null;
|
||||||
|
$prefix = 'pastes';
|
||||||
|
|
||||||
|
if (getenv('PRIVATEBIN_GCS_BUCKET')) {
|
||||||
|
$bucket = getenv('PRIVATEBIN_GCS_BUCKET');
|
||||||
|
}
|
||||||
|
if (is_array($options) && array_key_exists('bucket', $options)) {
|
||||||
|
$bucket = $options['bucket'];
|
||||||
|
}
|
||||||
|
if (is_array($options) && array_key_exists('prefix', $options)) {
|
||||||
|
$prefix = $options['prefix'];
|
||||||
|
}
|
||||||
|
if (is_array($options) && array_key_exists('client', $options)) {
|
||||||
|
$client = $options['client'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(self::$_instance instanceof self)) {
|
||||||
|
self::$_instance = new self($bucket, $prefix, $client);
|
||||||
|
}
|
||||||
|
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.
|
||||||
|
* @param $pasteid string to get the key for
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
private function _getKey($pasteid)
|
||||||
|
{
|
||||||
|
if ($this->_prefix != '') {
|
||||||
|
return $this->_prefix . '/' . $pasteid;
|
||||||
|
}
|
||||||
|
return $pasteid;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uploads the payload in the $this->_bucket under the specified key.
|
||||||
|
* 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
|
||||||
|
* and salt.
|
||||||
|
*
|
||||||
|
* @param $key string to store the payload under
|
||||||
|
* @param $payload array to store
|
||||||
|
* @return bool true if successful, otherwise false.
|
||||||
|
*/
|
||||||
|
private function upload($key, $payload)
|
||||||
|
{
|
||||||
|
$metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array();
|
||||||
|
unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']);
|
||||||
|
foreach ($metadata as $k => $v) {
|
||||||
|
$metadata[$k] = strval($v);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
$this->_bucket->upload(Json::encode($payload), array(
|
||||||
|
'name' => $key,
|
||||||
|
'chunkSize' => 262144,
|
||||||
|
'predefinedAcl' => 'private',
|
||||||
|
'metadata' => array(
|
||||||
|
'content-type' => 'application/json',
|
||||||
|
'metadata' => $metadata,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('failed to upload ' . $key . ' to ' . $this->_bucket->name() . ', ' .
|
||||||
|
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function create($pasteid, array $paste)
|
||||||
|
{
|
||||||
|
if ($this->exists($pasteid)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->upload($this->_getKey($pasteid), $paste);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function read($pasteid)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$o = $this->_bucket->object($this->_getKey($pasteid));
|
||||||
|
$data = $o->downloadAsString();
|
||||||
|
return Json::decode($data);
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
return false;
|
||||||
|
} catch (Exception $e) {
|
||||||
|
error_log('failed to read ' . $pasteid . ' from ' . $this->_bucket->name() . ', ' .
|
||||||
|
trim(preg_replace('/\s\s+/', ' ', $e->getMessage())));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function delete($pasteid)
|
||||||
|
{
|
||||||
|
$name = $this->_getKey($pasteid);
|
||||||
|
|
||||||
|
try {
|
||||||
|
foreach ($this->_bucket->objects(array('prefix' => $name . '/discussion/')) as $comment) {
|
||||||
|
try {
|
||||||
|
$this->_bucket->object($comment->name())->delete();
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
// ignore if already deleted.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
// there are no discussions associated with the paste
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->_bucket->object($name)->delete();
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
// ignore if already deleted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function exists($pasteid)
|
||||||
|
{
|
||||||
|
$o = $this->_bucket->object($this->_getKey($pasteid));
|
||||||
|
return $o->exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function createComment($pasteid, $parentid, $commentid, array $comment)
|
||||||
|
{
|
||||||
|
if ($this->existsComment($pasteid, $parentid, $commentid)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$key = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid;
|
||||||
|
return $this->upload($key, $comment);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function readComments($pasteid)
|
||||||
|
{
|
||||||
|
$comments = array();
|
||||||
|
$prefix = $this->_getKey($pasteid) . '/discussion/';
|
||||||
|
try {
|
||||||
|
foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $key) {
|
||||||
|
$comment = JSON::decode($this->_bucket->object($key->name())->downloadAsString());
|
||||||
|
$comment['id'] = basename($key->name());
|
||||||
|
$slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']);
|
||||||
|
$comments[$slot] = $comment;
|
||||||
|
}
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
// no comments found
|
||||||
|
}
|
||||||
|
return $comments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
public function existsComment($pasteid, $parentid, $commentid)
|
||||||
|
{
|
||||||
|
$name = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid;
|
||||||
|
$o = $this->_bucket->object($name);
|
||||||
|
return $o->exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritDoc
|
||||||
|
*/
|
||||||
|
protected function _getExpiredPastes($batchsize)
|
||||||
|
{
|
||||||
|
$expired = array();
|
||||||
|
|
||||||
|
$now = time();
|
||||||
|
$prefix = $this->_prefix;
|
||||||
|
if ($prefix != '') {
|
||||||
|
$prefix = $prefix . '/';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $object) {
|
||||||
|
$metadata = $object->info()['metadata'];
|
||||||
|
if ($metadata != null && array_key_exists('expire_date', $metadata)) {
|
||||||
|
$expire_at = intval($metadata['expire_date']);
|
||||||
|
if ($expire_at != 0 && $expire_at < $now) {
|
||||||
|
array_push($expired, basename($object->name()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($expired) > $batchsize) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NotFoundException $e) {
|
||||||
|
// no objects in the bucket yet
|
||||||
|
}
|
||||||
|
return $expired;
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,12 +90,15 @@ abstract class AbstractPersistence
|
||||||
}
|
}
|
||||||
$file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess';
|
$file = self::$_path . DIRECTORY_SEPARATOR . '.htaccess';
|
||||||
if (!is_file($file)) {
|
if (!is_file($file)) {
|
||||||
$writtenBytes = @file_put_contents(
|
$writtenBytes = 0;
|
||||||
$file,
|
if ($fileCreated = @touch($file)) {
|
||||||
'Require all denied' . PHP_EOL,
|
$writtenBytes = @file_put_contents(
|
||||||
LOCK_EX
|
$file,
|
||||||
);
|
'Require all denied' . PHP_EOL,
|
||||||
if ($writtenBytes === false || $writtenBytes < 19) {
|
LOCK_EX
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if ($fileCreated === false || $writtenBytes === false || $writtenBytes < 19) {
|
||||||
throw new Exception('unable to write to file ' . $file, 11);
|
throw new Exception('unable to write to file ' . $file, 11);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,8 +118,15 @@ abstract class AbstractPersistence
|
||||||
{
|
{
|
||||||
self::_initialize();
|
self::_initialize();
|
||||||
$file = self::$_path . DIRECTORY_SEPARATOR . $filename;
|
$file = self::$_path . DIRECTORY_SEPARATOR . $filename;
|
||||||
$writtenBytes = @file_put_contents($file, $data, LOCK_EX);
|
$fileCreated = true;
|
||||||
if ($writtenBytes === false || $writtenBytes < strlen($data)) {
|
$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);
|
throw new Exception('unable to write to file ' . $file, 13);
|
||||||
}
|
}
|
||||||
@chmod($file, 0640); // protect file access
|
@chmod($file, 0640); // protect file access
|
||||||
|
|
|
@ -288,7 +288,7 @@ class Request
|
||||||
}
|
}
|
||||||
krsort($mediaTypes);
|
krsort($mediaTypes);
|
||||||
foreach ($mediaTypes as $acceptedQuality => $acceptedValues) {
|
foreach ($mediaTypes as $acceptedQuality => $acceptedValues) {
|
||||||
if ($acceptedQuality === 0.0) {
|
if ($acceptedQuality === '0.0') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
foreach ($acceptedValues as $acceptedValue) {
|
foreach ($acceptedValues as $acceptedValue) {
|
||||||
|
|
716
tst/Data/GoogleCloudStorageTest.php
Normal file
716
tst/Data/GoogleCloudStorageTest.php
Normal file
|
@ -0,0 +1,716 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
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 PrivateBin\Data\GoogleCloudStorage;
|
||||||
|
|
||||||
|
class GoogleCloudStorageTest extends PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
private static $_client;
|
||||||
|
private static $_bucket;
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
// 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']);
|
||||||
|
$this->_model = GoogleCloudStorage::getInstance(array(
|
||||||
|
'bucket' => self::$_bucket->name(),
|
||||||
|
'prefix' => 'pastes',
|
||||||
|
'client' => self::$_client, ));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tearDown()
|
||||||
|
{
|
||||||
|
foreach (self::$_bucket->objects() as $object) {
|
||||||
|
$object->delete();
|
||||||
|
}
|
||||||
|
error_reporting(E_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function tearDownAfterClass()
|
||||||
|
{
|
||||||
|
self::$_bucket->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFileBasedDataStoreWorks()
|
||||||
|
{
|
||||||
|
$this->_model->delete(Helper::getPasteId());
|
||||||
|
|
||||||
|
// storing pastes
|
||||||
|
$paste = Helper::getPaste(2, array('expire_date' => 1344803344));
|
||||||
|
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
|
||||||
|
$this->assertTrue($this->_model->create(Helper::getPasteId(), $paste), 'store new paste');
|
||||||
|
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
||||||
|
$this->assertFalse($this->_model->create(Helper::getPasteId(), $paste), 'unable to store the same paste twice');
|
||||||
|
$this->assertEquals($paste, $this->_model->read(Helper::getPasteId()));
|
||||||
|
|
||||||
|
// storing comments
|
||||||
|
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
|
||||||
|
$this->assertTrue($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'store comment');
|
||||||
|
$this->assertTrue($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment exists after storing it');
|
||||||
|
$this->assertFalse($this->_model->createComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId(), Helper::getComment()), 'unable to store the same comment twice');
|
||||||
|
$comment = Helper::getComment();
|
||||||
|
$comment['id'] = Helper::getCommentId();
|
||||||
|
$comment['parentid'] = Helper::getPasteId();
|
||||||
|
$this->assertEquals(
|
||||||
|
array($comment['meta']['created'] => $comment),
|
||||||
|
$this->_model->readComments(Helper::getPasteId())
|
||||||
|
);
|
||||||
|
|
||||||
|
// deleting pastes
|
||||||
|
$this->_model->delete(Helper::getPasteId());
|
||||||
|
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste successfully deleted');
|
||||||
|
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment was deleted with paste');
|
||||||
|
$this->assertFalse($this->_model->read(Helper::getPasteId()), 'paste can no longer be found');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* pastes a-g are expired and should get deleted, x never expires and y-z expire in an hour
|
||||||
|
*/
|
||||||
|
public function testPurge()
|
||||||
|
{
|
||||||
|
$expired = Helper::getPaste(2, array('expire_date' => 1344803344));
|
||||||
|
$paste = Helper::getPaste(2, array('expire_date' => time() + 3600));
|
||||||
|
$keys = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'x', 'y', 'z');
|
||||||
|
$ids = array();
|
||||||
|
foreach ($keys as $key) {
|
||||||
|
$ids[$key] = hash('fnv164', $key);
|
||||||
|
$this->assertFalse($this->_model->exists($ids[$key]), "paste $key does not yet exist");
|
||||||
|
if (in_array($key, array('x', 'y', 'z'))) {
|
||||||
|
$this->assertTrue($this->_model->create($ids[$key], $paste), "store $key paste");
|
||||||
|
} elseif ($key === 'x') {
|
||||||
|
$this->assertTrue($this->_model->create($ids[$key], Helper::getPaste()), "store $key paste");
|
||||||
|
} else {
|
||||||
|
$this->assertTrue($this->_model->create($ids[$key], $expired), "store $key paste");
|
||||||
|
}
|
||||||
|
$this->assertTrue($this->_model->exists($ids[$key]), "paste $key exists after storing it");
|
||||||
|
}
|
||||||
|
$this->_model->purge(10);
|
||||||
|
foreach ($ids as $key => $id) {
|
||||||
|
if (in_array($key, array('x', 'y', 'z'))) {
|
||||||
|
$this->assertTrue($this->_model->exists($id), "paste $key exists after purge");
|
||||||
|
$this->_model->delete($id);
|
||||||
|
} else {
|
||||||
|
$this->assertFalse($this->_model->exists($id), "paste $key was purged");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testErrorDetection()
|
||||||
|
{
|
||||||
|
$this->_model->delete(Helper::getPasteId());
|
||||||
|
$paste = Helper::getPaste(2, array('expire' => "Invalid UTF-8 sequence: \xB1\x31"));
|
||||||
|
$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->exists(Helper::getPasteId()), 'paste does still not exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCommentErrorDetection()
|
||||||
|
{
|
||||||
|
$this->_model->delete(Helper::getPasteId());
|
||||||
|
$comment = Helper::getComment(1, array('nickname' => "Invalid UTF-8 sequence: \xB1\x31"));
|
||||||
|
$this->assertFalse($this->_model->exists(Helper::getPasteId()), 'paste does not yet exist');
|
||||||
|
$this->assertTrue($this->_model->create(Helper::getPasteId(), Helper::getPaste()), 'store new paste');
|
||||||
|
$this->assertTrue($this->_model->exists(Helper::getPasteId()), 'paste exists after storing it');
|
||||||
|
$this->assertFalse($this->_model->existsComment(Helper::getPasteId(), Helper::getPasteId(), Helper::getCommentId()), 'comment does not yet exist');
|
||||||
|
$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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
1
vendor/composer/autoload_classmap.php
vendored
1
vendor/composer/autoload_classmap.php
vendored
|
@ -31,6 +31,7 @@ return array(
|
||||||
'PrivateBin\\Data\\AbstractData' => $baseDir . '/lib/Data/AbstractData.php',
|
'PrivateBin\\Data\\AbstractData' => $baseDir . '/lib/Data/AbstractData.php',
|
||||||
'PrivateBin\\Data\\Database' => $baseDir . '/lib/Data/Database.php',
|
'PrivateBin\\Data\\Database' => $baseDir . '/lib/Data/Database.php',
|
||||||
'PrivateBin\\Data\\Filesystem' => $baseDir . '/lib/Data/Filesystem.php',
|
'PrivateBin\\Data\\Filesystem' => $baseDir . '/lib/Data/Filesystem.php',
|
||||||
|
'PrivateBin\\Data\\GoogleCloudStorage' => $baseDir . '/lib/Data/GoogleCloudStorage.php',
|
||||||
'PrivateBin\\Filter' => $baseDir . '/lib/Filter.php',
|
'PrivateBin\\Filter' => $baseDir . '/lib/Filter.php',
|
||||||
'PrivateBin\\FormatV2' => $baseDir . '/lib/FormatV2.php',
|
'PrivateBin\\FormatV2' => $baseDir . '/lib/FormatV2.php',
|
||||||
'PrivateBin\\I18n' => $baseDir . '/lib/I18n.php',
|
'PrivateBin\\I18n' => $baseDir . '/lib/I18n.php',
|
||||||
|
|
1
vendor/composer/autoload_static.php
vendored
1
vendor/composer/autoload_static.php
vendored
|
@ -63,6 +63,7 @@ class ComposerStaticInitDontChange
|
||||||
'PrivateBin\\Data\\AbstractData' => __DIR__ . '/../..' . '/lib/Data/AbstractData.php',
|
'PrivateBin\\Data\\AbstractData' => __DIR__ . '/../..' . '/lib/Data/AbstractData.php',
|
||||||
'PrivateBin\\Data\\Database' => __DIR__ . '/../..' . '/lib/Data/Database.php',
|
'PrivateBin\\Data\\Database' => __DIR__ . '/../..' . '/lib/Data/Database.php',
|
||||||
'PrivateBin\\Data\\Filesystem' => __DIR__ . '/../..' . '/lib/Data/Filesystem.php',
|
'PrivateBin\\Data\\Filesystem' => __DIR__ . '/../..' . '/lib/Data/Filesystem.php',
|
||||||
|
'PrivateBin\\Data\\GoogleCloudStorage' => __DIR__ . '/../..' . '/lib/Data/GoogleCloudStorage.php',
|
||||||
'PrivateBin\\Filter' => __DIR__ . '/../..' . '/lib/Filter.php',
|
'PrivateBin\\Filter' => __DIR__ . '/../..' . '/lib/Filter.php',
|
||||||
'PrivateBin\\FormatV2' => __DIR__ . '/../..' . '/lib/FormatV2.php',
|
'PrivateBin\\FormatV2' => __DIR__ . '/../..' . '/lib/FormatV2.php',
|
||||||
'PrivateBin\\I18n' => __DIR__ . '/../..' . '/lib/I18n.php',
|
'PrivateBin\\I18n' => __DIR__ . '/../..' . '/lib/I18n.php',
|
||||||
|
|
Loading…
Reference in a new issue