mirror of
https://github.com/filegator/filegator.git
synced 2025-10-27 18:31:53 +01:00
deploy: c9cb5afa91
This commit is contained in:
45
backend/App.php
Normal file
45
backend/App.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator;
|
||||
|
||||
use Filegator\Config\Config;
|
||||
use Filegator\Container\Container;
|
||||
use Filegator\Kernel\Request;
|
||||
use Filegator\Kernel\Response;
|
||||
use Filegator\Kernel\StreamedResponse;
|
||||
|
||||
class App
|
||||
{
|
||||
private $container;
|
||||
|
||||
public function __construct(Config $config, Request $request, Response $response, StreamedResponse $sresponse, Container $container)
|
||||
{
|
||||
$container->set(Config::class, $config);
|
||||
$container->set(Container::class, $container);
|
||||
$container->set(Request::class, $request);
|
||||
$container->set(Response::class, $response);
|
||||
$container->set(StreamedResponse::class, $sresponse);
|
||||
|
||||
foreach ($config->get('services', []) as $key => $service) {
|
||||
$container->set($key, $container->get($service['handler']));
|
||||
$container->get($key)->init(isset($service['config']) ? $service['config'] : []);
|
||||
}
|
||||
|
||||
$response->send();
|
||||
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
public function resolve($name)
|
||||
{
|
||||
return $this->container->get($name);
|
||||
}
|
||||
}
|
||||
45
backend/Config/Config.php
Normal file
45
backend/Config/Config.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Config;
|
||||
|
||||
class Config
|
||||
{
|
||||
protected $config;
|
||||
|
||||
public function __construct(array $config = [])
|
||||
{
|
||||
$this->config = $config;
|
||||
|
||||
$timezone = isset($this->config['timezone']) ? $this->config['timezone'] : 'UTC';
|
||||
date_default_timezone_set($timezone);
|
||||
}
|
||||
|
||||
public function get($key = null, $default = null)
|
||||
{
|
||||
if (is_null($key)) {
|
||||
return $this->config;
|
||||
}
|
||||
|
||||
$key = is_array($key) ? $key : explode('.', $key);
|
||||
|
||||
$target = $this->config;
|
||||
|
||||
while (! is_null($segment = array_shift($key))) {
|
||||
if (is_array($target) && array_key_exists($segment, $target)) {
|
||||
$target = $target[$segment];
|
||||
} else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
return $target;
|
||||
}
|
||||
}
|
||||
17
backend/Container/Container.php
Normal file
17
backend/Container/Container.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Container;
|
||||
|
||||
use DI\Container as PHPDIContainer;
|
||||
|
||||
class Container extends PHPDIContainer implements ContainerInterface
|
||||
{
|
||||
}
|
||||
20
backend/Container/ContainerInterface.php
Normal file
20
backend/Container/ContainerInterface.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Container;
|
||||
|
||||
interface ContainerInterface
|
||||
{
|
||||
public function get($name);
|
||||
|
||||
public function set(string $name, $value);
|
||||
|
||||
public function call($callable, array $parameters = []);
|
||||
}
|
||||
123
backend/Controllers/AdminController.php
Normal file
123
backend/Controllers/AdminController.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Controllers;
|
||||
|
||||
use Filegator\Kernel\Request;
|
||||
use Filegator\Kernel\Response;
|
||||
use Filegator\Services\Auth\AuthInterface;
|
||||
use Filegator\Services\Auth\User;
|
||||
use Filegator\Services\Storage\Filesystem;
|
||||
use Rakit\Validation\Validator;
|
||||
|
||||
class AdminController
|
||||
{
|
||||
protected $auth;
|
||||
|
||||
protected $storage;
|
||||
|
||||
public function __construct(AuthInterface $auth, Filesystem $storage)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
$this->storage = $storage;
|
||||
}
|
||||
|
||||
public function listUsers(Request $request, Response $response)
|
||||
{
|
||||
return $response->json($this->auth->allUsers());
|
||||
}
|
||||
|
||||
public function storeUser(User $user, Request $request, Response $response, Validator $validator)
|
||||
{
|
||||
$validator->setMessage('required', 'This field is required');
|
||||
$validation = $validator->validate($request->all(), [
|
||||
'name' => 'required',
|
||||
'username' => 'required',
|
||||
'homedir' => 'required',
|
||||
'password' => 'required',
|
||||
]);
|
||||
|
||||
if ($validation->fails()) {
|
||||
$errors = $validation->errors();
|
||||
|
||||
return $response->json($errors->firstOfAll(), 422);
|
||||
}
|
||||
|
||||
if ($this->auth->find($request->input('username'))) {
|
||||
return $response->json(['username' => 'Username already taken'], 422);
|
||||
}
|
||||
|
||||
try {
|
||||
$user->setName($request->input('name'));
|
||||
$user->setUsername($request->input('username'));
|
||||
$user->setHomedir(
|
||||
rtrim($this->auth->user()->getHomeDir(), $this->storage->getSeparator())
|
||||
.$this->storage->getSeparator()
|
||||
.ltrim($request->input('homedir'), $this->storage->getSeparator())
|
||||
);
|
||||
$user->setRole($request->input('role', 'user'));
|
||||
$user->setPermissions($request->input('permissions'));
|
||||
$ret = $this->auth->add($user, $request->input('password'));
|
||||
} catch (\Exception $e) {
|
||||
return $response->json($e->getMessage(), 422);
|
||||
}
|
||||
|
||||
return $response->json($ret);
|
||||
}
|
||||
|
||||
public function updateUser($username, Request $request, Response $response, Validator $validator)
|
||||
{
|
||||
$user = $this->auth->find($username);
|
||||
|
||||
if (! $user) {
|
||||
return $response->json('User not found', 422);
|
||||
}
|
||||
|
||||
$validator->setMessage('required', 'This field is required');
|
||||
$validation = $validator->validate($request->all(), [
|
||||
'name' => 'required',
|
||||
'username' => 'required',
|
||||
'homedir' => 'required',
|
||||
]);
|
||||
|
||||
if ($validation->fails()) {
|
||||
$errors = $validation->errors();
|
||||
|
||||
return $response->json($errors->firstOfAll(), 422);
|
||||
}
|
||||
|
||||
if ($username != $request->input('username') && $this->auth->find($request->input('username'))) {
|
||||
return $response->json(['username' => 'Username already taken'], 422);
|
||||
}
|
||||
|
||||
try {
|
||||
$user->setName($request->input('name'));
|
||||
$user->setUsername($request->input('username'));
|
||||
$user->setHomedir($request->input('homedir'));
|
||||
$user->setRole($request->input('role', 'user'));
|
||||
$user->setPermissions($request->input('permissions'));
|
||||
|
||||
return $response->json($this->auth->update($username, $user, $request->input('password', '')));
|
||||
} catch (\Exception $e) {
|
||||
return $response->json($e->getMessage(), 422);
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteUser($username, Request $request, Response $response)
|
||||
{
|
||||
$user = $this->auth->find($username);
|
||||
|
||||
if (! $user) {
|
||||
return $response->json('User not found', 422);
|
||||
}
|
||||
|
||||
return $response->json($this->auth->delete($user));
|
||||
}
|
||||
}
|
||||
76
backend/Controllers/AuthController.php
Normal file
76
backend/Controllers/AuthController.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Controllers;
|
||||
|
||||
use Filegator\Kernel\Request;
|
||||
use Filegator\Kernel\Response;
|
||||
use Filegator\Services\Auth\AuthInterface;
|
||||
use Filegator\Services\Logger\LoggerInterface;
|
||||
use Rakit\Validation\Validator;
|
||||
|
||||
class AuthController
|
||||
{
|
||||
protected $logger;
|
||||
|
||||
public function __construct(LoggerInterface $logger)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function login(Request $request, Response $response, AuthInterface $auth)
|
||||
{
|
||||
$username = $request->input('username');
|
||||
$password = $request->input('password');
|
||||
|
||||
if ($auth->authenticate($username, $password)) {
|
||||
$this->logger->log("Logged in {$username} from IP ".$request->getClientIp());
|
||||
|
||||
return $response->json($auth->user());
|
||||
}
|
||||
|
||||
$this->logger->log("Login failed for {$username} from IP ".$request->getClientIp());
|
||||
|
||||
return $response->json('Login failed, please try again', 422);
|
||||
}
|
||||
|
||||
public function logout(Response $response, AuthInterface $auth)
|
||||
{
|
||||
return $response->json($auth->forget());
|
||||
}
|
||||
|
||||
public function getUser(Response $response, AuthInterface $auth)
|
||||
{
|
||||
$user = $auth->user() ?: $auth->getGuest();
|
||||
|
||||
return $response->json($user);
|
||||
}
|
||||
|
||||
public function changePassword(Request $request, Response $response, AuthInterface $auth, Validator $validator)
|
||||
{
|
||||
$validator->setMessage('required', 'This field is required');
|
||||
$validation = $validator->validate($request->all(), [
|
||||
'oldpassword' => 'required',
|
||||
'newpassword' => 'required',
|
||||
]);
|
||||
|
||||
if ($validation->fails()) {
|
||||
$errors = $validation->errors();
|
||||
|
||||
return $response->json($errors->firstOfAll(), 422);
|
||||
}
|
||||
|
||||
if (! $auth->authenticate($auth->user()->getUsername(), $request->input('oldpassword'))) {
|
||||
return $response->json(['oldpassword' => 'Wrong password'], 422);
|
||||
}
|
||||
|
||||
return $response->json($auth->update($auth->user()->getUsername(), $auth->user(), $request->input('newpassword')));
|
||||
}
|
||||
}
|
||||
189
backend/Controllers/DownloadController.php
Normal file
189
backend/Controllers/DownloadController.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Controllers;
|
||||
|
||||
use Filegator\Config\Config;
|
||||
use Filegator\Kernel\Request;
|
||||
use Filegator\Kernel\Response;
|
||||
use Filegator\Kernel\StreamedResponse;
|
||||
use Filegator\Services\Archiver\ArchiverInterface;
|
||||
use Filegator\Services\Auth\AuthInterface;
|
||||
use Filegator\Services\Session\SessionStorageInterface as Session;
|
||||
use Filegator\Services\Storage\Filesystem;
|
||||
use Filegator\Services\Tmpfs\TmpfsInterface;
|
||||
use Symfony\Component\HttpFoundation\HeaderUtils;
|
||||
use Symfony\Component\Mime\MimeTypes;
|
||||
|
||||
class DownloadController
|
||||
{
|
||||
protected $auth;
|
||||
|
||||
protected $session;
|
||||
|
||||
protected $config;
|
||||
|
||||
protected $storage;
|
||||
|
||||
public function __construct(Config $config, Session $session, AuthInterface $auth, Filesystem $storage)
|
||||
{
|
||||
$this->session = $session;
|
||||
$this->config = $config;
|
||||
$this->auth = $auth;
|
||||
|
||||
$user = $this->auth->user() ?: $this->auth->getGuest();
|
||||
|
||||
$this->storage = $storage;
|
||||
$this->storage->setPathPrefix($user->getHomeDir());
|
||||
}
|
||||
|
||||
public function download(Request $request, Response $response, StreamedResponse $streamedResponse)
|
||||
{
|
||||
try {
|
||||
$file = $this->storage->readStream((string) base64_decode($request->input('path')));
|
||||
} catch (\Exception $e) {
|
||||
return $response->redirect('/');
|
||||
}
|
||||
|
||||
$streamedResponse->setCallback(function () use ($file) {
|
||||
// @codeCoverageIgnoreStart
|
||||
set_time_limit(0);
|
||||
if ($file['stream']) {
|
||||
while (! feof($file['stream'])) {
|
||||
echo fread($file['stream'], 1024 * 8);
|
||||
if (ob_get_level() > 0) {ob_flush();}
|
||||
flush();
|
||||
}
|
||||
fclose($file['stream']);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
});
|
||||
|
||||
$extension = pathinfo($file['filename'], PATHINFO_EXTENSION);
|
||||
$mimes = (new MimeTypes())->getMimeTypes($extension);
|
||||
$contentType = !empty($mimes) ? $mimes[0] : 'application/octet-stream';
|
||||
|
||||
$disposition = HeaderUtils::DISPOSITION_ATTACHMENT;
|
||||
|
||||
$download_inline = (array)$this->config->get('download_inline', ['pdf']);
|
||||
if (in_array($extension, $download_inline) || in_array('*', $download_inline)) {
|
||||
$disposition = HeaderUtils::DISPOSITION_INLINE;
|
||||
}
|
||||
|
||||
$contentDisposition = HeaderUtils::makeDisposition($disposition, $file['filename'], 'file');
|
||||
|
||||
$streamedResponse->headers->set(
|
||||
'Content-Disposition',
|
||||
$contentDisposition
|
||||
);
|
||||
$streamedResponse->headers->set(
|
||||
'Content-Type',
|
||||
$contentType
|
||||
);
|
||||
$streamedResponse->headers->set(
|
||||
'Content-Transfer-Encoding',
|
||||
'binary'
|
||||
);
|
||||
if (isset($file['filesize'])) {
|
||||
$streamedResponse->headers->set(
|
||||
'Content-Length',
|
||||
$file['filesize']
|
||||
);
|
||||
}
|
||||
// @codeCoverageIgnoreStart
|
||||
if (APP_ENV == 'development') {
|
||||
$streamedResponse->headers->set(
|
||||
'Access-Control-Allow-Origin',
|
||||
$request->headers->get('Origin')
|
||||
);
|
||||
$streamedResponse->headers->set(
|
||||
'Access-Control-Allow-Credentials',
|
||||
'true'
|
||||
);
|
||||
}
|
||||
// @codeCoverageIgnoreEnd
|
||||
|
||||
// close session so we can continue streaming, note: dev is single-threaded
|
||||
$this->session->save();
|
||||
|
||||
$streamedResponse->send();
|
||||
}
|
||||
|
||||
public function batchDownloadCreate(Request $request, Response $response, ArchiverInterface $archiver)
|
||||
{
|
||||
$items = $request->input('items', []);
|
||||
|
||||
$uniqid = $archiver->createArchive($this->storage);
|
||||
|
||||
// close session
|
||||
$this->session->save();
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item->type == 'dir') {
|
||||
$archiver->addDirectoryFromStorage($item->path);
|
||||
}
|
||||
if ($item->type == 'file') {
|
||||
$archiver->addFileFromStorage($item->path);
|
||||
}
|
||||
}
|
||||
|
||||
$archiver->closeArchive();
|
||||
|
||||
return $response->json(['uniqid' => $uniqid]);
|
||||
}
|
||||
|
||||
public function batchDownloadStart(Request $request, StreamedResponse $streamedResponse, TmpfsInterface $tmpfs)
|
||||
{
|
||||
$uniqid = (string) preg_replace('/[^0-9a-zA-Z_]/', '', (string) $request->input('uniqid'));
|
||||
$file = $tmpfs->readStream($uniqid);
|
||||
|
||||
$streamedResponse->setCallback(function () use ($file, $tmpfs, $uniqid) {
|
||||
// @codeCoverageIgnoreStart
|
||||
set_time_limit(0);
|
||||
if ($file['stream']) {
|
||||
while (! feof($file['stream'])) {
|
||||
echo fread($file['stream'], 1024 * 8);
|
||||
if (ob_get_level() > 0) {ob_flush();}
|
||||
flush();
|
||||
}
|
||||
fclose($file['stream']);
|
||||
}
|
||||
$tmpfs->remove($uniqid);
|
||||
// @codeCoverageIgnoreEnd
|
||||
});
|
||||
|
||||
$streamedResponse->headers->set(
|
||||
'Content-Disposition',
|
||||
HeaderUtils::makeDisposition(
|
||||
HeaderUtils::DISPOSITION_ATTACHMENT,
|
||||
$this->config->get('frontend_config.default_archive_name'),
|
||||
'archive.zip'
|
||||
)
|
||||
);
|
||||
$streamedResponse->headers->set(
|
||||
'Content-Type',
|
||||
'application/octet-stream'
|
||||
);
|
||||
$streamedResponse->headers->set(
|
||||
'Content-Transfer-Encoding',
|
||||
'binary'
|
||||
);
|
||||
if (isset($file['filesize'])) {
|
||||
$streamedResponse->headers->set(
|
||||
'Content-Length',
|
||||
$file['filesize']
|
||||
);
|
||||
}
|
||||
// close session so we can continue streaming, note: dev is single-threaded
|
||||
$this->session->save();
|
||||
|
||||
$streamedResponse->send();
|
||||
}
|
||||
}
|
||||
34
backend/Controllers/ErrorController.php
Normal file
34
backend/Controllers/ErrorController.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Controllers;
|
||||
|
||||
use Filegator\Kernel\Request;
|
||||
use Filegator\Kernel\Response;
|
||||
|
||||
class ErrorController
|
||||
{
|
||||
protected $request_type;
|
||||
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->request_type = $request->getContentType();
|
||||
}
|
||||
|
||||
public function notFound(Response $response)
|
||||
{
|
||||
return $this->request_type == 'json' ? $response->json('Not Found', 404) : $response->html('Not Found', 404);
|
||||
}
|
||||
|
||||
public function methodNotAllowed(Response $response)
|
||||
{
|
||||
return $this->request_type == 'json' ? $response->json('Not Allowed', 401) : $response->html('Not Found', 401);
|
||||
}
|
||||
}
|
||||
194
backend/Controllers/FileController.php
Normal file
194
backend/Controllers/FileController.php
Normal file
@@ -0,0 +1,194 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Controllers;
|
||||
|
||||
use Filegator\Config\Config;
|
||||
use Filegator\Kernel\Request;
|
||||
use Filegator\Kernel\Response;
|
||||
use Filegator\Services\Archiver\ArchiverInterface;
|
||||
use Filegator\Services\Auth\AuthInterface;
|
||||
use Filegator\Services\Session\SessionStorageInterface as Session;
|
||||
use Filegator\Services\Storage\Filesystem;
|
||||
|
||||
class FileController
|
||||
{
|
||||
const SESSION_CWD = 'current_path';
|
||||
|
||||
protected $session;
|
||||
|
||||
protected $auth;
|
||||
|
||||
protected $config;
|
||||
|
||||
protected $storage;
|
||||
|
||||
protected $separator;
|
||||
|
||||
public function __construct(Config $config, Session $session, AuthInterface $auth, Filesystem $storage)
|
||||
{
|
||||
$this->session = $session;
|
||||
$this->config = $config;
|
||||
$this->auth = $auth;
|
||||
|
||||
$user = $this->auth->user() ?: $this->auth->getGuest();
|
||||
|
||||
$this->storage = $storage;
|
||||
$this->storage->setPathPrefix($user->getHomeDir());
|
||||
|
||||
$this->separator = $this->storage->getSeparator();
|
||||
}
|
||||
|
||||
public function changeDirectory(Request $request, Response $response)
|
||||
{
|
||||
$path = $request->input('to', $this->separator);
|
||||
|
||||
$this->session->set(self::SESSION_CWD, $path);
|
||||
|
||||
return $response->json($this->storage->getDirectoryCollection($path));
|
||||
}
|
||||
|
||||
public function getDirectory(Request $request, Response $response)
|
||||
{
|
||||
$path = $request->input('dir', $this->session->get(self::SESSION_CWD, $this->separator));
|
||||
|
||||
$content = $this->storage->getDirectoryCollection($path);
|
||||
|
||||
return $response->json($content);
|
||||
}
|
||||
|
||||
public function createNew(Request $request, Response $response)
|
||||
{
|
||||
$type = $request->input('type', 'file');
|
||||
$name = $request->input('name');
|
||||
$path = $this->session->get(self::SESSION_CWD, $this->separator);
|
||||
|
||||
if ($type == 'dir') {
|
||||
$this->storage->createDir($path, $request->input('name'));
|
||||
}
|
||||
if ($type == 'file') {
|
||||
$this->storage->createFile($path, $request->input('name'));
|
||||
}
|
||||
|
||||
return $response->json('Done');
|
||||
}
|
||||
|
||||
public function copyItems(Request $request, Response $response)
|
||||
{
|
||||
$items = $request->input('items', []);
|
||||
$destination = $request->input('destination', $this->separator);
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item->type == 'dir') {
|
||||
$this->storage->copyDir($item->path, $destination);
|
||||
}
|
||||
if ($item->type == 'file') {
|
||||
$this->storage->copyFile($item->path, $destination);
|
||||
}
|
||||
}
|
||||
|
||||
return $response->json('Done');
|
||||
}
|
||||
|
||||
public function moveItems(Request $request, Response $response)
|
||||
{
|
||||
$items = $request->input('items', []);
|
||||
$destination = $request->input('destination', $this->separator);
|
||||
|
||||
foreach ($items as $item) {
|
||||
$full_destination = trim($destination, $this->separator)
|
||||
.$this->separator
|
||||
.ltrim($item->name, $this->separator);
|
||||
$this->storage->move($item->path, $full_destination);
|
||||
}
|
||||
|
||||
return $response->json('Done');
|
||||
}
|
||||
|
||||
public function zipItems(Request $request, Response $response, ArchiverInterface $archiver)
|
||||
{
|
||||
$items = $request->input('items', []);
|
||||
$destination = $request->input('destination', $this->separator);
|
||||
$name = $request->input('name', $this->config->get('frontend_config.default_archive_name'));
|
||||
|
||||
$archiver->createArchive($this->storage);
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item->type == 'dir') {
|
||||
$archiver->addDirectoryFromStorage($item->path);
|
||||
}
|
||||
if ($item->type == 'file') {
|
||||
$archiver->addFileFromStorage($item->path);
|
||||
}
|
||||
}
|
||||
|
||||
$archiver->storeArchive($destination, $name);
|
||||
|
||||
return $response->json('Done');
|
||||
}
|
||||
|
||||
public function unzipItem(Request $request, Response $response, ArchiverInterface $archiver)
|
||||
{
|
||||
$source = $request->input('item');
|
||||
$destination = $request->input('destination', $this->separator);
|
||||
|
||||
$archiver->uncompress($source, $destination, $this->storage);
|
||||
|
||||
return $response->json('Done');
|
||||
}
|
||||
|
||||
public function renameItem(Request $request, Response $response)
|
||||
{
|
||||
$destination = $request->input('destination', $this->separator);
|
||||
$from = $request->input('from');
|
||||
$to = $request->input('to');
|
||||
|
||||
$this->storage->rename($destination, $from, $to);
|
||||
|
||||
return $response->json('Done');
|
||||
}
|
||||
|
||||
public function deleteItems(Request $request, Response $response)
|
||||
{
|
||||
$items = $request->input('items', []);
|
||||
|
||||
foreach ($items as $item) {
|
||||
if ($item->type == 'dir') {
|
||||
$this->storage->deleteDir($item->path);
|
||||
}
|
||||
if ($item->type == 'file') {
|
||||
$this->storage->deleteFile($item->path);
|
||||
}
|
||||
}
|
||||
|
||||
return $response->json('Done');
|
||||
}
|
||||
|
||||
public function saveContent(Request $request, Response $response)
|
||||
{
|
||||
$path = $request->input('dir', $this->session->get(self::SESSION_CWD, $this->separator));
|
||||
|
||||
$name = $request->input('name');
|
||||
$content = $request->input('content');
|
||||
|
||||
$stream = tmpfile();
|
||||
fwrite($stream, $content);
|
||||
rewind($stream);
|
||||
|
||||
$this->storage->deleteFile($path.$this->separator.$name);
|
||||
$this->storage->store($path, $name, $stream);
|
||||
|
||||
if (is_resource($stream)) {
|
||||
fclose($stream);
|
||||
}
|
||||
|
||||
return $response->json('Done');
|
||||
}
|
||||
}
|
||||
131
backend/Controllers/UploadController.php
Normal file
131
backend/Controllers/UploadController.php
Normal file
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Controllers;
|
||||
|
||||
use Filegator\Config\Config;
|
||||
use Filegator\Kernel\Request;
|
||||
use Filegator\Kernel\Response;
|
||||
use Filegator\Services\Auth\AuthInterface;
|
||||
use Filegator\Services\Storage\Filesystem;
|
||||
use Filegator\Services\Tmpfs\TmpfsInterface;
|
||||
|
||||
class UploadController
|
||||
{
|
||||
protected $auth;
|
||||
|
||||
protected $config;
|
||||
|
||||
protected $storage;
|
||||
|
||||
protected $tmpfs;
|
||||
|
||||
public function __construct(Config $config, AuthInterface $auth, Filesystem $storage, TmpfsInterface $tmpfs)
|
||||
{
|
||||
$this->config = $config;
|
||||
$this->auth = $auth;
|
||||
$this->tmpfs = $tmpfs;
|
||||
|
||||
$user = $this->auth->user() ?: $this->auth->getGuest();
|
||||
|
||||
$this->storage = $storage;
|
||||
$this->storage->setPathPrefix($user->getHomeDir());
|
||||
}
|
||||
|
||||
public function chunkCheck(Request $request, Response $response)
|
||||
{
|
||||
$file_name = $request->input('resumableFilename', 'file');
|
||||
$identifier = (string) preg_replace('/[^0-9a-zA-Z_]/', '', (string) $request->input('resumableIdentifier'));
|
||||
$chunk_number = (int) $request->input('resumableChunkNumber');
|
||||
|
||||
$chunk_file = 'multipart_'.$identifier.$file_name.'.part'.$chunk_number;
|
||||
|
||||
if ($this->tmpfs->exists($chunk_file)) {
|
||||
return $response->json('Chunk exists', 200);
|
||||
}
|
||||
|
||||
return $response->json('Chunk does not exists', 204);
|
||||
}
|
||||
|
||||
public function upload(Request $request, Response $response)
|
||||
{
|
||||
$file_name = $request->input('resumableFilename', 'file');
|
||||
$destination = $request->input('resumableRelativePath');
|
||||
$chunk_number = (int) $request->input('resumableChunkNumber');
|
||||
$total_chunks = (int) $request->input('resumableTotalChunks');
|
||||
$total_size = (int) $request->input('resumableTotalSize');
|
||||
$identifier = (string) preg_replace('/[^0-9a-zA-Z_]/', '', (string) $request->input('resumableIdentifier'));
|
||||
|
||||
$filebag = $request->files;
|
||||
$file = $filebag->get('file');
|
||||
|
||||
$overwrite_on_upload = (bool) $this->config->get('overwrite_on_upload', false);
|
||||
|
||||
// php 8.1 fix
|
||||
// remove new key 'full_path' so it can preserve compatibility with symfony FileBag
|
||||
// see https://php.watch/versions/8.1/$_FILES-full-path
|
||||
if ($file && is_array($file) && array_key_exists('full_path', $file)) {
|
||||
unset($file['full_path']);
|
||||
$filebag->set('file', $file);
|
||||
$file = $filebag->get('file');
|
||||
}
|
||||
|
||||
if (! $file || ! $file->isValid() || $file->getSize() > $this->config->get('frontend_config.upload_max_size')) {
|
||||
return $response->json('Bad file', 422);
|
||||
}
|
||||
|
||||
$prefix = 'multipart_'.$identifier;
|
||||
|
||||
if ($this->tmpfs->exists($prefix.'_error')) {
|
||||
return $response->json('Chunk too big', 422);
|
||||
}
|
||||
|
||||
$stream = fopen($file->getPathName(), 'r');
|
||||
|
||||
$this->tmpfs->write($prefix.$file_name.'.part'.$chunk_number, $stream);
|
||||
|
||||
// check if all the parts present, and create the final destination file
|
||||
$chunks_size = 0;
|
||||
foreach ($this->tmpfs->findAll($prefix.'*') as $chunk) {
|
||||
$chunks_size += $chunk['size'];
|
||||
}
|
||||
|
||||
// file too big, cleanup to protect server, set error trap
|
||||
if ($chunks_size > $this->config->get('frontend_config.upload_max_size')) {
|
||||
foreach ($this->tmpfs->findAll($prefix.'*') as $tmp_chunk) {
|
||||
$this->tmpfs->remove($tmp_chunk['name']);
|
||||
}
|
||||
$this->tmpfs->write($prefix.'_error', '');
|
||||
|
||||
return $response->json('Chunk too big', 422);
|
||||
}
|
||||
|
||||
// if all the chunks are present, create final file and store it
|
||||
if ($chunks_size >= $total_size) {
|
||||
for ($i = 1; $i <= $total_chunks; ++$i) {
|
||||
$part = $this->tmpfs->readStream($prefix.$file_name.'.part'.$i);
|
||||
$this->tmpfs->write($file_name, $part['stream'], true);
|
||||
}
|
||||
|
||||
$final = $this->tmpfs->readStream($file_name);
|
||||
$res = $this->storage->store($destination, $final['filename'], $final['stream'], $overwrite_on_upload);
|
||||
|
||||
// cleanup
|
||||
$this->tmpfs->remove($file_name);
|
||||
foreach ($this->tmpfs->findAll($prefix.'*') as $expired_chunk) {
|
||||
$this->tmpfs->remove($expired_chunk['name']);
|
||||
}
|
||||
|
||||
return $res ? $response->json('Stored') : $response->json('Error storing file');
|
||||
}
|
||||
|
||||
return $response->json('Uploaded');
|
||||
}
|
||||
}
|
||||
28
backend/Controllers/ViewController.php
Normal file
28
backend/Controllers/ViewController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Controllers;
|
||||
|
||||
use Filegator\Config\Config;
|
||||
use Filegator\Kernel\Response;
|
||||
use Filegator\Services\View\ViewInterface;
|
||||
|
||||
class ViewController
|
||||
{
|
||||
public function index(Response $response, ViewInterface $view)
|
||||
{
|
||||
return $response->html($view->getIndexPage());
|
||||
}
|
||||
|
||||
public function getFrontendConfig(Response $response, Config $config)
|
||||
{
|
||||
return $response->json($config->get('frontend_config'));
|
||||
}
|
||||
}
|
||||
270
backend/Controllers/routes.php
Normal file
270
backend/Controllers/routes.php
Normal file
@@ -0,0 +1,270 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
[
|
||||
'route' => [
|
||||
'GET', '/', '\Filegator\Controllers\ViewController@index',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/login', '\Filegator\Controllers\AuthController@login',
|
||||
],
|
||||
'roles' => [
|
||||
'guest',
|
||||
],
|
||||
'permissions' => [
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/logout', '\Filegator\Controllers\AuthController@logout',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'GET', '/getuser', '\Filegator\Controllers\AuthController@getUser',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/changepassword', '\Filegator\Controllers\AuthController@changePassword',
|
||||
],
|
||||
'roles' => [
|
||||
'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'GET', '/getconfig', '\Filegator\Controllers\ViewController@getFrontendConfig',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/changedir', '\Filegator\Controllers\FileController@changeDirectory',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'read',
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/getdir', '\Filegator\Controllers\FileController@getDirectory',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'read',
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/copyitems', '\Filegator\Controllers\FileController@copyItems',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'read', 'write',
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/moveitems', '\Filegator\Controllers\FileController@moveItems',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'read', 'write',
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/renameitem', '\Filegator\Controllers\FileController@renameItem',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'read', 'write',
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/zipitems', '\Filegator\Controllers\FileController@zipItems',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'read', 'write', 'zip',
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/unzipitem', '\Filegator\Controllers\FileController@unzipItem',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'read', 'write', 'zip',
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/deleteitems', '\Filegator\Controllers\FileController@deleteItems',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'read', 'write',
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/createnew', '\Filegator\Controllers\FileController@createNew',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'read', 'write',
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'GET', '/upload', '\Filegator\Controllers\UploadController@chunkCheck',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'upload',
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/upload', '\Filegator\Controllers\UploadController@upload',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'upload',
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'GET', '/download', '\Filegator\Controllers\DownloadController@download',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'download',
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/batchdownload', '\Filegator\Controllers\DownloadController@batchDownloadCreate',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'read', 'download', 'batchdownload',
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'GET', '/batchdownload', '\Filegator\Controllers\DownloadController@batchDownloadStart',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'read', 'download', 'batchdownload',
|
||||
],
|
||||
],
|
||||
// admins
|
||||
[
|
||||
'route' => [
|
||||
'GET', '/listusers', '\Filegator\Controllers\AdminController@listUsers',
|
||||
],
|
||||
'roles' => [
|
||||
'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/storeuser', '\Filegator\Controllers\AdminController@storeUser',
|
||||
],
|
||||
'roles' => [
|
||||
'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/updateuser/{username}', '\Filegator\Controllers\AdminController@updateUser',
|
||||
],
|
||||
'roles' => [
|
||||
'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/deleteuser/{username}', '\Filegator\Controllers\AdminController@deleteUser',
|
||||
],
|
||||
'roles' => [
|
||||
'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
],
|
||||
],
|
||||
[
|
||||
'route' => [
|
||||
'POST', '/savecontent', '\Filegator\Controllers\FileController@saveContent',
|
||||
],
|
||||
'roles' => [
|
||||
'guest', 'user', 'admin',
|
||||
],
|
||||
'permissions' => [
|
||||
'read', 'write',
|
||||
],
|
||||
],
|
||||
];
|
||||
55
backend/Kernel/Request.php
Normal file
55
backend/Kernel/Request.php
Normal file
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Kernel;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
|
||||
|
||||
class Request extends SymfonyRequest
|
||||
{
|
||||
public function input($key, $default = null)
|
||||
{
|
||||
// first try GET, then POST
|
||||
$value = $this->get($key, $this->query->get($key));
|
||||
|
||||
// then look into JSON content, fallback to default
|
||||
if ($value === null) {
|
||||
$content = json_decode((string) $this->getContent());
|
||||
$value = isset($content->{$key}) ? $content->{$key} : $default;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
public function all()
|
||||
{
|
||||
$params = [];
|
||||
|
||||
// first look into JSON content
|
||||
$content = json_decode((string) $this->getContent());
|
||||
if (! empty($content)) {
|
||||
foreach ($content as $key => $param) {
|
||||
$params[$key] = $param;
|
||||
}
|
||||
}
|
||||
|
||||
// then try (and override) with POST
|
||||
foreach ($this->request as $key => $param) {
|
||||
$params[$key] = $param;
|
||||
}
|
||||
|
||||
// finally try (and override) with GET
|
||||
foreach ($this->query as $key => $param) {
|
||||
$params[$key] = $param;
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
||||
51
backend/Kernel/Response.php
Normal file
51
backend/Kernel/Response.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Kernel;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response as SymfonyResponse;
|
||||
|
||||
class Response extends SymfonyResponse
|
||||
{
|
||||
public function json($content, $status_code = 200)
|
||||
{
|
||||
$this->headers->set('Content-Type', 'application/json');
|
||||
$this->setStatusCode($status_code);
|
||||
|
||||
$this->setContent(json_encode([
|
||||
'data' => $content,
|
||||
]));
|
||||
}
|
||||
|
||||
public function html($content, $status_code = 200)
|
||||
{
|
||||
$this->setStatusCode($status_code);
|
||||
$this->setContent($content);
|
||||
}
|
||||
|
||||
public function redirect($url, $status_code = 302)
|
||||
{
|
||||
$this->setStatusCode($status_code);
|
||||
$this->setContent(
|
||||
sprintf('<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta http-equiv="refresh" content="0;url=%1$s" />
|
||||
<title>Redirecting to %1$s</title>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="%1$s">%1$s</a>.
|
||||
</body>
|
||||
</html>', htmlspecialchars($url, ENT_QUOTES, 'UTF-8'))
|
||||
);
|
||||
$this->headers->set('Location', $url);
|
||||
}
|
||||
}
|
||||
17
backend/Kernel/StreamedResponse.php
Normal file
17
backend/Kernel/StreamedResponse.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Kernel;
|
||||
|
||||
use Symfony\Component\HttpFoundation\StreamedResponse as SymfonyStreamedResponse;
|
||||
|
||||
class StreamedResponse extends SymfonyStreamedResponse
|
||||
{
|
||||
}
|
||||
148
backend/Services/Archiver/Adapters/ZipArchiver.php
Normal file
148
backend/Services/Archiver/Adapters/ZipArchiver.php
Normal file
@@ -0,0 +1,148 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Archiver\Adapters;
|
||||
|
||||
use Filegator\Services\Archiver\ArchiverInterface;
|
||||
use Filegator\Services\Service;
|
||||
use Filegator\Services\Storage\Filesystem as Storage;
|
||||
use Filegator\Services\Tmpfs\TmpfsInterface;
|
||||
use League\Flysystem\Config as Flyconfig;
|
||||
use League\Flysystem\Filesystem as Flysystem;
|
||||
use League\Flysystem\ZipArchive\ZipArchiveAdapter;
|
||||
|
||||
class ZipArchiver implements Service, ArchiverInterface
|
||||
{
|
||||
protected $archive;
|
||||
|
||||
protected $storage;
|
||||
|
||||
protected $tmpfs;
|
||||
|
||||
protected $uniqid;
|
||||
|
||||
protected $tmp_files = [];
|
||||
|
||||
public function __construct(TmpfsInterface $tmpfs)
|
||||
{
|
||||
$this->tmpfs = $tmpfs;
|
||||
}
|
||||
|
||||
public function init(array $config = [])
|
||||
{
|
||||
}
|
||||
|
||||
public function createArchive(Storage $storage): string
|
||||
{
|
||||
$this->uniqid = uniqid();
|
||||
|
||||
$this->archive = new Flysystem(
|
||||
new ZipAdapter($this->tmpfs->getFileLocation($this->uniqid))
|
||||
);
|
||||
|
||||
$this->storage = $storage;
|
||||
|
||||
return $this->uniqid;
|
||||
}
|
||||
|
||||
public function addDirectoryFromStorage(string $path)
|
||||
{
|
||||
$content = $this->storage->getDirectoryCollection($path, true);
|
||||
$this->archive->createDir($path);
|
||||
|
||||
foreach ($content->all() as $item) {
|
||||
if ($item['type'] == 'dir') {
|
||||
$this->archive->createDir($item['path']);
|
||||
}
|
||||
if ($item['type'] == 'file') {
|
||||
$this->addFileFromStorage($item['path']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function addFileFromStorage(string $path)
|
||||
{
|
||||
$file_uniqid = uniqid();
|
||||
|
||||
$file = $this->storage->readStream($path);
|
||||
|
||||
$this->tmpfs->write($file_uniqid, $file['stream']);
|
||||
|
||||
$this->archive->write($path, $this->tmpfs->getFileLocation($file_uniqid));
|
||||
|
||||
$this->tmp_files[] = $file_uniqid;
|
||||
}
|
||||
|
||||
public function uncompress(string $source, string $destination, Storage $storage)
|
||||
{
|
||||
$name = uniqid().'.zip';
|
||||
|
||||
$remote_archive = $storage->readStream($source);
|
||||
$this->tmpfs->write($name, $remote_archive['stream']);
|
||||
|
||||
$archive = new Flysystem(
|
||||
new ZipAdapter($this->tmpfs->getFileLocation($name))
|
||||
);
|
||||
|
||||
$contents = $archive->listContents('/', true);
|
||||
|
||||
foreach ($contents as $item) {
|
||||
$stream = null;
|
||||
if ($item['type'] == 'dir') {
|
||||
$storage->createDir($destination, $item['path']);
|
||||
}
|
||||
if ($item['type'] == 'file') {
|
||||
$stream = $archive->readStream($item['path']);
|
||||
$storage->store($destination.'/'.$item['dirname'], $item['basename'], $stream);
|
||||
}
|
||||
if (is_resource($stream)) {
|
||||
fclose($stream);
|
||||
}
|
||||
}
|
||||
|
||||
$this->tmpfs->remove($name);
|
||||
}
|
||||
|
||||
public function closeArchive()
|
||||
{
|
||||
$this->archive->getAdapter()->getArchive()->close();
|
||||
|
||||
foreach ($this->tmp_files as $file) {
|
||||
$this->tmpfs->remove($file);
|
||||
}
|
||||
}
|
||||
|
||||
public function storeArchive($destination, $name)
|
||||
{
|
||||
$this->closeArchive();
|
||||
|
||||
$file = $this->tmpfs->readStream($this->uniqid);
|
||||
$this->storage->store($destination, $name, $file['stream']);
|
||||
if (is_resource($file['stream'])) {
|
||||
fclose($file['stream']);
|
||||
}
|
||||
|
||||
$this->tmpfs->remove($this->uniqid);
|
||||
}
|
||||
}
|
||||
|
||||
class ZipAdapter extends ZipArchiveAdapter
|
||||
{
|
||||
public function write($path, $contents, Flyconfig $config)
|
||||
{
|
||||
$location = $this->applyPathPrefix($path);
|
||||
|
||||
// using addFile instead of addFromString
|
||||
// is more memory efficient
|
||||
$this->archive->addFile($contents, $location);
|
||||
|
||||
return compact('path', 'contents');
|
||||
}
|
||||
}
|
||||
28
backend/Services/Archiver/ArchiverInterface.php
Normal file
28
backend/Services/Archiver/ArchiverInterface.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Archiver;
|
||||
|
||||
use Filegator\Services\Storage\Filesystem;
|
||||
|
||||
interface ArchiverInterface
|
||||
{
|
||||
public function createArchive(Filesystem $storage): string;
|
||||
|
||||
public function uncompress(string $source, string $destination, Filesystem $storage);
|
||||
|
||||
public function addDirectoryFromStorage(string $path);
|
||||
|
||||
public function addFileFromStorage(string $path);
|
||||
|
||||
public function closeArchive();
|
||||
|
||||
public function storeArchive($destination, $name);
|
||||
}
|
||||
204
backend/Services/Auth/Adapters/Database.php
Normal file
204
backend/Services/Auth/Adapters/Database.php
Normal file
@@ -0,0 +1,204 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Auth\Adapters;
|
||||
|
||||
use Dibi\Connection;
|
||||
use Dibi\Row;
|
||||
use Filegator\Services\Auth\AuthInterface;
|
||||
use Filegator\Services\Auth\User;
|
||||
use Filegator\Services\Auth\UsersCollection;
|
||||
use Filegator\Services\Service;
|
||||
use Filegator\Services\Session\SessionStorageInterface as Session;
|
||||
use Filegator\Utils\PasswordHash;
|
||||
|
||||
class Database implements Service, AuthInterface
|
||||
{
|
||||
use PasswordHash;
|
||||
|
||||
const SESSION_KEY = 'database_auth';
|
||||
const SESSION_HASH = 'database_auth_hash';
|
||||
|
||||
const GUEST_USERNAME = 'guest';
|
||||
|
||||
protected $session;
|
||||
|
||||
protected $conn;
|
||||
|
||||
public function __construct(Session $session)
|
||||
{
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
public function init(array $config = [])
|
||||
{
|
||||
$this->conn = new Connection($config);
|
||||
}
|
||||
|
||||
public function getConnection()
|
||||
{
|
||||
return $this->conn;
|
||||
}
|
||||
|
||||
public function user(): ?User
|
||||
{
|
||||
if (! $this->session) return null;
|
||||
|
||||
$user = $this->session->get(self::SESSION_KEY, null);
|
||||
$hash = $this->session->get(self::SESSION_HASH, null);
|
||||
|
||||
if (! $user) return null;
|
||||
|
||||
$ret = $this->getConnection()
|
||||
->fetch('SELECT * FROM users WHERE username = ?', $user->getUsername())
|
||||
;
|
||||
|
||||
if ($ret && $hash == $ret->password.$ret->permissions.$ret->homedir.$ret->role) {
|
||||
return $user;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function authenticate($username, $password): bool
|
||||
{
|
||||
$ret = $this->getConnection()
|
||||
->fetch('SELECT * FROM users WHERE username = ?', $username)
|
||||
;
|
||||
|
||||
if ($ret && $this->verifyPassword($password, $ret->password)) {
|
||||
$user = $this->mapToUserObject($ret);
|
||||
$this->store($user);
|
||||
$this->session->set(self::SESSION_HASH, $ret->password.$ret->permissions.$ret->homedir.$ret->role);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function forget()
|
||||
{
|
||||
return $this->session->invalidate();
|
||||
}
|
||||
|
||||
public function store(User $user)
|
||||
{
|
||||
return $this->session->set(self::SESSION_KEY, $user);
|
||||
}
|
||||
|
||||
public function update($username, User $user, $password = ''): User
|
||||
{
|
||||
if (! $this->find($username)) {
|
||||
throw new \Exception('User not found');
|
||||
}
|
||||
|
||||
if ($username != $user->getUsername() && $this->find($user->getUsername())) {
|
||||
throw new \Exception('Username already taken');
|
||||
}
|
||||
|
||||
$this->getConnection()->query('UPDATE users SET', [
|
||||
'username' => $user->getUsername(),
|
||||
'name' => $user->getName(),
|
||||
'homedir' => $user->getHomeDir(),
|
||||
'permissions' => $user->getPermissions(true),
|
||||
'role' => $user->getRole(),
|
||||
], 'WHERE username = ?', $username);
|
||||
|
||||
if ($password) {
|
||||
$this->getConnection()->query('UPDATE users SET', [
|
||||
'password' => $this->hashPassword($password),
|
||||
], 'WHERE username = ?', $username);
|
||||
}
|
||||
|
||||
return $this->find($user->getUsername()) ?: $user;
|
||||
}
|
||||
|
||||
public function add(User $user, $password): User
|
||||
{
|
||||
if ($this->find($user->getUsername())) {
|
||||
throw new \Exception('Username already taken');
|
||||
}
|
||||
|
||||
$this->getConnection()->query('INSERT INTO users', [
|
||||
'username' => $user->getUsername(),
|
||||
'name' => $user->getName(),
|
||||
'role' => $user->getRole(),
|
||||
'homedir' => $user->getHomeDir(),
|
||||
'permissions' => $user->getPermissions(true),
|
||||
'password' => $this->hashPassword($password),
|
||||
]);
|
||||
|
||||
return $this->find($user->getUsername()) ?: $user;
|
||||
}
|
||||
|
||||
public function delete(User $user)
|
||||
{
|
||||
if (! $this->find($user->getUsername())) {
|
||||
throw new \Exception('User not found');
|
||||
}
|
||||
|
||||
$this->getConnection()->query('DELETE FROM users WHERE username = ?', $user->getUsername());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function find($username): ?User
|
||||
{
|
||||
$row = $this->getConnection()
|
||||
->fetch('SELECT * FROM users WHERE username = ?', $username)
|
||||
;
|
||||
|
||||
if ($row) {
|
||||
return $this->mapToUserObject($row);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getGuest(): User
|
||||
{
|
||||
$guest = $this->find(self::GUEST_USERNAME);
|
||||
|
||||
if (! $guest || ! $guest->isGuest()) {
|
||||
throw new \Exception('No guest account');
|
||||
}
|
||||
|
||||
return $guest;
|
||||
}
|
||||
|
||||
public function allUsers(): UsersCollection
|
||||
{
|
||||
$users = new UsersCollection();
|
||||
|
||||
$rows = $this->getConnection()
|
||||
->fetchAll('SELECT * FROM users')
|
||||
;
|
||||
|
||||
foreach ($rows as $user) {
|
||||
$users->addUser($this->mapToUserObject($user));
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
protected function mapToUserObject(Row $user): User
|
||||
{
|
||||
$new = new User();
|
||||
|
||||
$new->setRole(isset($user->role) ? $user->role : 'guest');
|
||||
$new->setHomedir(isset($user->homedir) ? $user->homedir : '/');
|
||||
$new->setPermissions(isset($user->permissions) ? $user->permissions : '', true);
|
||||
$new->setUsername(isset($user->username) ? $user->username : '');
|
||||
$new->setName(isset($user->name) ? $user->name : 'Guest');
|
||||
|
||||
return $new;
|
||||
}
|
||||
}
|
||||
216
backend/Services/Auth/Adapters/JsonFile.php
Normal file
216
backend/Services/Auth/Adapters/JsonFile.php
Normal file
@@ -0,0 +1,216 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Auth\Adapters;
|
||||
|
||||
use Filegator\Services\Auth\AuthInterface;
|
||||
use Filegator\Services\Auth\User;
|
||||
use Filegator\Services\Auth\UsersCollection;
|
||||
use Filegator\Services\Service;
|
||||
use Filegator\Services\Session\SessionStorageInterface as Session;
|
||||
use Filegator\Utils\PasswordHash;
|
||||
|
||||
class JsonFile implements Service, AuthInterface
|
||||
{
|
||||
use PasswordHash;
|
||||
|
||||
const SESSION_KEY = 'json_auth';
|
||||
const SESSION_HASH = 'json_auth_hash';
|
||||
|
||||
const GUEST_USERNAME = 'guest';
|
||||
|
||||
protected $session;
|
||||
|
||||
protected $file;
|
||||
|
||||
public function __construct(Session $session)
|
||||
{
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
public function init(array $config = [])
|
||||
{
|
||||
if (! file_exists($config['file'])) {
|
||||
copy($config['file'].'.blank', $config['file']);
|
||||
}
|
||||
|
||||
$this->file = $config['file'];
|
||||
}
|
||||
|
||||
public function user(): ?User
|
||||
{
|
||||
if (! $this->session) return null;
|
||||
|
||||
$user = $this->session->get(self::SESSION_KEY, null);
|
||||
$hash = $this->session->get(self::SESSION_HASH, null);
|
||||
|
||||
if ($user) {
|
||||
foreach ($this->getUsers() as $u) {
|
||||
if ($u['username'] == $user->getUsername() && $hash == $u['password'].$u['permissions'].$u['homedir'].$u['role']) {
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function authenticate($username, $password): bool
|
||||
{
|
||||
$all_users = $this->getUsers();
|
||||
|
||||
foreach ($all_users as &$u) {
|
||||
if ($u['username'] == $username && $this->verifyPassword($password, $u['password'])) {
|
||||
$user = $this->mapToUserObject($u);
|
||||
$this->store($user);
|
||||
$this->session->set(self::SESSION_HASH, $u['password'].$u['permissions'].$u['homedir'].$u['role']);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function forget()
|
||||
{
|
||||
return $this->session->invalidate();
|
||||
}
|
||||
|
||||
public function store(User $user)
|
||||
{
|
||||
return $this->session->set(self::SESSION_KEY, $user);
|
||||
}
|
||||
|
||||
public function update($username, User $user, $password = ''): User
|
||||
{
|
||||
$all_users = $this->getUsers();
|
||||
|
||||
if ($username != $user->getUsername() && $this->find($user->getUsername())) {
|
||||
throw new \Exception('Username already taken');
|
||||
}
|
||||
|
||||
foreach ($all_users as &$u) {
|
||||
if ($u['username'] == $username) {
|
||||
$u['username'] = $user->getUsername();
|
||||
$u['name'] = $user->getName();
|
||||
$u['role'] = $user->getRole();
|
||||
$u['homedir'] = $user->getHomeDir();
|
||||
$u['permissions'] = $user->getPermissions(true);
|
||||
|
||||
if ($password) {
|
||||
$u['password'] = $this->hashPassword($password);
|
||||
}
|
||||
|
||||
$this->saveUsers($all_users);
|
||||
|
||||
return $this->find($user->getUsername()) ?: $user;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \Exception('User not found');
|
||||
}
|
||||
|
||||
public function add(User $user, $password): User
|
||||
{
|
||||
if ($this->find($user->getUsername())) {
|
||||
throw new \Exception('Username already taken');
|
||||
}
|
||||
|
||||
$all_users = $this->getUsers();
|
||||
|
||||
$all_users[] = [
|
||||
'username' => $user->getUsername(),
|
||||
'name' => $user->getName(),
|
||||
'role' => $user->getRole(),
|
||||
'homedir' => $user->getHomeDir(),
|
||||
'permissions' => $user->getPermissions(true),
|
||||
'password' => $this->hashPassword($password),
|
||||
];
|
||||
|
||||
$this->saveUsers($all_users);
|
||||
|
||||
return $this->find($user->getUsername()) ?: $user;
|
||||
}
|
||||
|
||||
public function delete(User $user)
|
||||
{
|
||||
$all_users = $this->getUsers();
|
||||
|
||||
foreach ($all_users as $key => $u) {
|
||||
if ($u['username'] == $user->getUsername()) {
|
||||
unset($all_users[$key]);
|
||||
$this->saveUsers($all_users);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
throw new \Exception('User not found');
|
||||
}
|
||||
|
||||
public function find($username): ?User
|
||||
{
|
||||
foreach ($this->getUsers() as $user) {
|
||||
if ($user['username'] == $username) {
|
||||
return $this->mapToUserObject($user);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getGuest(): User
|
||||
{
|
||||
$guest = $this->find(self::GUEST_USERNAME);
|
||||
|
||||
if (! $guest || ! $guest->isGuest()) {
|
||||
throw new \Exception('No guest account');
|
||||
}
|
||||
|
||||
return $guest;
|
||||
}
|
||||
|
||||
public function allUsers(): UsersCollection
|
||||
{
|
||||
$users = new UsersCollection();
|
||||
|
||||
foreach ($this->getUsers() as $user) {
|
||||
$users->addUser($this->mapToUserObject($user));
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
protected function mapToUserObject(array $user): User
|
||||
{
|
||||
$new = new User();
|
||||
|
||||
$new->setUsername($user['username']);
|
||||
$new->setName($user['name']);
|
||||
$new->setRole($user['role']);
|
||||
$new->setHomedir($user['homedir']);
|
||||
$new->setPermissions($user['permissions'], true);
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
protected function getUsers(): array
|
||||
{
|
||||
$users = json_decode((string) file_get_contents($this->file), true);
|
||||
|
||||
return is_array($users) ? $users : [];
|
||||
}
|
||||
|
||||
protected function saveUsers(array $users)
|
||||
{
|
||||
return file_put_contents($this->file, json_encode($users), LOCK_EX);
|
||||
}
|
||||
}
|
||||
250
backend/Services/Auth/Adapters/LDAP.php
Normal file
250
backend/Services/Auth/Adapters/LDAP.php
Normal file
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Adriano Hänggli <https://github.com/ahaenggli>
|
||||
*
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Auth\Adapters;
|
||||
|
||||
use Filegator\Services\Auth\AuthInterface;
|
||||
use Filegator\Services\Auth\User;
|
||||
use Filegator\Services\Auth\UsersCollection;
|
||||
use Filegator\Services\Service;
|
||||
use Filegator\Services\Session\SessionStorageInterface as Session;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class LDAP implements Service, AuthInterface
|
||||
{
|
||||
const SESSION_KEY = 'LDAP_auth';
|
||||
const GUEST_USERNAME = 'guest';
|
||||
|
||||
protected $session;
|
||||
protected $private_repos = false;
|
||||
protected $ldap_server;
|
||||
protected $ldap_bindDN;
|
||||
protected $ldap_bindPass;
|
||||
protected $ldap_baseDN;
|
||||
protected $ldap_filter;
|
||||
protected $ldap_attributes;
|
||||
protected $ldap_userFieldMapping;
|
||||
|
||||
public function __construct(Session $session)
|
||||
{
|
||||
$this->session = $session;
|
||||
}
|
||||
|
||||
public function init(array $config = [])
|
||||
{
|
||||
if(!isset($config['ldap_server']) || empty($config['ldap_server']))
|
||||
throw new \Exception('config ldap_server missing');
|
||||
|
||||
if (!extension_loaded('ldap')) throw new \Exception('ldap extension missing');
|
||||
|
||||
if($connect=ldap_connect($config['ldap_server'])){
|
||||
ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
$this->private_repos = $config['private_repos'];
|
||||
$this->ldap_server = $config['ldap_server'];
|
||||
$this->ldap_bindDN = $config['ldap_bindDN'];
|
||||
$this->ldap_bindPass = $config['ldap_bindPass'];
|
||||
$this->ldap_baseDN = $config['ldap_baseDN'];
|
||||
$this->ldap_filter = $config['ldap_filter'];
|
||||
$this->ldap_attributes = isset($config['ldap_attributes']) ? $config['ldap_attributes'] : ['*'];
|
||||
$this->ldap_userFieldMapping = $config['ldap_userFieldMapping'];
|
||||
}else {
|
||||
@ldap_close($connect);
|
||||
throw new \Exception('could not connect to domain');
|
||||
}
|
||||
|
||||
@ldap_close($connect);
|
||||
}
|
||||
|
||||
public function user(): ?User
|
||||
{
|
||||
return $this->session ? $this->session->get(self::SESSION_KEY, null) : null;
|
||||
}
|
||||
|
||||
public function authenticate($username, $password): bool
|
||||
{
|
||||
// prevent anonymous binding
|
||||
if(!isset($password) || empty($password)) return false;
|
||||
if(!isset($username) || empty($username)) return false;
|
||||
|
||||
// remove (optional) domains from the username
|
||||
if(!empty($this->ldap_userFieldMapping['username_RemoveDomains']) && is_array($this->ldap_userFieldMapping['username_RemoveDomains'])) {
|
||||
$username = str_replace($this->ldap_userFieldMapping['username_RemoveDomains'], '', $username);
|
||||
}
|
||||
|
||||
// add the domain to the username
|
||||
if(!empty($this->ldap_userFieldMapping['username_AddDomain'])) {
|
||||
if(strpos($username, $this->ldap_userFieldMapping['username_AddDomain']) === false) {
|
||||
$username .= $this->ldap_userFieldMapping['username_AddDomain'];
|
||||
}
|
||||
}
|
||||
|
||||
$all_users = $this->getUsers();
|
||||
|
||||
foreach ($all_users as &$u) {
|
||||
if ($u['username'] == $username && $this->verifyPassword($u['userDN'], $password)) {
|
||||
$user = $this->mapToUserObject($u);
|
||||
$this->store($user);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function forget()
|
||||
{
|
||||
return $this->session->invalidate();
|
||||
}
|
||||
|
||||
public function store(User $user)
|
||||
{
|
||||
return $this->session->set(self::SESSION_KEY, $user);
|
||||
}
|
||||
|
||||
public function update($username, User $user, $password = ''): User
|
||||
{
|
||||
return new User(); // not used
|
||||
}
|
||||
|
||||
public function add(User $user, $password): User
|
||||
{
|
||||
return new User(); // not used
|
||||
}
|
||||
|
||||
public function delete(User $user)
|
||||
{
|
||||
return true; // not used
|
||||
}
|
||||
|
||||
public function find($username): ?User
|
||||
{
|
||||
foreach ($this->getUsers() as $user) {
|
||||
if ($user['username'] == $username) {
|
||||
return $this->mapToUserObject($user);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getGuest(): User
|
||||
{
|
||||
$guest = $this->find(self::GUEST_USERNAME);
|
||||
|
||||
if (!$guest || !$guest->isGuest()) {
|
||||
$guest = new User();
|
||||
$guest->setUsername('guest');
|
||||
$guest->setName('Guest');
|
||||
$guest->setRole('guest');
|
||||
$guest->setHomedir('/');
|
||||
$guest->setPermissions([]);
|
||||
return $guest;
|
||||
}
|
||||
|
||||
return $guest;
|
||||
}
|
||||
|
||||
public function allUsers(): UsersCollection
|
||||
{
|
||||
$users = new UsersCollection();
|
||||
|
||||
foreach ($this->getUsers() as $user) {
|
||||
$users->addUser($this->mapToUserObject($user));
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
protected function mapToUserObject(array $user): User
|
||||
{
|
||||
$new = new User();
|
||||
$new->setUsername($user['username']);
|
||||
$new->setName($user['name']);
|
||||
$new->setRole($user['role']);
|
||||
$new->setHomedir($user['homedir']);
|
||||
$new->setPermissions($user['permissions'], true);
|
||||
return $new;
|
||||
}
|
||||
|
||||
protected function getUsers(): array
|
||||
{
|
||||
$ldapConn = @ldap_connect($this->ldap_server);
|
||||
if (!$ldapConn) throw new \Exception('Cannot Connect to LDAP server');
|
||||
@ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
|
||||
$ldapBind = @ldap_bind($ldapConn, $this->ldap_bindDN,$this->ldap_bindPass);
|
||||
if (!$ldapBind) throw new \Exception('Cannot Bind to LDAP server: Wrong credentials?');
|
||||
|
||||
// search the LDAP server for users
|
||||
$ldapSearch = @ldap_search($ldapConn, $this->ldap_baseDN, $this->ldap_filter, $this->ldap_attributes);
|
||||
$ldapResults = @ldap_get_entries($ldapConn, $ldapSearch);
|
||||
@ldap_close($ldapConn);
|
||||
|
||||
$users = [];
|
||||
|
||||
for ($item = 0; $item < $ldapResults['count']; $item++)
|
||||
{
|
||||
$user = [];
|
||||
$user['username'] = $ldapResults[$item][$this->ldap_userFieldMapping['username']][0];
|
||||
$user['name'] = $ldapResults[$item][$this->ldap_userFieldMapping['name']][0];
|
||||
$user['role'] = 'user';
|
||||
$user['homedir'] = '/';
|
||||
$user['permissions']=$this->ldap_userFieldMapping['default_permissions'];
|
||||
$user['userDN'] = $ldapResults[$item][$this->ldap_userFieldMapping['userDN']];
|
||||
|
||||
if(!empty($this->ldap_userFieldMapping['username_AddDomain'])){
|
||||
if(strpos($user['username'], $this->ldap_userFieldMapping['username_AddDomain']) === false)
|
||||
$user['username'] .= $this->ldap_userFieldMapping['username_AddDomain'];
|
||||
}
|
||||
|
||||
if(is_array($this->ldap_userFieldMapping['admin_usernames']))
|
||||
{
|
||||
if(in_array($user['username'], $this->ldap_userFieldMapping['admin_usernames'])) $user['role'] = 'admin';
|
||||
}
|
||||
|
||||
// private repositories for each user?
|
||||
if ($this->private_repos) {
|
||||
$user['homedir'] = '/'.$user['username'];
|
||||
}
|
||||
|
||||
// ...but not for admins
|
||||
if ($user['role'] == 'admin'){
|
||||
$user['homedir'] = '/';
|
||||
$user['permissions'] = 'read|write|upload|download|batchdownload|zip';
|
||||
}
|
||||
|
||||
if(is_array($user) && !empty($user)) $users[] = $user;
|
||||
}
|
||||
// print_r($users); // uncomment this line to see all available ldap-login-users
|
||||
return is_array($users) ? $users : [];
|
||||
}
|
||||
|
||||
private function verifyPassword($auth_user, $password)
|
||||
{
|
||||
if(!isset($this->ldap_server) || empty($this->ldap_server)) return false;
|
||||
if(!extension_loaded('ldap')) return false;
|
||||
|
||||
if($connect=ldap_connect($this->ldap_server))
|
||||
{
|
||||
ldap_set_option($connect, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
if($bind=ldap_bind($connect, $auth_user, $password)){
|
||||
@ldap_close($connect);
|
||||
return true;
|
||||
} else {
|
||||
@ldap_close($connect);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@ldap_close($connect);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
140
backend/Services/Auth/Adapters/WPAuth.php
Normal file
140
backend/Services/Auth/Adapters/WPAuth.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Auth\Adapters;
|
||||
|
||||
use Filegator\Services\Auth\AuthInterface;
|
||||
use Filegator\Services\Auth\User;
|
||||
use Filegator\Services\Auth\UsersCollection;
|
||||
use Filegator\Services\Service;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class WPAuth implements Service, AuthInterface
|
||||
{
|
||||
|
||||
protected $permissions = [];
|
||||
|
||||
protected $private_repos = false;
|
||||
|
||||
public function init(array $config = [])
|
||||
{
|
||||
define('WP_USE_THEMES', false);
|
||||
require_once(rtrim($config['wp_dir'], '/').'/wp-blog-header.php');
|
||||
|
||||
$this->permissions = isset($config['permissions']) ? (array)$config['permissions'] : [];
|
||||
$this->private_repos = isset($config['private_repos']) ? (bool)$config['private_repos'] : false;
|
||||
}
|
||||
|
||||
public function user(): ?User
|
||||
{
|
||||
$wpuser = wp_get_current_user();
|
||||
|
||||
if ($wpuser->exists()) {
|
||||
return $this->transformUser($wpuser);
|
||||
}
|
||||
|
||||
return $this->getGuest();
|
||||
}
|
||||
|
||||
public function transformUser($wpuser): User
|
||||
{
|
||||
$user = new User();
|
||||
$user->setUsername($wpuser->data->user_login);
|
||||
$user->setName($wpuser->data->display_name);
|
||||
$user->setRole('user');
|
||||
$user->setPermissions($this->permissions);
|
||||
$user->setHomedir('/');
|
||||
|
||||
// private repositories for each user?
|
||||
if ($this->private_repos) {
|
||||
$user->setHomedir('/'.$wpuser->data->user_login);
|
||||
}
|
||||
|
||||
// ...but not for wp admins
|
||||
if (in_array('administrator', (array)$wpuser->roles)) {
|
||||
$user->setHomedir('/');
|
||||
}
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
public function authenticate($username, $password): bool
|
||||
{
|
||||
$creds = array(
|
||||
'user_login' => $username,
|
||||
'user_password' => $password,
|
||||
'remember' => true
|
||||
);
|
||||
|
||||
$wpuser = wp_signon($creds, false);
|
||||
|
||||
if (!is_wp_error($wpuser)) {
|
||||
wp_set_current_user($wpuser->data->ID);
|
||||
$this->transformUser($wpuser);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function forget()
|
||||
{
|
||||
wp_logout();
|
||||
}
|
||||
|
||||
public function store(User $user)
|
||||
{
|
||||
return null; // not used
|
||||
}
|
||||
|
||||
public function update($username, User $user, $password = ''): User
|
||||
{
|
||||
if ($password && get_current_user_id()) {
|
||||
wp_set_password($password, get_current_user_id());
|
||||
}
|
||||
return new User(); // not used
|
||||
}
|
||||
|
||||
public function add(User $user, $password): User
|
||||
{
|
||||
return new User(); // not used
|
||||
}
|
||||
|
||||
public function delete(User $user)
|
||||
{
|
||||
return true; // not used
|
||||
}
|
||||
|
||||
public function find($username): ?User
|
||||
{
|
||||
return null; // not used
|
||||
}
|
||||
|
||||
public function allUsers(): UsersCollection
|
||||
{
|
||||
return new UsersCollection(); // not used
|
||||
}
|
||||
|
||||
public function getGuest(): User
|
||||
{
|
||||
$guest = new User();
|
||||
|
||||
$guest->setUsername('guest');
|
||||
$guest->setName('Guest');
|
||||
$guest->setRole('guest');
|
||||
$guest->setHomedir('/');
|
||||
$guest->setPermissions([]);
|
||||
|
||||
return $guest;
|
||||
}
|
||||
|
||||
}
|
||||
34
backend/Services/Auth/AuthInterface.php
Normal file
34
backend/Services/Auth/AuthInterface.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Auth;
|
||||
|
||||
interface AuthInterface
|
||||
{
|
||||
public function user(): ?User;
|
||||
|
||||
public function authenticate($username, $password): bool;
|
||||
|
||||
public function forget();
|
||||
|
||||
public function find($username): ?User;
|
||||
|
||||
public function store(User $user);
|
||||
|
||||
public function update($username, User $user, $password = ''): User;
|
||||
|
||||
public function add(User $user, $password): User;
|
||||
|
||||
public function delete(User $user);
|
||||
|
||||
public function getGuest(): User;
|
||||
|
||||
public function allUsers(): UsersCollection;
|
||||
}
|
||||
158
backend/Services/Auth/User.php
Normal file
158
backend/Services/Auth/User.php
Normal file
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Auth;
|
||||
|
||||
class User implements \JsonSerializable
|
||||
{
|
||||
protected $role = 'guest';
|
||||
|
||||
protected $permissions = [];
|
||||
|
||||
protected $username = '';
|
||||
|
||||
protected $homedir = '';
|
||||
|
||||
protected $name = '';
|
||||
|
||||
protected $available_roles = ['guest', 'user', 'admin'];
|
||||
|
||||
protected $available_permissions = ['read', 'write', 'upload', 'download', 'batchdownload', 'zip'];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
}
|
||||
|
||||
public function isGuest(): bool
|
||||
{
|
||||
return 'guest' == $this->role;
|
||||
}
|
||||
|
||||
public function isUser(): bool
|
||||
{
|
||||
return 'user' == $this->role;
|
||||
}
|
||||
|
||||
public function isAdmin(): bool
|
||||
{
|
||||
return 'admin' == $this->role;
|
||||
}
|
||||
|
||||
public function hasRole($check): bool
|
||||
{
|
||||
if (is_array($check)) {
|
||||
return in_array($this->getRole(), $check);
|
||||
}
|
||||
|
||||
return $this->getRole() == $check;
|
||||
}
|
||||
|
||||
public function getRole(): string
|
||||
{
|
||||
return $this->role;
|
||||
}
|
||||
|
||||
public function hasPermissions($check): bool
|
||||
{
|
||||
if (empty($check)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_array($check)) {
|
||||
return count(array_intersect($check, $this->getPermissions())) == count($check);
|
||||
}
|
||||
|
||||
return in_array($check, $this->getPermissions());
|
||||
}
|
||||
|
||||
public function setName(string $name): void
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function setUsername(string $username)
|
||||
{
|
||||
$this->username = $username;
|
||||
}
|
||||
|
||||
public function getUsername(): string
|
||||
{
|
||||
return $this->username;
|
||||
}
|
||||
|
||||
public function setHomedir(string $homedir)
|
||||
{
|
||||
$this->homedir = $homedir;
|
||||
}
|
||||
|
||||
public function getHomeDir(): string
|
||||
{
|
||||
return $this->homedir;
|
||||
}
|
||||
|
||||
public function setRole(string $role)
|
||||
{
|
||||
$this->checkValidRole($role);
|
||||
|
||||
$this->role = $role;
|
||||
}
|
||||
|
||||
public function setPermissions($permissions, $encoded = false)
|
||||
{
|
||||
if ($encoded) {
|
||||
$permissions = explode('|', $permissions);
|
||||
}
|
||||
|
||||
$this->checkValidPermissions($permissions);
|
||||
|
||||
$this->permissions = $permissions;
|
||||
}
|
||||
|
||||
public function getPermissions($encoded = false)
|
||||
{
|
||||
return $encoded ? implode('|', $this->permissions) : $this->permissions;
|
||||
}
|
||||
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return [
|
||||
'role' => $this->getRole(),
|
||||
'permissions' => $this->getPermissions(),
|
||||
'homedir' => $this->getHomeDir(),
|
||||
'username' => $this->getUsername(),
|
||||
'name' => $this->getName(),
|
||||
];
|
||||
}
|
||||
|
||||
protected function checkValidRole($role)
|
||||
{
|
||||
if (! in_array($role, $this->available_roles)) {
|
||||
throw new \Exception("User role {$role} does not exists.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function checkValidPermissions(array $permissions)
|
||||
{
|
||||
foreach ($permissions as $permission) {
|
||||
if ($permission && ! in_array($permission, $this->available_permissions)) {
|
||||
throw new \Exception("Permission {$permission} does not exists.");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
23
backend/Services/Auth/UsersCollection.php
Normal file
23
backend/Services/Auth/UsersCollection.php
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Auth;
|
||||
|
||||
use Filegator\Utils\Collection;
|
||||
|
||||
class UsersCollection implements \JsonSerializable
|
||||
{
|
||||
use Collection;
|
||||
|
||||
public function addUser(User $user)
|
||||
{
|
||||
$this->add($user);
|
||||
}
|
||||
}
|
||||
48
backend/Services/Cors/Cors.php
Normal file
48
backend/Services/Cors/Cors.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Cors;
|
||||
|
||||
use Filegator\Kernel\Request;
|
||||
use Filegator\Kernel\Response;
|
||||
use Filegator\Services\Service;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Cors implements Service
|
||||
{
|
||||
protected $request;
|
||||
|
||||
protected $response;
|
||||
|
||||
public function __construct(Request $request, Response $response)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
}
|
||||
|
||||
public function init(array $config = [])
|
||||
{
|
||||
if ($config['enabled'] !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->response->headers->set('Access-Control-Allow-Origin', $this->request->headers->get('Origin'));
|
||||
$this->response->headers->set('Access-Control-Allow-Credentials', 'true');
|
||||
$this->response->headers->set('Access-Control-Expose-Headers', 'x-csrf-token');
|
||||
|
||||
if ($this->request->server->get('REQUEST_METHOD') == 'OPTIONS') {
|
||||
$this->response->headers->set('Access-Control-Allow-Headers', 'content-type, x-csrf-token');
|
||||
$this->response->send();
|
||||
die;
|
||||
}
|
||||
}
|
||||
}
|
||||
39
backend/Services/Logger/Adapters/MonoLogger.php
Normal file
39
backend/Services/Logger/Adapters/MonoLogger.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Logger\Adapters;
|
||||
|
||||
use Filegator\Services\Logger\LoggerInterface;
|
||||
use Filegator\Services\Service;
|
||||
use Monolog\ErrorHandler;
|
||||
use Monolog\Logger;
|
||||
|
||||
class MonoLogger implements Service, LoggerInterface
|
||||
{
|
||||
protected $logger;
|
||||
|
||||
public function init(array $config = [])
|
||||
{
|
||||
$this->logger = new Logger('default');
|
||||
|
||||
foreach ($config['monolog_handlers'] as $handler) {
|
||||
$this->logger->pushHandler($handler());
|
||||
}
|
||||
|
||||
$handler = new ErrorHandler($this->logger);
|
||||
$handler->registerErrorHandler([], true);
|
||||
$handler->registerFatalHandler();
|
||||
}
|
||||
|
||||
public function log(string $message, int $level = Logger::INFO)
|
||||
{
|
||||
$this->logger->log($level, $message);
|
||||
}
|
||||
}
|
||||
16
backend/Services/Logger/LoggerInterface.php
Normal file
16
backend/Services/Logger/LoggerInterface.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Logger;
|
||||
|
||||
interface LoggerInterface
|
||||
{
|
||||
public function log(string $message, int $level);
|
||||
}
|
||||
80
backend/Services/Router/Router.php
Normal file
80
backend/Services/Router/Router.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Router;
|
||||
|
||||
use FastRoute;
|
||||
use Filegator\Container\Container;
|
||||
use Filegator\Kernel\Request;
|
||||
use Filegator\Services\Auth\AuthInterface;
|
||||
use Filegator\Services\Service;
|
||||
|
||||
class Router implements Service
|
||||
{
|
||||
protected $request;
|
||||
|
||||
protected $auth;
|
||||
|
||||
protected $container;
|
||||
|
||||
protected $user;
|
||||
|
||||
public function __construct(Request $request, AuthInterface $auth, Container $container)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->container = $container;
|
||||
$this->user = $auth->user() ?: $auth->getGuest();
|
||||
}
|
||||
|
||||
public function init(array $config = [])
|
||||
{
|
||||
$uri = '/';
|
||||
$http_method = $this->request->getMethod();
|
||||
|
||||
if ($r = $this->request->query->get($config['query_param'])) {
|
||||
$this->request->query->remove($config['query_param']);
|
||||
$uri = rawurldecode($r);
|
||||
}
|
||||
|
||||
$routes = require $config['routes_file'];
|
||||
|
||||
$dispatcher = FastRoute\simpleDispatcher(function (FastRoute\RouteCollector $r) use ($routes) {
|
||||
if ($routes && ! empty($routes)) {
|
||||
foreach ($routes as $params) {
|
||||
if ($this->user->hasRole($params['roles']) && $this->user->hasPermissions($params['permissions'])) {
|
||||
$r->addRoute($params['route'][0], $params['route'][1], $params['route'][2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$routeInfo = $dispatcher->dispatch($http_method, $uri);
|
||||
|
||||
$controller = '\Filegator\Controllers\ErrorController';
|
||||
$action = 'notFound';
|
||||
$params = [];
|
||||
|
||||
switch ($routeInfo[0]) {
|
||||
case FastRoute\Dispatcher::FOUND:
|
||||
$handler = explode('@', $routeInfo[1]);
|
||||
$controller = $handler[0];
|
||||
$action = $handler[1];
|
||||
$params = $routeInfo[2];
|
||||
|
||||
break;
|
||||
case FastRoute\Dispatcher::METHOD_NOT_ALLOWED:
|
||||
$action = 'methodNotAllowed';
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
$this->container->call([$controller, $action], $params);
|
||||
}
|
||||
}
|
||||
99
backend/Services/Security/Security.php
Normal file
99
backend/Services/Security/Security.php
Normal file
@@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Security;
|
||||
|
||||
use Filegator\Kernel\Request;
|
||||
use Filegator\Kernel\Response;
|
||||
use Filegator\Services\Service;
|
||||
use Filegator\Services\Logger\LoggerInterface;
|
||||
use Symfony\Component\Security\Csrf\CsrfToken;
|
||||
use Symfony\Component\Security\Csrf\CsrfTokenManager;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
class Security implements Service
|
||||
{
|
||||
protected $request;
|
||||
|
||||
protected $response;
|
||||
|
||||
protected $logger;
|
||||
|
||||
public function __construct(Request $request, Response $response, LoggerInterface $logger)
|
||||
{
|
||||
$this->request = $request;
|
||||
$this->response = $response;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function init(array $config = [])
|
||||
{
|
||||
if ($config['csrf_protection']) {
|
||||
|
||||
$key = isset($config['csrf_key']) ? $config['csrf_key'] : 'protection';
|
||||
|
||||
$http_method = $this->request->getMethod();
|
||||
$csrfManager = new CsrfTokenManager();
|
||||
|
||||
if (in_array($http_method, ['GET', 'HEAD', 'OPTIONS'])) {
|
||||
$this->response->headers->set('X-CSRF-Token', $csrfManager->getToken($key));
|
||||
} else {
|
||||
$token = new CsrfToken($key, $this->request->headers->get('X-CSRF-Token'));
|
||||
|
||||
if (! $csrfManager->isTokenValid($token)) {
|
||||
$this->logger->log("Csrf token not valid");
|
||||
die;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($config['ip_whitelist'])) $config['ip_allowlist'] = $config['ip_whitelist']; // deprecated, compatibility
|
||||
|
||||
if (! empty($config['ip_allowlist'])) {
|
||||
$pass = false;
|
||||
foreach ($config['ip_allowlist'] as $ip) {
|
||||
if ($this->request->getClientIp() == $ip) {
|
||||
$pass = true;
|
||||
}
|
||||
}
|
||||
if (! $pass) {
|
||||
$this->response->setStatusCode(403);
|
||||
$this->response->send();
|
||||
$this->logger->log("Forbidden - IP not found in allowlist ".$this->request->getClientIp());
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
if (! empty($config['ip_blacklist'])) $config['ip_denylist'] = $config['ip_blacklist']; // deprecated, compatibility
|
||||
|
||||
if (! empty($config['ip_denylist'])) {
|
||||
$pass = true;
|
||||
foreach ($config['ip_denylist'] as $ip) {
|
||||
if ($this->request->getClientIp() == $ip) {
|
||||
$pass = false;
|
||||
}
|
||||
}
|
||||
if (! $pass) {
|
||||
$this->response->setStatusCode(403);
|
||||
$this->response->send();
|
||||
$this->logger->log("Forbidden - IP matched against denylist ".$this->request->getClientIp());
|
||||
die;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (empty($config['allow_insecure_overlays']) || !$config['allow_insecure_overlays']) {
|
||||
$this->response->headers->set('X-Frame-Options', 'sameorigin');
|
||||
$this->response->headers->set('Content-Security-Policy', 'frame-ancestors \'self\'');
|
||||
}
|
||||
}
|
||||
}
|
||||
16
backend/Services/Service.php
Normal file
16
backend/Services/Service.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services;
|
||||
|
||||
interface Service
|
||||
{
|
||||
public function init(array $config = []);
|
||||
}
|
||||
74
backend/Services/Session/Adapters/SessionStorage.php
Normal file
74
backend/Services/Session/Adapters/SessionStorage.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Session\Adapters;
|
||||
|
||||
use Filegator\Kernel\Request;
|
||||
use Filegator\Services\Service;
|
||||
use Filegator\Services\Session\Session;
|
||||
use Filegator\Services\Session\SessionStorageInterface;
|
||||
|
||||
class SessionStorage implements Service, SessionStorageInterface
|
||||
{
|
||||
protected $request;
|
||||
|
||||
protected $config;
|
||||
|
||||
public function __construct(Request $request)
|
||||
{
|
||||
$this->request = $request;
|
||||
}
|
||||
|
||||
public function init(array $config = [])
|
||||
{
|
||||
// we don't have a previous session attached
|
||||
if (! $this->getSession()) {
|
||||
$handler = $config['handler'];
|
||||
$session = new Session($handler());
|
||||
$session->setName('filegator');
|
||||
|
||||
$this->setSession($session);
|
||||
}
|
||||
}
|
||||
|
||||
public function save()
|
||||
{
|
||||
$this->getSession()->save();
|
||||
}
|
||||
|
||||
public function set(string $key, $data)
|
||||
{
|
||||
return $this->getSession()->set($key, $data);
|
||||
}
|
||||
|
||||
public function get(string $key, $default = null)
|
||||
{
|
||||
return $this->getSession() ? $this->getSession()->get($key, $default) : $default;
|
||||
}
|
||||
|
||||
public function invalidate()
|
||||
{
|
||||
if (! $this->getSession()->isStarted()) {
|
||||
$this->getSession()->start();
|
||||
}
|
||||
|
||||
$this->getSession()->invalidate();
|
||||
}
|
||||
|
||||
private function setSession(Session $session)
|
||||
{
|
||||
return $this->request->setSession($session);
|
||||
}
|
||||
|
||||
private function getSession(): ?Session
|
||||
{
|
||||
return $this->request->getSession();
|
||||
}
|
||||
}
|
||||
17
backend/Services/Session/Session.php
Normal file
17
backend/Services/Session/Session.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Session;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Session\Session as SymfonySession;
|
||||
|
||||
class Session extends SymfonySession
|
||||
{
|
||||
}
|
||||
22
backend/Services/Session/SessionStorageInterface.php
Normal file
22
backend/Services/Session/SessionStorageInterface.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Session;
|
||||
|
||||
interface SessionStorageInterface
|
||||
{
|
||||
public function set(string $key, $data);
|
||||
|
||||
public function get(string $key, $default = null);
|
||||
|
||||
public function invalidate();
|
||||
|
||||
public function save();
|
||||
}
|
||||
57
backend/Services/Storage/DirectoryCollection.php
Normal file
57
backend/Services/Storage/DirectoryCollection.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Storage;
|
||||
|
||||
use Filegator\Utils\Collection;
|
||||
|
||||
class DirectoryCollection implements \JsonSerializable
|
||||
{
|
||||
use Collection;
|
||||
|
||||
protected $location;
|
||||
|
||||
public function __construct($location)
|
||||
{
|
||||
$this->location = $location;
|
||||
}
|
||||
|
||||
public function addFile(string $type, string $path, string $name, int $size, int $timestamp)
|
||||
{
|
||||
if (! in_array($type, ['dir', 'file', 'back'])) {
|
||||
throw new \Exception('Invalid file type.');
|
||||
}
|
||||
|
||||
$this->add([
|
||||
'type' => $type,
|
||||
'path' => $path,
|
||||
'name' => $name,
|
||||
'size' => $size,
|
||||
'time' => $timestamp,
|
||||
]);
|
||||
}
|
||||
|
||||
public function resetTimestamps($timestamp = 0)
|
||||
{
|
||||
foreach ($this->items as &$item) {
|
||||
$item['time'] = $timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
public function jsonSerialize()
|
||||
{
|
||||
$this->sortByValue('type');
|
||||
|
||||
return [
|
||||
'location' => $this->location,
|
||||
'files' => $this->items,
|
||||
];
|
||||
}
|
||||
}
|
||||
312
backend/Services/Storage/Filesystem.php
Normal file
312
backend/Services/Storage/Filesystem.php
Normal file
@@ -0,0 +1,312 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Storage;
|
||||
|
||||
use Filegator\Services\Service;
|
||||
use League\Flysystem\Filesystem as Flysystem;
|
||||
|
||||
class Filesystem implements Service
|
||||
{
|
||||
protected $separator;
|
||||
|
||||
protected $storage;
|
||||
|
||||
protected $path_prefix;
|
||||
|
||||
public function init(array $config = [])
|
||||
{
|
||||
$this->separator = $config['separator'];
|
||||
$this->path_prefix = $this->separator;
|
||||
|
||||
$adapter = $config['adapter'];
|
||||
$config = isset($config['config']) ? $config['config'] : [];
|
||||
|
||||
$this->storage = new Flysystem($adapter(), $config);
|
||||
}
|
||||
|
||||
public function createDir(string $path, string $name)
|
||||
{
|
||||
$destination = $this->joinPaths($this->applyPathPrefix($path), $name);
|
||||
|
||||
while (! empty($this->storage->listContents($destination, true))) {
|
||||
$destination = $this->upcountName($destination);
|
||||
}
|
||||
|
||||
return $this->storage->createDir($destination);
|
||||
}
|
||||
|
||||
public function createFile(string $path, string $name)
|
||||
{
|
||||
$destination = $this->joinPaths($this->applyPathPrefix($path), $name);
|
||||
|
||||
while ($this->storage->has($destination)) {
|
||||
$destination = $this->upcountName($destination);
|
||||
}
|
||||
|
||||
$this->storage->put($destination, '');
|
||||
}
|
||||
|
||||
public function fileExists(string $path)
|
||||
{
|
||||
$path = $this->applyPathPrefix($path);
|
||||
|
||||
return $this->storage->has($path);
|
||||
}
|
||||
|
||||
public function isDir(string $path)
|
||||
{
|
||||
$path = $this->applyPathPrefix($path);
|
||||
|
||||
return $this->storage->getSize($path) === false;
|
||||
}
|
||||
|
||||
public function copyFile(string $source, string $destination)
|
||||
{
|
||||
$source = $this->applyPathPrefix($source);
|
||||
$destination = $this->joinPaths($this->applyPathPrefix($destination), $this->getBaseName($source));
|
||||
|
||||
while ($this->storage->has($destination)) {
|
||||
$destination = $this->upcountName($destination);
|
||||
}
|
||||
|
||||
return $this->storage->copy($source, $destination);
|
||||
}
|
||||
|
||||
public function copyDir(string $source, string $destination)
|
||||
{
|
||||
$source = $this->applyPathPrefix($this->addSeparators($source));
|
||||
$destination = $this->applyPathPrefix($this->addSeparators($destination));
|
||||
$source_dir = $this->getBaseName($source);
|
||||
$real_destination = $this->joinPaths($destination, $source_dir);
|
||||
|
||||
while (! empty($this->storage->listContents($real_destination, true))) {
|
||||
$real_destination = $this->upcountName($real_destination);
|
||||
}
|
||||
|
||||
$contents = $this->storage->listContents($source, true);
|
||||
|
||||
if (empty($contents)) {
|
||||
$this->storage->createDir($real_destination);
|
||||
}
|
||||
|
||||
foreach ($contents as $file) {
|
||||
$source_path = $this->separator.ltrim($file['path'], $this->separator);
|
||||
$path = substr($source_path, strlen($source), strlen($source_path));
|
||||
|
||||
if ($file['type'] == 'dir') {
|
||||
$this->storage->createDir($this->joinPaths($real_destination, $path));
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($file['type'] == 'file') {
|
||||
$this->storage->copy($file['path'], $this->joinPaths($real_destination, $path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteDir(string $path)
|
||||
{
|
||||
return $this->storage->deleteDir($this->applyPathPrefix($path));
|
||||
}
|
||||
|
||||
public function deleteFile(string $path)
|
||||
{
|
||||
return $this->storage->delete($this->applyPathPrefix($path));
|
||||
}
|
||||
|
||||
public function readStream(string $path): array
|
||||
{
|
||||
if ($this->isDir($path)) {
|
||||
throw new \Exception('Cannot stream directory');
|
||||
}
|
||||
|
||||
$path = $this->applyPathPrefix($path);
|
||||
|
||||
return [
|
||||
'filename' => $this->getBaseName($path),
|
||||
'stream' => $this->storage->readStream($path),
|
||||
'filesize' => $this->storage->getSize($path),
|
||||
];
|
||||
}
|
||||
|
||||
public function move(string $from, string $to): bool
|
||||
{
|
||||
$from = $this->applyPathPrefix($from);
|
||||
$to = $this->applyPathPrefix($to);
|
||||
|
||||
while ($this->storage->has($to)) {
|
||||
$to = $this->upcountName($to);
|
||||
}
|
||||
|
||||
return $this->storage->rename($from, $to);
|
||||
}
|
||||
|
||||
public function rename(string $destination, string $from, string $to): bool
|
||||
{
|
||||
$from = $this->joinPaths($this->applyPathPrefix($destination), $from);
|
||||
$to = $this->joinPaths($this->applyPathPrefix($destination), $to);
|
||||
|
||||
while ($this->storage->has($to)) {
|
||||
$to = $this->upcountName($to);
|
||||
}
|
||||
|
||||
return $this->storage->rename($from, $to);
|
||||
}
|
||||
|
||||
public function store(string $path, string $name, $resource, bool $overwrite = false): bool
|
||||
{
|
||||
$destination = $this->joinPaths($this->applyPathPrefix($path), $name);
|
||||
|
||||
while ($this->storage->has($destination)) {
|
||||
if ($overwrite) {
|
||||
$this->storage->delete($destination);
|
||||
} else {
|
||||
$destination = $this->upcountName($destination);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->storage->putStream($destination, $resource);
|
||||
}
|
||||
|
||||
public function setPathPrefix(string $path_prefix)
|
||||
{
|
||||
$this->path_prefix = $this->addSeparators($path_prefix);
|
||||
}
|
||||
|
||||
public function getSeparator()
|
||||
{
|
||||
return $this->separator;
|
||||
}
|
||||
|
||||
public function getPathPrefix(): string
|
||||
{
|
||||
return $this->path_prefix;
|
||||
}
|
||||
|
||||
public function getDirectoryCollection(string $path, bool $recursive = false): DirectoryCollection
|
||||
{
|
||||
$collection = new DirectoryCollection($path);
|
||||
|
||||
foreach ($this->storage->listContents($this->applyPathPrefix($path), $recursive) as $entry) {
|
||||
// By default only 'path' and 'type' is present
|
||||
|
||||
$name = $this->getBaseName($entry['path']);
|
||||
$userpath = $this->stripPathPrefix($entry['path']);
|
||||
$dirname = isset($entry['dirname']) ? $entry['dirname'] : $path;
|
||||
$size = isset($entry['size']) ? $entry['size'] : 0;
|
||||
$timestamp = isset($entry['timestamp']) ? $entry['timestamp'] : 0;
|
||||
|
||||
$collection->addFile($entry['type'], $userpath, $name, $size, $timestamp);
|
||||
}
|
||||
|
||||
if (! $recursive && $this->addSeparators($path) !== $this->separator) {
|
||||
$collection->addFile('back', $this->getParent($path), '..', 0, 0);
|
||||
}
|
||||
|
||||
return $collection;
|
||||
}
|
||||
|
||||
protected function upcountCallback($matches)
|
||||
{
|
||||
$index = isset($matches[1]) ? intval($matches[1]) + 1 : 1;
|
||||
$ext = isset($matches[2]) ? $matches[2] : '';
|
||||
|
||||
return ' ('.$index.')'.$ext;
|
||||
}
|
||||
|
||||
protected function upcountName($name)
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/(?:(?: \(([\d]+)\))?(\.[^.]+))?$/',
|
||||
[$this, 'upcountCallback'],
|
||||
$name,
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
private function applyPathPrefix(string $path): string
|
||||
{
|
||||
if ($path == '..'
|
||||
|| strpos($path, '..'.$this->separator) !== false
|
||||
|| strpos($path, $this->separator.'..') !== false
|
||||
) {
|
||||
$path = $this->separator;
|
||||
}
|
||||
|
||||
return $this->joinPaths($this->getPathPrefix(), $path);
|
||||
}
|
||||
|
||||
private function stripPathPrefix(string $path): string
|
||||
{
|
||||
$path = $this->separator.ltrim($path, $this->separator);
|
||||
|
||||
if (substr($path, 0, strlen($this->getPathPrefix())) == $this->getPathPrefix()) {
|
||||
$path = $this->separator.substr($path, strlen($this->getPathPrefix()));
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
private function addSeparators(string $dir): string
|
||||
{
|
||||
if (! $dir || $dir == $this->separator || ! trim($dir, $this->separator)) {
|
||||
return $this->separator;
|
||||
}
|
||||
|
||||
return $this->separator.trim($dir, $this->separator).$this->separator;
|
||||
}
|
||||
|
||||
private function joinPaths(string $path1, string $path2): string
|
||||
{
|
||||
$path1 = $this->escapeDots($path1);
|
||||
$path2 = $this->escapeDots($path2);
|
||||
|
||||
if (! $path2 || ! trim($path2, $this->separator)) {
|
||||
return $this->addSeparators($path1);
|
||||
}
|
||||
|
||||
return $this->addSeparators($path1).ltrim($path2, $this->separator);
|
||||
}
|
||||
|
||||
private function getParent(string $dir): string
|
||||
{
|
||||
if (! $dir || $dir == $this->separator || ! trim($dir, $this->separator)) {
|
||||
return $this->separator;
|
||||
}
|
||||
|
||||
$tmp = explode($this->separator, trim($dir, $this->separator));
|
||||
array_pop($tmp);
|
||||
|
||||
return $this->separator.trim(implode($this->separator, $tmp), $this->separator);
|
||||
}
|
||||
|
||||
private function getBaseName(string $path): string
|
||||
{
|
||||
if (! $path || $path == $this->separator || ! trim($path, $this->separator)) {
|
||||
return $this->separator;
|
||||
}
|
||||
|
||||
$tmp = explode($this->separator, trim($path, $this->separator));
|
||||
|
||||
return (string) array_pop($tmp);
|
||||
}
|
||||
|
||||
private function escapeDots(string $path): string
|
||||
{
|
||||
$path = preg_replace('/\\\+\.{2,}/', '', $path);
|
||||
$path = preg_replace('/\.{2,}\\\+/', '', $path);
|
||||
$path = preg_replace('/\/+\.{2,}/', '', $path);
|
||||
$path = preg_replace('/\.{2,}\/+/', '', $path);
|
||||
|
||||
return $path;
|
||||
}
|
||||
}
|
||||
138
backend/Services/Tmpfs/Adapters/Tmpfs.php
Normal file
138
backend/Services/Tmpfs/Adapters/Tmpfs.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Tmpfs\Adapters;
|
||||
|
||||
use Filegator\Services\Service;
|
||||
use Filegator\Services\Tmpfs\TmpfsInterface;
|
||||
|
||||
class Tmpfs implements Service, TmpfsInterface
|
||||
{
|
||||
protected $path;
|
||||
|
||||
public function init(array $config = [])
|
||||
{
|
||||
$this->path = $config['path'];
|
||||
|
||||
if (! is_dir($this->path)) {
|
||||
mkdir($this->path);
|
||||
}
|
||||
|
||||
if (mt_rand(0, 99) < $config['gc_probability_perc']) {
|
||||
$this->clean($config['gc_older_than']);
|
||||
}
|
||||
}
|
||||
|
||||
public function write(string $filename, $data, $append = false)
|
||||
{
|
||||
$filename = $this->sanitizeFilename($filename);
|
||||
|
||||
$flags = 0;
|
||||
|
||||
if ($append) {
|
||||
$flags = FILE_APPEND;
|
||||
}
|
||||
|
||||
file_put_contents($this->getPath().$filename, $data, $flags);
|
||||
}
|
||||
|
||||
public function getFileLocation(string $filename): string
|
||||
{
|
||||
$filename = $this->sanitizeFilename($filename);
|
||||
|
||||
return $this->getPath().$filename;
|
||||
}
|
||||
|
||||
public function read(string $filename): string
|
||||
{
|
||||
$filename = $this->sanitizeFilename($filename);
|
||||
|
||||
return (string) file_get_contents($this->getPath().$filename);
|
||||
}
|
||||
|
||||
public function readStream(string $filename): array
|
||||
{
|
||||
$filename = $this->sanitizeFilename($filename);
|
||||
|
||||
$stream = fopen($this->getPath().$filename, 'r');
|
||||
$filesize = filesize($this->getPath().$filename);
|
||||
|
||||
return [
|
||||
'filename' => $filename,
|
||||
'stream' => $stream,
|
||||
'filesize' => $filesize,
|
||||
];
|
||||
}
|
||||
|
||||
public function exists(string $filename): bool
|
||||
{
|
||||
$filename = $this->sanitizeFilename($filename);
|
||||
|
||||
return file_exists($this->getPath().$filename);
|
||||
}
|
||||
|
||||
public function findAll($pattern): array
|
||||
{
|
||||
$files = [];
|
||||
$matches = glob($this->getPath().$pattern);
|
||||
if (! empty($matches)) {
|
||||
foreach ($matches as $filename) {
|
||||
if (is_file($filename)) {
|
||||
$files[] = [
|
||||
'name' => basename($filename),
|
||||
'size' => filesize($filename),
|
||||
'time' => filemtime($filename),
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
public function remove(string $filename)
|
||||
{
|
||||
$filename = $this->sanitizeFilename($filename);
|
||||
|
||||
unlink($this->getPath().$filename);
|
||||
}
|
||||
|
||||
public function clean(int $older_than)
|
||||
{
|
||||
$files = $this->findAll('*');
|
||||
foreach ($files as $file) {
|
||||
if (time() - $file['time'] >= $older_than) {
|
||||
$this->remove($file['name']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getPath(): string
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
|
||||
private function sanitizeFilename($filename)
|
||||
{
|
||||
$filename = (string) preg_replace(
|
||||
'~
|
||||
[<>:"/\\|?*]| # file system reserved https://en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
|
||||
[\x00-\x1F]| # control characters http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx
|
||||
[\x7F\xA0\xAD]| # non-printing characters DEL, NO-BREAK SPACE, SOFT HYPHEN
|
||||
[;\\\{}^\~`] # other non-safe
|
||||
~xu',
|
||||
'-',
|
||||
(string) $filename
|
||||
);
|
||||
|
||||
// maximize filename length to 255 bytes http://serverfault.com/a/9548/44086
|
||||
return mb_substr($filename, 0, 255);
|
||||
}
|
||||
}
|
||||
30
backend/Services/Tmpfs/TmpfsInterface.php
Normal file
30
backend/Services/Tmpfs/TmpfsInterface.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\Tmpfs;
|
||||
|
||||
interface TmpfsInterface
|
||||
{
|
||||
public function exists(string $filename): bool;
|
||||
|
||||
public function findAll($pattern): array;
|
||||
|
||||
public function write(string $filename, $data, $append);
|
||||
|
||||
public function read(string $filename): string;
|
||||
|
||||
public function readStream(string $filename): array;
|
||||
|
||||
public function remove(string $filename);
|
||||
|
||||
public function getFileLocation(string $filename): string;
|
||||
|
||||
public function clean(int $older_than);
|
||||
}
|
||||
61
backend/Services/View/Adapters/Vuejs.php
Normal file
61
backend/Services/View/Adapters/Vuejs.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\View\Adapters;
|
||||
|
||||
use Filegator\Config\Config;
|
||||
use Filegator\Services\Service;
|
||||
use Filegator\Services\View\ViewInterface;
|
||||
|
||||
class Vuejs implements Service, ViewInterface
|
||||
{
|
||||
private $config;
|
||||
|
||||
public function __construct(Config $config)
|
||||
{
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function init(array $config = [])
|
||||
{
|
||||
$this->add_to_head = isset($config['add_to_head']) ? $config['add_to_head'] : '';
|
||||
$this->add_to_body = isset($config['add_to_body']) ? $config['add_to_body'] : '';
|
||||
}
|
||||
|
||||
public function getIndexPage()
|
||||
{
|
||||
$title = APP_ENV == 'development' ? 'Development mode' : $this->config->get('frontend_config.app_name');
|
||||
$public_path = $this->config->get('public_path');
|
||||
$public_dir = $this->config->get('public_dir');
|
||||
|
||||
return '<!DOCTYPE html>
|
||||
<html lang=en>
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<meta http-equiv=X-UA-Compatible content="IE=edge">
|
||||
<meta name=viewport content="width=device-width,initial-scale=1">
|
||||
<meta name="robots" content="noindex,nofollow">
|
||||
<title>'.$title.'</title>
|
||||
'.$this->add_to_head.'
|
||||
<link href="'.$public_path.'css/app.css?'.@filemtime($public_dir.'/css/app.css').'" rel=stylesheet>
|
||||
<link href="'.$public_path.'css/chunk-vendors.css?'.@filemtime($public_dir.'/css/chunk-vendors.css').'" rel=stylesheet>
|
||||
</head>
|
||||
<body>
|
||||
<noscript><strong>Please enable JavaScript to continue.</strong></noscript>
|
||||
<div id=app></div>
|
||||
<script src="'.$public_path.'js/app.js?'.@filemtime($public_dir.'/js/app.js').'"></script>
|
||||
<script src="'.$public_path.'js/chunk-vendors.js?'.@filemtime($public_dir.'/js/chunk-vendors.js').'"></script>
|
||||
|
||||
'.$this->add_to_body.'
|
||||
</body>
|
||||
</html>
|
||||
';
|
||||
}
|
||||
}
|
||||
16
backend/Services/View/ViewInterface.php
Normal file
16
backend/Services/View/ViewInterface.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Services\View;
|
||||
|
||||
interface ViewInterface
|
||||
{
|
||||
public function getIndexPage();
|
||||
}
|
||||
58
backend/Utils/Collection.php
Normal file
58
backend/Utils/Collection.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Utils;
|
||||
|
||||
trait Collection
|
||||
{
|
||||
protected $items = [];
|
||||
|
||||
public function add($obj)
|
||||
{
|
||||
return $this->items[] = $obj;
|
||||
}
|
||||
|
||||
public function delete($obj)
|
||||
{
|
||||
foreach ($this->items as $key => $item) {
|
||||
if ($item === $obj) {
|
||||
unset($this->items[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function all()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
public function length()
|
||||
{
|
||||
return count($this->items);
|
||||
}
|
||||
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->items;
|
||||
}
|
||||
|
||||
public function sortByValue($value, $desc = false)
|
||||
{
|
||||
usort($this->items, function ($a, $b) use ($value) {
|
||||
return $a[$value] <=> $b[$value];
|
||||
});
|
||||
|
||||
if ($desc) {
|
||||
$this->items = array_reverse($this->items);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
33
backend/Utils/PasswordHash.php
Normal file
33
backend/Utils/PasswordHash.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the FileGator package.
|
||||
*
|
||||
* (c) Milos Stojanovic <alcalbg@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE file
|
||||
*/
|
||||
|
||||
namespace Filegator\Utils;
|
||||
|
||||
/**
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
trait PasswordHash
|
||||
{
|
||||
public static function hashPassword($value)
|
||||
{
|
||||
$hash = password_hash($value, PASSWORD_BCRYPT);
|
||||
|
||||
if ($hash === false) {
|
||||
throw new \Exception('Bcrypt hashing not supported.');
|
||||
}
|
||||
|
||||
return $hash;
|
||||
}
|
||||
|
||||
public static function verifyPassword($value, $hash)
|
||||
{
|
||||
return password_verify($value, $hash);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user