1 | <?php |
||
2 | |||
3 | namespace PrivateBin\Data; |
||
4 | |||
5 | use Exception; |
||
6 | use Google\Cloud\Core\Exception\NotFoundException; |
||
0 ignored issues
–
show
|
|||
7 | use Google\Cloud\Storage\Bucket; |
||
0 ignored issues
–
show
The type
Google\Cloud\Storage\Bucket was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||
8 | use Google\Cloud\Storage\StorageClient; |
||
0 ignored issues
–
show
The type
Google\Cloud\Storage\StorageClient was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths
Loading history...
|
|||
9 | use PrivateBin\Json; |
||
10 | |||
11 | class GoogleCloudStorage extends AbstractData |
||
12 | { |
||
13 | /** |
||
14 | * GCS client |
||
15 | * |
||
16 | * @access private |
||
17 | * @var StorageClient |
||
18 | */ |
||
19 | private $_client = null; |
||
20 | |||
21 | /** |
||
22 | * GCS bucket |
||
23 | * |
||
24 | * @access private |
||
25 | * @var Bucket |
||
26 | */ |
||
27 | private $_bucket = null; |
||
28 | |||
29 | /** |
||
30 | * object prefix |
||
31 | * |
||
32 | * @access private |
||
33 | * @var string |
||
34 | */ |
||
35 | private $_prefix = 'pastes'; |
||
36 | |||
37 | /** |
||
38 | * bucket acl type |
||
39 | * |
||
40 | * @access private |
||
41 | * @var bool |
||
42 | */ |
||
43 | private $_uniformacl = false; |
||
44 | |||
45 | /** |
||
46 | * instantiantes a new Google Cloud Storage data backend. |
||
47 | * |
||
48 | * @access public |
||
49 | * @param array $options |
||
50 | */ |
||
51 | 43 | public function __construct(array $options) |
|
52 | { |
||
53 | 43 | if (getenv('PRIVATEBIN_GCS_BUCKET')) { |
|
54 | $bucket = getenv('PRIVATEBIN_GCS_BUCKET'); |
||
55 | } |
||
56 | 43 | if (is_array($options) && array_key_exists('bucket', $options)) { |
|
57 | 43 | $bucket = $options['bucket']; |
|
58 | } |
||
59 | 43 | if (is_array($options) && array_key_exists('prefix', $options)) { |
|
60 | 43 | $this->_prefix = $options['prefix']; |
|
61 | } |
||
62 | 43 | if (is_array($options) && array_key_exists('uniformacl', $options)) { |
|
63 | 32 | $this->_uniformacl = $options['uniformacl']; |
|
64 | } |
||
65 | |||
66 | 43 | $this->_client = class_exists('StorageClientStub', false) ? |
|
67 | 43 | new \StorageClientStub(array()) : |
|
68 | new StorageClient(array('suppressKeyFileNotice' => true)); |
||
69 | 43 | if (isset($bucket)) { |
|
70 | 43 | $this->_bucket = $this->_client->bucket($bucket); |
|
71 | } |
||
72 | } |
||
73 | |||
74 | /** |
||
75 | * returns the google storage object key for $pasteid in $this->_bucket. |
||
76 | * |
||
77 | * @access private |
||
78 | * @param $pasteid string to get the key for |
||
79 | * @return string |
||
80 | */ |
||
81 | 41 | private function _getKey($pasteid) |
|
82 | { |
||
83 | 41 | if ($this->_prefix != '') { |
|
84 | 41 | return $this->_prefix . '/' . $pasteid; |
|
85 | } |
||
86 | return $pasteid; |
||
87 | } |
||
88 | |||
89 | /** |
||
90 | * Uploads the payload in the $this->_bucket under the specified key. |
||
91 | * The entire payload is stored as a JSON document. The metadata is replicated |
||
92 | * as the GCS object's metadata except for the fields attachment, attachmentname |
||
93 | * and salt. |
||
94 | * |
||
95 | * @param $key string to store the payload under |
||
96 | * @param $payload array to store |
||
97 | * @return bool true if successful, otherwise false. |
||
98 | */ |
||
99 | 27 | private function _upload($key, $payload) |
|
100 | { |
||
101 | 27 | $metadata = array_key_exists('meta', $payload) ? $payload['meta'] : array(); |
|
102 | 27 | unset($metadata['attachment'], $metadata['attachmentname'], $metadata['salt']); |
|
103 | 27 | foreach ($metadata as $k => $v) { |
|
104 | 27 | $metadata[$k] = strval($v); |
|
105 | } |
||
106 | try { |
||
107 | 27 | $data = array( |
|
108 | 27 | 'name' => $key, |
|
109 | 27 | 'chunkSize' => 262144, |
|
110 | 27 | 'metadata' => array( |
|
111 | 27 | 'content-type' => 'application/json', |
|
112 | 27 | 'metadata' => $metadata, |
|
113 | 27 | ), |
|
114 | 27 | ); |
|
115 | 27 | if (!$this->_uniformacl) { |
|
116 | 27 | $data['predefinedAcl'] = 'private'; |
|
117 | } |
||
118 | 27 | $this->_bucket->upload(Json::encode($payload), $data); |
|
119 | 2 | } catch (Exception $e) { |
|
120 | 2 | error_log('failed to upload ' . $key . ' to ' . $this->_bucket->name() . ', ' . |
|
121 | 2 | trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); |
|
122 | 2 | return false; |
|
123 | } |
||
124 | 26 | return true; |
|
125 | } |
||
126 | |||
127 | /** |
||
128 | * @inheritDoc |
||
129 | */ |
||
130 | 27 | public function create($pasteid, array $paste) |
|
131 | { |
||
132 | 27 | if ($this->exists($pasteid)) { |
|
133 | 1 | return false; |
|
134 | } |
||
135 | |||
136 | 27 | return $this->_upload($this->_getKey($pasteid), $paste); |
|
137 | } |
||
138 | |||
139 | /** |
||
140 | * @inheritDoc |
||
141 | */ |
||
142 | 20 | public function read($pasteid) |
|
143 | { |
||
144 | try { |
||
145 | 20 | $o = $this->_bucket->object($this->_getKey($pasteid)); |
|
146 | 20 | $data = $o->downloadAsString(); |
|
147 | 20 | return Json::decode($data); |
|
148 | 1 | } catch (NotFoundException $e) { |
|
149 | 1 | return false; |
|
150 | } catch (Exception $e) { |
||
151 | error_log('failed to read ' . $pasteid . ' from ' . $this->_bucket->name() . ', ' . |
||
152 | trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); |
||
153 | return false; |
||
154 | } |
||
155 | } |
||
156 | |||
157 | /** |
||
158 | * @inheritDoc |
||
159 | */ |
||
160 | 11 | public function delete($pasteid) |
|
161 | { |
||
162 | 11 | $name = $this->_getKey($pasteid); |
|
163 | |||
164 | try { |
||
165 | 11 | foreach ($this->_bucket->objects(array('prefix' => $name . '/discussion/')) as $comment) { |
|
166 | try { |
||
167 | 1 | $this->_bucket->object($comment->name())->delete(); |
|
168 | } catch (NotFoundException $e) { |
||
169 | // ignore if already deleted. |
||
170 | } |
||
171 | } |
||
172 | } catch (NotFoundException $e) { |
||
173 | // there are no discussions associated with the paste |
||
174 | } |
||
175 | |||
176 | try { |
||
177 | 11 | $this->_bucket->object($name)->delete(); |
|
178 | 3 | } catch (NotFoundException $e) { |
|
179 | // ignore if already deleted |
||
180 | } |
||
181 | } |
||
182 | |||
183 | /** |
||
184 | * @inheritDoc |
||
185 | */ |
||
186 | 41 | public function exists($pasteid) |
|
187 | { |
||
188 | 41 | $o = $this->_bucket->object($this->_getKey($pasteid)); |
|
189 | 41 | return $o->exists(); |
|
190 | } |
||
191 | |||
192 | /** |
||
193 | * @inheritDoc |
||
194 | */ |
||
195 | 4 | public function createComment($pasteid, $parentid, $commentid, array $comment) |
|
196 | { |
||
197 | 4 | if ($this->existsComment($pasteid, $parentid, $commentid)) { |
|
198 | 1 | return false; |
|
199 | } |
||
200 | 4 | $key = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid; |
|
201 | 4 | return $this->_upload($key, $comment); |
|
202 | } |
||
203 | |||
204 | /** |
||
205 | * @inheritDoc |
||
206 | */ |
||
207 | 12 | public function readComments($pasteid) |
|
208 | { |
||
209 | 12 | $comments = array(); |
|
210 | 12 | $prefix = $this->_getKey($pasteid) . '/discussion/'; |
|
211 | try { |
||
212 | 12 | foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $key) { |
|
213 | 2 | $comment = JSON::decode($this->_bucket->object($key->name())->downloadAsString()); |
|
214 | 2 | $comment['id'] = basename($key->name()); |
|
215 | 2 | $slot = $this->getOpenSlot($comments, (int) $comment['meta']['created']); |
|
216 | 2 | $comments[$slot] = $comment; |
|
217 | } |
||
218 | } catch (NotFoundException $e) { |
||
219 | // no comments found |
||
220 | } |
||
221 | 12 | return $comments; |
|
222 | } |
||
223 | |||
224 | /** |
||
225 | * @inheritDoc |
||
226 | */ |
||
227 | 7 | public function existsComment($pasteid, $parentid, $commentid) |
|
228 | { |
||
229 | 7 | $name = $this->_getKey($pasteid) . '/discussion/' . $parentid . '/' . $commentid; |
|
230 | 7 | $o = $this->_bucket->object($name); |
|
231 | 7 | return $o->exists(); |
|
232 | } |
||
233 | |||
234 | /** |
||
235 | * @inheritDoc |
||
236 | */ |
||
237 | 6 | public function purgeValues($namespace, $time) |
|
238 | { |
||
239 | 6 | $path = 'config/' . $namespace; |
|
240 | try { |
||
241 | 6 | foreach ($this->_bucket->objects(array('prefix' => $path)) as $object) { |
|
242 | 3 | $name = $object->name(); |
|
243 | 3 | if (strlen($name) > strlen($path) && substr($name, strlen($path), 1) !== '/') { |
|
244 | continue; |
||
245 | } |
||
246 | 3 | $info = $object->info(); |
|
247 | 3 | if (key_exists('metadata', $info) && key_exists('value', $info['metadata'])) { |
|
248 | 3 | $value = $info['metadata']['value']; |
|
249 | 3 | if (is_numeric($value) && intval($value) < $time) { |
|
250 | try { |
||
251 | 2 | $object->delete(); |
|
252 | } catch (NotFoundException $e) { |
||
253 | // deleted by another instance. |
||
254 | } |
||
255 | } |
||
256 | } |
||
257 | } |
||
258 | } catch (NotFoundException $e) { |
||
259 | // no objects in the bucket yet |
||
260 | } |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * For GoogleCloudStorage, the value will also be stored in the metadata for the |
||
265 | * namespaces traffic_limiter and purge_limiter. |
||
266 | * @inheritDoc |
||
267 | */ |
||
268 | 18 | public function setValue($value, $namespace, $key = '') |
|
269 | { |
||
270 | 18 | if ($key === '') { |
|
271 | 17 | $key = 'config/' . $namespace; |
|
272 | } else { |
||
273 | 6 | $key = 'config/' . $namespace . '/' . $key; |
|
274 | } |
||
275 | |||
276 | 18 | $metadata = array('namespace' => $namespace); |
|
277 | 18 | if ($namespace != 'salt') { |
|
278 | 13 | $metadata['value'] = strval($value); |
|
279 | } |
||
280 | try { |
||
281 | 18 | $data = array( |
|
282 | 18 | 'name' => $key, |
|
283 | 18 | 'chunkSize' => 262144, |
|
284 | 18 | 'metadata' => array( |
|
285 | 18 | 'content-type' => 'application/json', |
|
286 | 18 | 'metadata' => $metadata, |
|
287 | 18 | ), |
|
288 | 18 | ); |
|
289 | 18 | if (!$this->_uniformacl) { |
|
290 | 18 | $data['predefinedAcl'] = 'private'; |
|
291 | } |
||
292 | 18 | $this->_bucket->upload($value, $data); |
|
293 | } catch (Exception $e) { |
||
294 | error_log('failed to set key ' . $key . ' to ' . $this->_bucket->name() . ', ' . |
||
295 | trim(preg_replace('/\s\s+/', ' ', $e->getMessage()))); |
||
296 | return false; |
||
297 | } |
||
298 | 18 | return true; |
|
299 | } |
||
300 | |||
301 | /** |
||
302 | * @inheritDoc |
||
303 | */ |
||
304 | 18 | public function getValue($namespace, $key = '') |
|
305 | { |
||
306 | 18 | if ($key === '') { |
|
307 | 17 | $key = 'config/' . $namespace; |
|
308 | } else { |
||
309 | 6 | $key = 'config/' . $namespace . '/' . $key; |
|
310 | } |
||
311 | try { |
||
312 | 18 | $o = $this->_bucket->object($key); |
|
313 | 18 | return $o->downloadAsString(); |
|
314 | 18 | } catch (NotFoundException $e) { |
|
315 | 18 | return ''; |
|
316 | } |
||
317 | } |
||
318 | |||
319 | /** |
||
320 | * @inheritDoc |
||
321 | */ |
||
322 | 11 | protected function _getExpiredPastes($batchsize) |
|
323 | { |
||
324 | 11 | $expired = array(); |
|
325 | |||
326 | 11 | $now = time(); |
|
327 | 11 | $prefix = $this->_prefix; |
|
328 | 11 | if ($prefix != '') { |
|
329 | 11 | $prefix .= '/'; |
|
330 | } |
||
331 | try { |
||
332 | 11 | foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $object) { |
|
333 | 2 | $metadata = $object->info()['metadata']; |
|
334 | 2 | if ($metadata != null && array_key_exists('expire_date', $metadata)) { |
|
335 | 1 | $expire_at = intval($metadata['expire_date']); |
|
336 | 1 | if ($expire_at != 0 && $expire_at < $now) { |
|
337 | 1 | array_push($expired, basename($object->name())); |
|
338 | } |
||
339 | } |
||
340 | |||
341 | 2 | if (count($expired) > $batchsize) { |
|
342 | break; |
||
343 | } |
||
344 | } |
||
345 | } catch (NotFoundException $e) { |
||
346 | // no objects in the bucket yet |
||
347 | } |
||
348 | 11 | return $expired; |
|
349 | } |
||
350 | |||
351 | /** |
||
352 | * @inheritDoc |
||
353 | */ |
||
354 | public function getAllPastes() |
||
355 | { |
||
356 | $pastes = array(); |
||
357 | $prefix = $this->_prefix; |
||
358 | if ($prefix != '') { |
||
359 | $prefix .= '/'; |
||
360 | } |
||
361 | |||
362 | try { |
||
363 | foreach ($this->_bucket->objects(array('prefix' => $prefix)) as $object) { |
||
364 | $candidate = substr($object->name(), strlen($prefix)); |
||
365 | if (strpos($candidate, '/') === false) { |
||
366 | $pastes[] = $candidate; |
||
367 | } |
||
368 | } |
||
369 | } catch (NotFoundException $e) { |
||
370 | // no objects in the bucket yet |
||
371 | } |
||
372 | return $pastes; |
||
373 | } |
||
374 | } |
||
375 |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths