diff --git a/app/Console/Commands/CreateUser.php b/app/Console/Commands/CreateUser.php new file mode 100644 index 0000000..8c1c0c7 --- /dev/null +++ b/app/Console/Commands/CreateUser.php @@ -0,0 +1,74 @@ +argument('login'); + + login: + // If user was not provided, asking for it + if (empty($login)) { + $login = $this->ask('Enter the user\'s login'); + } + + if (! preg_match('~^[a-z0-9]{1,40}$~', $login)) { + $this->error('Invalid login format. Must only contains letters and numbers, between 1 and 40 chars'); + unset($login); + goto login; + } + + // Checking login unicity + if (Storage::disk('users')->exists($login.'.json')) { + $this->error('User "'.$login.'" already exists'); + unset($login); + goto login; + } + + password: + // Asking for user's password + $password = $this->secret('Enter the user\'s password'); + + if (strlen($password) < 5) { + $this->error('Invalid password format. Must only contains 5 chars minimum'); + unset($password); + goto password; + } + + try { + Storage::disk('users')->put($login.'.json', json_encode([ + 'login' => $login, + 'password' => Hash::make($password) + ])); + + $this->info('User has been created'); + } + catch(Exception $e) { + $this->error('An error occurred, could not create user'); + } + } +} diff --git a/app/Console/Commands/PurgeFiles.php b/app/Console/Commands/PurgeFiles.php index d321b20..0140cb7 100644 --- a/app/Console/Commands/PurgeFiles.php +++ b/app/Console/Commands/PurgeFiles.php @@ -13,7 +13,7 @@ class PurgeFiles extends Command * * @var string */ - protected $signature = 'storage:purge-expired'; + protected $signature = 'fs:purge-expired'; /** * The console command description. diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php index e6b9960..5dc2cba 100644 --- a/app/Console/Kernel.php +++ b/app/Console/Kernel.php @@ -13,6 +13,7 @@ class Kernel extends ConsoleKernel protected function schedule(Schedule $schedule): void { // $schedule->command('inspire')->hourly(); + $schedule->command('fs:purge-expired')->hourly(); } /** diff --git a/app/Http/Controllers/UploadController.php b/app/Http/Controllers/UploadController.php index ae39986..761bd2e 100644 --- a/app/Http/Controllers/UploadController.php +++ b/app/Http/Controllers/UploadController.php @@ -53,8 +53,8 @@ class UploadController extends Controller $metadata = Upload::getMetadata($bundleId); // Validating file - abort_if(! $request->hasFile('file'), 401); - abort_if(! $request->file('file')->isValid(), 401); + abort_if(! $request->hasFile('file'), 422); + abort_if(! $request->file('file')->isValid(), 422); $this->validate($request, [ 'file' => 'required|file|max:'.(Upload::fileMaxSize() / 1000) @@ -101,7 +101,7 @@ class UploadController extends Controller $metadata = Upload::getMetadata($bundleId); - abort_if(empty($request->file), 401); + abort_if(empty($request->file), 422); try { $metadata = Upload::deleteFile($bundleId, $request->file); diff --git a/app/Http/Controllers/WebController.php b/app/Http/Controllers/WebController.php index b4f851e..961ce52 100644 --- a/app/Http/Controllers/WebController.php +++ b/app/Http/Controllers/WebController.php @@ -2,19 +2,64 @@ namespace App\Http\Controllers; use App\Helpers\Upload; - +use Exception; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Hash; +use Illuminate\Support\Facades\Session; +use Illuminate\Support\Facades\Storage; class WebController extends Controller { - function homepage(Request $request) + public function homepage() { return view('homepage'); } + public function login() { + return view('login'); + } + + public function doLogin(Request $request) { + abort_if(! $request->ajax(), 403); + + $request->validate([ + 'login' => 'required', + 'password' => 'required' + ]); + + try { + if (Storage::disk('users')->missing($request->login.'.json')) { + throw new Exception('Authentication failed'); + } + + $json = Storage::disk('users')->get($request->login.'.json'); + + if (! $user = json_decode($json, true)) { + throw new Exception('Cannot decode JSON file'); + } + + if (! Hash::check($request->password, $user['password'])) { + throw new Exception('Authentication failed'); + } + + $request->session()->put('login', $request->login); + $request->session()->put('authenticated', true); + + return response()->json([ + 'result' => true, + ]); + } + catch (Exception $e) { + return response()->json([ + 'result' => false, + 'error' => $e->getMessage() + ]); + } + } + function newBundle(Request $request) { // Aborting if request is not AJAX - abort_if(! $request->ajax(), 401); + abort_if(! $request->ajax(), 403); $request->validate([ 'bundle_id' => 'required', diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php index 9482eb5..cf3dcd2 100644 --- a/app/Http/Kernel.php +++ b/app/Http/Kernel.php @@ -63,7 +63,7 @@ class Kernel extends HttpKernel 'signed' => \App\Http\Middleware\ValidateSignature::class, 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, - 'upload' => \App\Http\Middleware\UploadAccess::class, + 'can.upload' => \App\Http\Middleware\UploadAccess::class, 'access.owner' => \App\Http\Middleware\OwnerAccess::class, 'access.guest' => \App\Http\Middleware\GuestAccess::class ]; diff --git a/app/Http/Middleware/GuestAccess.php b/app/Http/Middleware/GuestAccess.php index 44db5e8..df4fdd9 100644 --- a/app/Http/Middleware/GuestAccess.php +++ b/app/Http/Middleware/GuestAccess.php @@ -17,9 +17,9 @@ class GuestAccess public function handle(Request $request, Closure $next): Response { // Aborting if Bundle ID is not present - abort_if(empty($request->route()->parameter('bundle')), 401); + abort_if(empty($request->route()->parameter('bundle')), 403); - abort_if(empty($request->auth), 401); + abort_if(empty($request->auth), 403); // Getting metadata $metadata = Upload::getMetadata($request->route()->parameter('bundle')); @@ -28,7 +28,7 @@ class GuestAccess abort_if(empty($metadata), 404); // Aborting if auth_token is different from URL param - abort_if($metadata['preview_token'] !== $request->auth, 401); + abort_if($metadata['preview_token'] !== $request->auth, 403); // Checking bundle expiration abort_if($metadata['expires_at'] < time(), 404); diff --git a/app/Http/Middleware/OwnerAccess.php b/app/Http/Middleware/OwnerAccess.php index 4134333..20b6ef1 100644 --- a/app/Http/Middleware/OwnerAccess.php +++ b/app/Http/Middleware/OwnerAccess.php @@ -17,10 +17,10 @@ class OwnerAccess public function handle(Request $request, Closure $next): Response { // Aborting if request is not AJAX - abort_if(! $request->ajax(), 401); + abort_if(! $request->ajax(), 403); // Aborting if Bundle ID is not present - abort_if(empty($request->route()->parameter('bundle')), 401); + abort_if(empty($request->route()->parameter('bundle')), 403); // Aborting if auth is not present $auth = null; @@ -30,7 +30,7 @@ class OwnerAccess else if (! empty($request->auth)) { $auth = $request->auth; } - abort_if(empty($auth), 401); + abort_if(empty($auth), 403); // Getting metadata $metadata = Upload::getMetadata($request->route()->parameter('bundle')); @@ -39,7 +39,7 @@ class OwnerAccess abort_if(empty($metadata), 404); // Aborting if auth_token is different from URL param - abort_if($metadata['owner_token'] !== $auth, 401); + abort_if($metadata['owner_token'] !== $auth, 403); return $next($request); } diff --git a/app/Http/Middleware/UploadAccess.php b/app/Http/Middleware/UploadAccess.php index 7445ce7..7a07d42 100644 --- a/app/Http/Middleware/UploadAccess.php +++ b/app/Http/Middleware/UploadAccess.php @@ -18,8 +18,14 @@ class UploadAccess */ public function handle(Request $request, Closure $next): Response { - if (Upload::canUpload($request->ip()) !== true) { - abort(401); + if ($request->session()->missing('authenticated') && Upload::canUpload($request->ip()) !== true) { + //return redirect('login'); + if ($request->ajax()) { + abort(401); + } + else { + return response()->view('login'); + } } return $next($request); diff --git a/config/filesystems.php b/config/filesystems.php index e87835e..859c0e3 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -60,6 +60,22 @@ return [ ] ], + 'users' => [ + 'driver' => 'local', + 'root' => env('STORAGE_PATH', storage_path('app/users')), + 'visibility' => 'private', + 'permissions' => [ + 'file' => [ + 'public' => 0600, + 'private' => 0600, + ], + 'dir' => [ + 'public' => 0755, + 'private' => 0700, + ], + ] + ], + 's3' => [ 'driver' => 's3', 'key' => env('AWS_ACCESS_KEY_ID'), diff --git a/lang/en/app.php b/lang/en/app.php index c100434..24b4ea2 100644 --- a/lang/en/app.php +++ b/lang/en/app.php @@ -75,7 +75,11 @@ return [ 'expired' => 'Expired', 'existing-bundles' => 'Your existing bundles', 'or-create' => 'New bundle', - 'no-existing-bundle' => 'No existing bundle' + 'no-existing-bundle' => 'No existing bundle', + 'authentication' => 'Authentication', + 'login' => 'Username', + 'password' => 'Password', + 'do-login' => 'Login now' ]; diff --git a/lang/fr/app.php b/lang/fr/app.php index e594171..ef0434f 100644 --- a/lang/fr/app.php +++ b/lang/fr/app.php @@ -75,7 +75,11 @@ return [ 'expired' => 'Expirés', 'existing-bundles' => 'Vos archives existantes', 'or-create' => 'Nouvelle archive', - 'no-existing-bundle' => 'Aucune archive existante' + 'no-existing-bundle' => 'Aucune archive existante', + 'authentication' => 'Authentification', + 'login' => 'Identifiant', + 'password' => 'Mot de passe', + 'do-login' => 'S\'authentifier' ]; diff --git a/readme.md b/readme.md index 38f6b38..bf9152a 100644 --- a/readme.md +++ b/readme.md @@ -1,37 +1,32 @@ # Files Sharing -> !!! +> > FILES SHARING VERSION 2 JUST RELEASED -> !!! +>
@lang('app.existing-bundles')
+ + @lang('app.no-existing-bundle')