Refactoring storage access

Added checks during installation phase
This commit is contained in:
Sergio Brighenti 2018-12-09 14:42:50 +01:00
parent 535fce4f8f
commit da437a203b
11 changed files with 75 additions and 57 deletions

View file

@ -32,15 +32,6 @@ abstract class Controller
return null; return null;
} }
/**
* Get a filesystem instance
* @return Filesystem
*/
protected function getStorage(): Filesystem
{
return new Filesystem(new Local($this->settings['storage_dir']));
}
/** /**
* @param $id * @param $id
* @return int * @return int
@ -51,7 +42,7 @@ abstract class Controller
$totalSize = 0; $totalSize = 0;
$filesystem = $this->getStorage(); $filesystem = storage();
foreach ($medias as $media) { foreach ($medias as $media) {
try { try {
$totalSize += $filesystem->getSize($media->storage_path); $totalSize += $filesystem->getSize($media->storage_path);

View file

@ -49,7 +49,7 @@ class DashboardController extends Controller
$pages = $this->database->query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `user_id` = ?', Session::get('user_id'))->fetch()->count / self::PER_PAGE; $pages = $this->database->query('SELECT COUNT(*) AS `count` FROM `uploads` WHERE `user_id` = ?', Session::get('user_id'))->fetch()->count / self::PER_PAGE;
} }
$filesystem = $this->getStorage(); $filesystem = storage();
foreach ($medias as $media) { foreach ($medias as $media) {
$extension = pathinfo($media->filename, PATHINFO_EXTENSION); $extension = pathinfo($media->filename, PATHINFO_EXTENSION);
@ -91,7 +91,7 @@ class DashboardController extends Controller
$totalSize = 0; $totalSize = 0;
$filesystem = $this->getStorage(); $filesystem = storage();
foreach ($medias as $media) { foreach ($medias as $media) {
$totalSize += $filesystem->getSize($media->storage_path); $totalSize += $filesystem->getSize($media->storage_path);
} }

View file

@ -55,7 +55,7 @@ class UploadController extends Controller
$fileInfo = pathinfo($file->getClientFilename()); $fileInfo = pathinfo($file->getClientFilename());
$storagePath = "$user->user_code/$code.$fileInfo[extension]"; $storagePath = "$user->user_code/$code.$fileInfo[extension]";
$this->getStorage()->writeStream($storagePath, $file->getStream()->detach()); storage()->writeStream($storagePath, $file->getStream()->detach());
$this->database->query('INSERT INTO `uploads`(`user_id`, `code`, `filename`, `storage_path`) VALUES (?, ?, ?, ?)', [ $this->database->query('INSERT INTO `uploads`(`user_id`, `code`, `filename`, `storage_path`) VALUES (?, ?, ?, ?)', [
$user->id, $user->id,
@ -88,12 +88,10 @@ class UploadController extends Controller
throw new NotFoundException($request, $response); throw new NotFoundException($request, $response);
} }
$filesystem = $this->getStorage();
if (isBot($request->getHeaderLine('User-Agent'))) { if (isBot($request->getHeaderLine('User-Agent'))) {
return $this->streamMedia($request, $response, $filesystem, $media); return $this->streamMedia($request, $response, storage(), $media);
} else { } else {
$filesystem = storage();
try { try {
$media->mimetype = $filesystem->getMimetype($media->storage_path); $media->mimetype = $filesystem->getMimetype($media->storage_path);
$media->size = humanFileSize($filesystem->getSize($media->storage_path)); $media->size = humanFileSize($filesystem->getSize($media->storage_path));
@ -152,9 +150,8 @@ class UploadController extends Controller
if (Session::get('admin', false) || $user->id === $media->user_id) { if (Session::get('admin', false) || $user->id === $media->user_id) {
$filesystem = $this->getStorage();
try { try {
$filesystem->delete($media->storage_path); storage()->delete($media->storage_path);
} catch (FileNotFoundException $e) { } catch (FileNotFoundException $e) {
throw new NotFoundException($request, $response); throw new NotFoundException($request, $response);
} finally { } finally {
@ -185,7 +182,7 @@ class UploadController extends Controller
throw new NotFoundException($request, $response); throw new NotFoundException($request, $response);
} }
return $this->streamMedia($request, $response, $this->getStorage(), $media); return $this->streamMedia($request, $response, storage(), $media);
} }
/** /**
@ -203,7 +200,7 @@ class UploadController extends Controller
if (!$media || !$media->published && Session::get('user_id') !== $media->user_id && !Session::get('admin', false)) { if (!$media || !$media->published && Session::get('user_id') !== $media->user_id && !Session::get('admin', false)) {
throw new NotFoundException($request, $response); throw new NotFoundException($request, $response);
} }
return $this->streamMedia($request, $response, $this->getStorage(), $media); return $this->streamMedia($request, $response, storage(), $media);
} }
@ -222,7 +219,7 @@ class UploadController extends Controller
if (!$media || !$media->published && Session::get('user_id') !== $media->user_id && !Session::get('admin', false)) { if (!$media || !$media->published && Session::get('user_id') !== $media->user_id && !Session::get('admin', false)) {
throw new NotFoundException($request, $response); throw new NotFoundException($request, $response);
} }
return $this->streamMedia($request, $response, $this->getStorage(), $media, 'attachment'); return $this->streamMedia($request, $response, storage(), $media, 'attachment');
} }
/** /**
@ -267,9 +264,8 @@ class UploadController extends Controller
if (Session::get('admin', false) || $media->user_id === Session::get('user_id')) { if (Session::get('admin', false) || $media->user_id === Session::get('user_id')) {
$filesystem = $this->getStorage();
try { try {
$filesystem->delete($media->storage_path); storage()->delete($media->storage_path);
} catch (FileNotFoundException $e) { } catch (FileNotFoundException $e) {
throw new NotFoundException($request, $response); throw new NotFoundException($request, $response);
} finally { } finally {

View file

@ -90,7 +90,7 @@ class Session
*/ */
public static function alert($message, string $type = 'info'): void public static function alert($message, string $type = 'info'): void
{ {
$_SESSION['_flash'] = [$type => $message]; $_SESSION['_flash'][] = [$type => $message];
} }

