This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | namespace Kapersoft\FlysystemSharefile; |
||
4 | |||
5 | use Exception; |
||
6 | use League\Flysystem\Util; |
||
7 | use League\Flysystem\Config; |
||
8 | use Kapersoft\Sharefile\Client; |
||
9 | use League\Flysystem\Adapter\AbstractAdapter; |
||
10 | use League\Flysystem\Adapter\Polyfill\StreamedTrait; |
||
11 | use League\Flysystem\Adapter\Polyfill\NotSupportingVisibilityTrait; |
||
12 | |||
13 | /** |
||
14 | * Flysysten ShareFile Adapter. |
||
15 | * |
||
16 | * @author Jan Willem Kaper <[email protected]> |
||
17 | * @license MIT (see License.txt) |
||
18 | * |
||
19 | * @link http://github.com/kapersoft/flysystem-sharefile |
||
20 | */ |
||
21 | class SharefileAdapter extends AbstractAdapter |
||
22 | { |
||
23 | use StreamedTrait; |
||
24 | use NotSupportingVisibilityTrait; |
||
25 | |||
26 | /** ShareFile access control constants */ |
||
27 | const CAN_ADD_FOLDER = 'CanAddFolder'; |
||
28 | const ADD_NODE = 'CanAddNode'; |
||
29 | const CAN_VIEW = 'CanView'; |
||
30 | const CAN_DOWNLOAD = 'CanDownload'; |
||
31 | const CAN_UPLOAD = 'CanUpload'; |
||
32 | const CAN_SEND = 'CanSend'; |
||
33 | const CAN_DELETE_CURRENT_ITEM = 'CanDeleteCurrentItem'; |
||
34 | const CAN_DELETE_CHILD_ITEMS = 'CanDeleteChildItems'; |
||
35 | const CAN_MANAGE_PERMISSIONS = 'CanManagePermissions'; |
||
36 | const CAN_CREATEOFFICE_DOCUMENTS = 'CanCreateOfficeDocuments'; |
||
37 | |||
38 | /** |
||
39 | * ShareFile Client. |
||
40 | * |
||
41 | * @var \Kapersoft\Sharefile\Client; |
||
42 | * */ |
||
43 | protected $client; |
||
44 | |||
45 | /** |
||
46 | * Indicated if metadata should include the ShareFile item array. |
||
47 | * |
||
48 | * @var bool |
||
49 | * */ |
||
50 | protected $returnShareFileItem; |
||
51 | |||
52 | /** |
||
53 | * SharefileAdapter constructor. |
||
54 | * |
||
55 | * @param Client $client Instance of Kapersoft\Sharefile\Client |
||
56 | * @param string $prefix Folder prefix |
||
57 | * @param bool $returnShareFileItem Indicated if getMetadatsa/listContents should return ShareFile item array. |
||
58 | * |
||
59 | * @param string $prefix |
||
60 | */ |
||
61 | 723 | public function __construct(Client $client, string $prefix = '', bool $returnShareFileItem = false) |
|
62 | { |
||
63 | 723 | $this->client = $client; |
|
64 | |||
65 | 723 | $this->returnShareFileItem = $returnShareFileItem; |
|
66 | |||
67 | 723 | $this->setPathPrefix($prefix); |
|
68 | 723 | } |
|
69 | |||
70 | /** |
||
71 | * {@inheritdoc} |
||
72 | */ |
||
73 | 240 | public function has($path) |
|
74 | { |
||
75 | 240 | return $this->getMetadata($path); |
|
76 | } |
||
77 | |||
78 | /** |
||
79 | * {@inheritdoc} |
||
80 | */ |
||
81 | 60 | View Code Duplication | public function read($path) |
0 ignored issues
–
show
|
|||
82 | { |
||
83 | 60 | if (!$item = $this->getItemByPath($path)) { |
|
84 | 30 | return false; |
|
85 | } |
||
86 | |||
87 | 30 | if (!$this->checkAccessControl($item, self::CAN_DOWNLOAD)) { |
|
88 | return false; |
||
89 | } |
||
90 | |||
91 | 30 | $contents = $this->client->getItemContents($item['Id']); |
|
92 | |||
93 | 30 | return $this->mapItemInfo($item, Util::dirname($path), $contents); |
|
94 | } |
||
95 | |||
96 | /** |
||
97 | * {@inheritdoc} |
||
98 | */ |
||
99 | 60 | public function listContents($directory = '', $recursive = false) |
|
100 | { |
||
101 | 60 | if (!$item = $this->getItemByPath($directory)) { |
|
102 | 30 | return false; |
|
0 ignored issues
–
show
The return type of
return false; (false ) is incompatible with the return type declared by the interface League\Flysystem\ReadInterface::listContents of type array .
If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design. Let’s take a look at an example: class Author {
private $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
abstract class Post {
public function getAuthor() {
return 'Johannes';
}
}
class BlogPost extends Post {
public function getAuthor() {
return new Author('Johannes');
}
}
class ForumPost extends Post { /* ... */ }
function my_function(Post $post) {
echo strtoupper($post->getAuthor());
}
Our function ![]() |
|||
103 | } |
||
104 | |||
105 | 30 | return $this->buildItemList($item, $directory, $recursive); |
|
106 | } |
||
107 | |||
108 | /** |
||
109 | * {@inheritdoc} |
||
110 | */ |
||
111 | 540 | public function getMetadata($path) |
|
112 | { |
||
113 | 540 | if (!$item = $this->getItemByPath($path)) { |
|
114 | 180 | return false; |
|
115 | } |
||
116 | 360 | $metadata = $this->mapItemInfo($item, Util::dirname($path)); |
|
117 | |||
118 | 360 | if (in_array($path, ['/', ''], true)) { |
|
119 | $metadata['path'] = $path; |
||
120 | } |
||
121 | |||
122 | 360 | return $metadata; |
|
123 | } |
||
124 | |||
125 | /** |
||
126 | * {@inheritdoc} |
||
127 | */ |
||
128 | 60 | public function getSize($path) |
|
129 | { |
||
130 | 60 | return $this->getMetadata($path); |
|
131 | } |
||
132 | |||
133 | /** |
||
134 | * {@inheritdoc} |
||
135 | */ |
||
136 | public function getMimetype($path) |
||
137 | { |
||
138 | return $this->getMetadata($path); |
||
139 | } |
||
140 | |||
141 | /** |
||
142 | * {@inheritdoc} |
||
143 | */ |
||
144 | 60 | public function getTimestamp($path) |
|
145 | { |
||
146 | 60 | return $this->getmetaData($path); |
|
147 | } |
||
148 | |||
149 | /** |
||
150 | * {@inheritdoc} |
||
151 | */ |
||
152 | 150 | public function write($path, $contents, Config $config = null) |
|
153 | { |
||
154 | 150 | return $this->uploadFile($path, $contents, true); |
|
155 | } |
||
156 | |||
157 | /** |
||
158 | * {@inheritdoc} |
||
159 | */ |
||
160 | 150 | public function update($path, $contents, Config $config = null) |
|
161 | { |
||
162 | 150 | return $this->uploadFile($path, $contents, true); |
|
163 | } |
||
164 | |||
165 | /** |
||
166 | * {@inheritdoc} |
||
167 | */ |
||
168 | 180 | public function rename($path, $newpath) |
|
169 | { |
||
170 | 180 | if (!$targetFolderItem = $this->getItemByPath(Util::dirname($newpath))) { |
|
171 | 30 | return false; |
|
172 | } |
||
173 | |||
174 | 150 | if (!$this->checkAccessControl($targetFolderItem, self::CAN_UPLOAD)) { |
|
175 | 60 | return false; |
|
176 | } |
||
177 | |||
178 | 90 | if (!$item = $this->getItemByPath($path)) { |
|
179 | 30 | return false; |
|
180 | } |
||
181 | |||
182 | $data = [ |
||
183 | 60 | 'FileName' => basename($newpath), |
|
184 | 60 | 'Name' => basename($newpath), |
|
185 | 'Parent' => [ |
||
186 | 60 | 'Id' => $targetFolderItem['Id'], |
|
187 | ], |
||
188 | ]; |
||
189 | |||
190 | 60 | $this->client->updateItem($item['Id'], $data); |
|
191 | |||
192 | 60 | return is_array($this->has($newpath)); |
|
193 | } |
||
194 | |||
195 | /** |
||
196 | * {@inheritdoc} |
||
197 | */ |
||
198 | 180 | public function copy($path, $newpath) |
|
199 | { |
||
200 | 180 | if (!$targetFolderItem = $this->getItemByPath(Util::dirname($newpath))) { |
|
201 | 30 | return false; |
|
202 | } |
||
203 | |||
204 | 150 | if (!$this->checkAccessControl($targetFolderItem, self::CAN_UPLOAD)) { |
|
205 | 60 | return false; |
|
206 | } |
||
207 | |||
208 | 90 | if (!$item = $this->getItemByPath($path)) { |
|
209 | 30 | return false; |
|
210 | } |
||
211 | |||
212 | 60 | if (strcasecmp(Util::dirname($path), Util::dirname($newpath)) != 0 && |
|
213 | 60 | strcasecmp(basename($path), basename($newpath)) == 0) { |
|
214 | 30 | $this->client->copyItem($targetFolderItem['Id'], $item['Id'], true); |
|
215 | } else { |
||
216 | 30 | $contents = $this->client->getItemContents($item['Id']); |
|
217 | 30 | $this->uploadFile($newpath, $contents, true); |
|
218 | } |
||
219 | |||
220 | 60 | return is_array($this->has($newpath)); |
|
221 | } |
||
222 | |||
223 | /** |
||
224 | * {@inheritdoc} |
||
225 | */ |
||
226 | 60 | public function delete($path) |
|
227 | { |
||
228 | 60 | return $this->deleteDir($path); |
|
229 | } |
||
230 | |||
231 | /** |
||
232 | * {@inheritdoc} |
||
233 | */ |
||
234 | 60 | public function deleteDir($dirname) |
|
235 | { |
||
236 | 60 | if (!$item = $this->getItemByPath($dirname)) { |
|
237 | 30 | return false; |
|
238 | } |
||
239 | |||
240 | 30 | if (!$this->checkAccessControl($item, self::CAN_DELETE_CURRENT_ITEM)) { |
|
241 | return false; |
||
242 | } |
||
243 | |||
244 | 30 | $this->client->deleteItem($item['Id']); |
|
245 | |||
246 | 30 | return $this->has($dirname) === false; |
|
247 | } |
||
248 | |||
249 | /** |
||
250 | * {@inheritdoc} |
||
251 | */ |
||
252 | 120 | public function createDir($dirname, Config $config = null) |
|
253 | { |
||
254 | 120 | $parentFolder = Util::dirname($dirname); |
|
255 | 120 | $folder = basename($dirname); |
|
256 | |||
257 | 120 | if (!$parentFolderItem = $this->getItemByPath($parentFolder)) { |
|
258 | 30 | return false; |
|
259 | } |
||
260 | |||
261 | 90 | if (!$this->checkAccessControl($parentFolderItem, self::CAN_ADD_FOLDER)) { |
|
262 | 60 | return false; |
|
263 | } |
||
264 | |||
265 | 30 | $this->client->createFolder($parentFolderItem['Id'], $folder, $folder, true); |
|
266 | |||
267 | 30 | return $this->has($dirname); |
|
268 | } |
||
269 | |||
270 | /** |
||
271 | * {@inheritdoc} |
||
272 | */ |
||
273 | 120 | public function put($path, $contents) |
|
274 | { |
||
275 | 120 | return $this->uploadFile($path, $contents, true); |
|
276 | } |
||
277 | |||
278 | /** |
||
279 | * {@inheritdoc} |
||
280 | */ |
||
281 | 30 | View Code Duplication | public function readAndDelete($path) |
0 ignored issues
–
show
This method seems to be duplicated in your project.
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation. You can also find more detailed suggestions in the “Code” section of your repository. ![]() |
|||
282 | { |
||
283 | 30 | if (!$item = $this->getItemByPath($path)) { |
|
284 | 30 | return false; |
|
285 | } |
||
286 | |||
287 | if (!$this->checkAccessControl($item, self::CAN_DOWNLOAD) || |
||
288 | !$this->checkAccessControl($item, self::CAN_DELETE_CURRENT_ITEM)) { |
||
289 | return false; |
||
290 | } |
||
291 | |||
292 | $itemContents = $this->client->getItemContents($item['Id']); |
||
293 | |||
294 | $this->delete($path); |
||
295 | |||
296 | return $itemContents; |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Returns ShareFile client. |
||
301 | * |
||
302 | * @return Client |
||
303 | */ |
||
304 | 3 | public function getClient(): Client |
|
305 | { |
||
306 | 3 | return $this->client; |
|
307 | } |
||
308 | |||
309 | /** |
||
310 | * Upload a file to ShareFile. |
||
311 | * |
||
312 | * @param string $path File path |
||
313 | * @param string $contents Contents of the file |
||
314 | * @param bool $overwrite Overwrite file is it exists |
||
315 | * |
||
316 | * @return array|false |
||
317 | */ |
||
318 | 240 | protected function uploadFile(string $path, string $contents, bool $overwrite = false) |
|
319 | { |
||
320 | 240 | if (!$parentFolderItem = $this->getItemByPath(Util::dirname($path))) { |
|
321 | 30 | return false; |
|
322 | } |
||
323 | |||
324 | 210 | if (!$this->checkAccessControl($parentFolderItem, self::CAN_UPLOAD)) { |
|
325 | 60 | return false; |
|
326 | } |
||
327 | |||
328 | 150 | $filename = $this->prepareUploadFile(basename($path), $contents); |
|
329 | |||
330 | 150 | $this->client->uploadFileStandard($filename, $parentFolderItem['Id'], false, $overwrite); |
|
331 | |||
332 | 150 | $this->removeUploadFile($filename); |
|
333 | |||
334 | 150 | if ($metadata = $this->getMetadata($path)) { |
|
335 | 150 | $metadata['contents'] = $contents; |
|
336 | |||
337 | 150 | return $metadata; |
|
338 | } |
||
339 | |||
340 | return false; |
||
341 | } |
||
342 | |||
343 | /** |
||
344 | * Prepares upload-file. |
||
345 | * |
||
346 | * @param string $filename Filename |
||
347 | * @param string $contents Contents of the file |
||
348 | * |
||
349 | * @return string |
||
350 | */ |
||
351 | 150 | protected function prepareUploadFile(string $filename, string $contents):string |
|
352 | { |
||
353 | 150 | $filename = tempnam(sys_get_temp_dir(), '') . '/' . $filename; |
|
354 | 150 | unlink(Util::dirname($filename)); |
|
355 | 150 | mkdir(Util::dirname($filename)); |
|
356 | 150 | file_put_contents($filename, $contents); |
|
357 | |||
358 | 150 | return $filename; |
|
359 | } |
||
360 | |||
361 | /** |
||
362 | * Removes temporary directory and upload-file. |
||
363 | * |
||
364 | * @param string $filename Filename. |
||
365 | */ |
||
366 | 150 | protected function removeUploadFile(string $filename) |
|
367 | { |
||
368 | 150 | unlink($filename); |
|
369 | 150 | rmdir(Util::dirname($filename)); |
|
370 | 150 | } |
|
371 | |||
372 | /** |
||
373 | * Map ShareFile item to FlySystem metadata. |
||
374 | * |
||
375 | * @param array $item ShareFile item |
||
376 | * @param string $path Base path |
||
377 | * @param string|null $contents Contents of the file (optional) |
||
378 | * |
||
379 | * @return array |
||
380 | */ |
||
381 | 420 | protected function mapItemInfo(array $item, string $path = '', string $contents = null): array |
|
382 | { |
||
383 | 420 | $timestamp = $item['ClientModifiedDate'] ?? $item['ClientCreatedDate'] ?? |
|
384 | 420 | $item['CreationDate'] ?? $item['ProgenyEditDate'] ?? ''; |
|
385 | 420 | $timestamp = !empty($timestamp) ? strtotime($timestamp) : false; |
|
386 | |||
387 | 420 | if ($path == '.') { |
|
388 | $path = ''; |
||
389 | } |
||
390 | 420 | $path = trim($path . '/' . $item['FileName'], '/'); |
|
391 | |||
392 | 420 | if ($this->isShareFileApiModelsFile($item)) { |
|
393 | 420 | $mimetype = Util::guessMimeType($item['FileName'], $contents); |
|
394 | 420 | $type = 'file'; |
|
395 | } else { |
||
396 | 30 | $mimetype = 'inode/directory'; |
|
397 | 30 | $type = 'dir'; |
|
398 | } |
||
399 | |||
400 | 420 | return array_merge( |
|
401 | [ |
||
402 | 420 | 'timestamp' => $timestamp, |
|
403 | 420 | 'path' => $path, |
|
404 | 420 | 'mimetype' => $mimetype, |
|
405 | 420 | 'dirname' => pathinfo($path, PATHINFO_DIRNAME), |
|
406 | 420 | 'extension' => pathinfo($item['FileName'], PATHINFO_EXTENSION), |
|
407 | 420 | 'filename' => pathinfo($item['FileName'], PATHINFO_FILENAME), |
|
408 | 420 | 'basename' => pathinfo($item['FileName'], PATHINFO_FILENAME), |
|
409 | 420 | 'type' => $type, |
|
410 | 420 | 'size' => $item['FileSizeBytes'], |
|
411 | 420 | 'contents' => !empty($contents) ? $contents : false, |
|
412 | 'stream' => false, |
||
413 | ], |
||
414 | 420 | $this->returnShareFileItem ? ['sharefile_item' => $item] : [] |
|
415 | ); |
||
416 | } |
||
417 | |||
418 | /** |
||
419 | * Map list of ShareFile items with metadata. |
||
420 | * |
||
421 | * @param array $items List of ShareFile items |
||
422 | * @param string $path Base path |
||
423 | * |
||
424 | * @return array |
||
425 | */ |
||
426 | 30 | protected function mapItemList(array $items, string $path):array |
|
427 | { |
||
428 | 30 | return array_map( |
|
429 | 30 | function ($item) use ($path) { |
|
430 | 30 | return $this->mapItemInfo($item, $path); |
|
431 | 30 | }, |
|
432 | 30 | $items |
|
433 | ); |
||
434 | } |
||
435 | |||
436 | /** |
||
437 | * Build metadata list from ShareFile item. |
||
438 | * |
||
439 | * @param array $item ShareFile item |
||
440 | * @param string $path Path of the given ShareFile item |
||
441 | * @param bool $recursive Recursive mode |
||
442 | * |
||
443 | * @return array |
||
444 | */ |
||
445 | 30 | protected function buildItemList(array $item, string $path, bool $recursive = false):array |
|
446 | { |
||
447 | 30 | if ($this->isShareFileApiModelsFile($item)) { |
|
448 | 30 | return []; |
|
449 | } |
||
450 | |||
451 | 30 | $children = $this->client->getItemById($item['Id'], true); |
|
452 | |||
453 | 30 | if ($children['FileCount'] < 2 || !isset($children['Children'])) { |
|
454 | return []; |
||
455 | } |
||
456 | |||
457 | 30 | $children = $this->removeAllExceptFilesAndFolders($children['Children']); |
|
458 | |||
459 | 30 | $itemList = $this->mapItemList($children, $path); |
|
460 | |||
461 | 30 | if ($recursive) { |
|
462 | 30 | foreach ($children as $child) { |
|
463 | 30 | $path = $path . '/' . $child['FileName']; |
|
464 | |||
465 | 30 | $itemList = array_merge( |
|
466 | 30 | $itemList, |
|
467 | 30 | $this->buildItemList($child, $path, true) |
|
468 | ); |
||
469 | } |
||
470 | } |
||
471 | |||
472 | 30 | return $itemList; |
|
473 | } |
||
474 | |||
475 | /** |
||
476 | * Remove all items except files and folders in the given array of ShareFile items. |
||
477 | * |
||
478 | * @param array $items Array of ShareFile items |
||
479 | * |
||
480 | * @return array |
||
481 | */ |
||
482 | 30 | protected function removeAllExceptFilesAndFolders(array $items):array |
|
483 | { |
||
484 | 30 | return array_filter( |
|
485 | 30 | $items, |
|
486 | 30 | function ($item) { |
|
487 | 30 | return $this->isShareFileApiModelsFolder($item) || $this->isShareFileApiModelsFile($item); |
|
488 | 30 | } |
|
489 | ); |
||
490 | } |
||
491 | |||
492 | /** |
||
493 | * Check if ShareFile item is a ShareFile.Api.Models.Folder type. |
||
494 | * |
||
495 | * @param array $item |
||
496 | * |
||
497 | * @return bool |
||
498 | */ |
||
499 | 570 | protected function isShareFileApiModelsFolder(array $item):bool |
|
500 | { |
||
501 | 570 | return $item['odata.type'] == 'ShareFile.Api.Models.Folder'; |
|
502 | } |
||
503 | |||
504 | /** |
||
505 | * Check if ShareFile item is a ShareFile.Api.Models.File type. |
||
506 | * |
||
507 | * @param array $item |
||
508 | * |
||
509 | * @return bool |
||
510 | */ |
||
511 | 570 | protected function isShareFileApiModelsFile(array $item):bool |
|
512 | { |
||
513 | 570 | return $item['odata.type'] == 'ShareFile.Api.Models.File'; |
|
514 | } |
||
515 | |||
516 | /** |
||
517 | * Get ShareFile item using path. |
||
518 | * |
||
519 | * @param string $path Path of the requested file |
||
520 | * |
||
521 | * @return array|false |
||
522 | * |
||
523 | * @throws Exception |
||
524 | */ |
||
525 | 720 | protected function getItemByPath(string $path) |
|
526 | { |
||
527 | 720 | if ($path == '.') { |
|
528 | $path = ''; |
||
529 | } |
||
530 | 720 | $path = '/' . trim($this->applyPathPrefix($path), '/'); |
|
531 | |||
532 | try { |
||
533 | 720 | $item = $this->client->getItemByPath($path); |
|
534 | 570 | if ($this->isShareFileApiModelsFolder($item) || $this->isShareFileApiModelsFile($item)) { |
|
535 | 570 | return $item; |
|
536 | } |
||
537 | 240 | } catch (exception $e) { |
|
538 | 240 | return false; |
|
539 | } |
||
540 | |||
541 | return false; |
||
542 | } |
||
543 | |||
544 | /** |
||
545 | * Check access control of a ShareFile item. |
||
546 | * |
||
547 | * @param array $item ShareFile item |
||
548 | * @param string $rule Access rule |
||
549 | * |
||
550 | * @return bool |
||
551 | */ |
||
552 | 420 | protected function checkAccessControl(array $item, string $rule):bool |
|
553 | { |
||
554 | 420 | if ($this->isShareFileApiModelsFile($item)) { |
|
555 | 60 | $item = $this->client->getItemById($item['Parent']['Id']); |
|
556 | 60 | if ($rule == self::CAN_DELETE_CURRENT_ITEM) { |
|
557 | 30 | $rule = self::CAN_DELETE_CHILD_ITEMS; |
|
558 | } |
||
559 | } |
||
560 | |||
561 | 420 | if (isset($item['Info'][$rule])) { |
|
562 | 390 | return $item['Info'][$rule] == 1; |
|
563 | } else { |
||
564 | 30 | return false; |
|
565 | } |
||
566 | } |
||
567 | } |
||
568 |
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.