View file

@ -1,7 +1,24 @@
<?php <?php
use League\Flysystem\Adapter\Local;
use League\Flysystem\Filesystem;
require __DIR__ . '/../vendor/autoload.php'; require __DIR__ . '/../vendor/autoload.php';
if (!function_exists('storage')) {
/**
* Get a filesystem instance given a path
* @param string $root
* @return Filesystem
*/
function storage($root = null): Filesystem
{
global $app;
$storagePath = $app->getContainer()->get('settings')['storage_dir'];
return new Filesystem(new Local($root !== null ? $root : $storagePath));
}
}
if (!function_exists('humanFileSize')) { if (!function_exists('humanFileSize')) {
/** /**
* Generate a human readable file size * Generate a human readable file size
@ -61,7 +78,7 @@ if (!function_exists('redirect')) {
*/ */
function redirect(\Slim\Http\Response $response, string $path, $args = [], $status = null) function redirect(\Slim\Http\Response $response, string $path, $args = [], $status = null)
{ {
if ($path === '/' || substr($path, 0, 1) === '/') { if ($path === '/' || $path === './' || substr($path, 0, 1) === '/') {
$url = urlFor($path); $url = urlFor($path);
} else { } else {
$url = route($path, $args); $url = route($path, $args);

View file

@ -1,6 +1,6 @@
{ {
"name": "sergix44/xbackbone", "name": "sergix44/xbackbone",
"version": "2.3", "version": "2.3.1",
"description": "A lightweight ShareX PHP backend", "description": "A lightweight ShareX PHP backend",
"type": "project", "type": "project",
"require": { "require": {

12
composer.lock generated
View file

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "b7bca0067b8963611274642752122ecb", "content-hash": "265c1ca72d526b36b50fcec633b5807f",
"packages": [ "packages": [
{ {
"name": "container-interop/container-interop", "name": "container-interop/container-interop",
@ -39,16 +39,16 @@
}, },
{ {
"name": "guzzlehttp/psr7", "name": "guzzlehttp/psr7",
"version": "1.5.0", "version": "1.5.2",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/guzzle/psr7.git", "url": "https://github.com/guzzle/psr7.git",
"reference": "53662d6688033a5eccde987bdd5a4a98ebe2d952" "reference": "9f83dded91781a01c63574e387eaa769be769115"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/guzzle/psr7/zipball/53662d6688033a5eccde987bdd5a4a98ebe2d952", "url": "https://api.github.com/repos/guzzle/psr7/zipball/9f83dded91781a01c63574e387eaa769be769115",
"reference": "53662d6688033a5eccde987bdd5a4a98ebe2d952", "reference": "9f83dded91781a01c63574e387eaa769be769115",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@ -102,7 +102,7 @@
"uri", "uri",
"url" "url"
], ],
"time": "2018-12-03T05:07:51+00:00" "time": "2018-12-04T20:46:45+00:00"
}, },
{ {
"name": "intervention/image", "name": "intervention/image",

View file

@ -113,6 +113,18 @@ $app = new App($container);
$app->get('/', function (Request $request, Response $response) { $app->get('/', function (Request $request, Response $response) {
if (!is_writable(__DIR__ . '/../resources/cache')) {
Session::alert('The cache folder is not writable (' . __DIR__ . '/../resources/cache' . ')', 'danger');
}
if (!is_writable(__DIR__ . '/../resources/database')) {
Session::alert('The database folder is not writable (' . __DIR__ . '/../resources/database' . ')', 'danger');
}
if (!is_writable(__DIR__ . '/../resources/sessions')) {
Session::alert('The sessions folder is not writable (' . __DIR__ . '/../resources/sessions' . ')', 'danger');
}
$installed = file_exists(__DIR__ . '/../config.php'); $installed = file_exists(__DIR__ . '/../config.php');
return $this->view->render($response, 'install.twig', ['installed' => $installed]); return $this->view->render($response, 'install.twig', ['installed' => $installed]);
@ -131,15 +143,22 @@ $app->post('/', function (Request $request, Response $response) use (&$config) {
$config['db']['username'] = $request->getParam('db_user'); $config['db']['username'] = $request->getParam('db_user');
$config['db']['password'] = $request->getParam('db_password'); $config['db']['password'] = $request->getParam('db_password');
try {
storage($config['storage_dir']);
} catch (LogicException $exception) {
Session::alert('The storage folder is not readable (' . $config['storage_dir'] . ')', 'danger');
return redirect($response, './');
} finally {
if (!is_writable($config['storage_dir'])) { if (!is_writable($config['storage_dir'])) {
Session::alert('The storage folder is not writable (' . $config['storage_dir'] . ')', 'danger'); Session::alert('The storage folder is not writable (' . $config['storage_dir'] . ')', 'danger');
return redirect($response, '.'); return redirect($response, './');
}
} }
$ret = file_put_contents(__DIR__ . '/../config.php', '<?php' . PHP_EOL . 'return ' . var_export($config, true) . ';'); $ret = file_put_contents(__DIR__ . '/../config.php', '<?php' . PHP_EOL . 'return ' . var_export($config, true) . ';');
if ($ret === false) { if ($ret === false) {
Session::alert('The config folder is not writable (' . __DIR__ . '/../config.php' . ')', 'danger'); Session::alert('The config folder is not writable (' . __DIR__ . '/../config.php' . ')', 'danger');
return redirect($response, '.'); return redirect($response, './');
} }
} }
@ -151,7 +170,7 @@ $app->post('/', function (Request $request, Response $response) use (&$config) {
migrate($config); migrate($config);
} catch (PDOException $exception) { } catch (PDOException $exception) {
Session::alert("Cannot connect to the database: {$exception->getMessage()} [{$exception->getCode()}]", 'danger'); Session::alert("Cannot connect to the database: {$exception->getMessage()} [{$exception->getCode()}]", 'danger');
return redirect($response, '.'); return redirect($response, './');
} }
if (!$installed) { if (!$installed) {

View file

@ -19,14 +19,7 @@
</head> </head>
<body> <body>
<div class="container"> <div class="container">
{% for type, message in alerts %} {% include 'comp/alert.twig' %}
<div class="alert alert-{{ type }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
{% endfor %}
<div class="row justify-content-center"> <div class="row justify-content-center">
<div class="col-md-8"> <div class="col-md-8">
<div class="card mt-3"> <div class="card mt-3">

View file

@ -12,11 +12,11 @@
"devDependencies": { "devDependencies": {
"grunt": "^1.0", "grunt": "^1.0",
"grunt-contrib-copy": "^1.0.0", "grunt-contrib-copy": "^1.0.0",
"grunt-contrib-cssmin": "^2.2.1", "grunt-contrib-cssmin": "^3.0.0",
"grunt-contrib-jshint": "^1.1.0", "grunt-contrib-jshint": "^2.0.0",
"grunt-contrib-uglify": "^3.3.0", "grunt-contrib-uglify": "^4.0.0",
"grunt-contrib-watch": "^1.1.0", "grunt-contrib-watch": "^1.1.0",
"grunt-zip": "^0.17.1", "grunt-zip": "^0.18.1",
"load-grunt-tasks": "^4.0.0" "load-grunt-tasks": "^4.0.0"
} }
} }

View file

@ -1,4 +1,5 @@
{% for type, message in alerts %} {% for alert in alerts %}
{% for type, message in alert %}
<div class="alert alert-{{ type }} alert-dismissible fade show" role="alert"> <div class="alert alert-{{ type }} alert-dismissible fade show" role="alert">
{{ message }} {{ message }}
<button type="button" class="close" data-dismiss="alert" aria-label="Close"> <button type="button" class="close" data-dismiss="alert" aria-label="Close">
@ -6,3 +7,4 @@
</button> </button>
</div> </div>
{% endfor %} {% endfor %}
{% endfor %}