Secure Asset/Media Folder through Auth Controller ? Laravel 5.2 - php

I have public/Asset/Media/folder
I can access this file publicly like below.
http://localhost/myapp/public/Asset/Media/1/phpunit.xml
Similarly there are other folders in the Asset/Media folder which are being created on the fly.
There are many files also present in those sub folder and are also present in Asset/Media folder
Is there any way, such that if I try to access any file in Asset/Media folder or any file present in the sub folder of Asset/Media folder, I should be redirected to login page because authentication is not done?
I meant, can i use Auth Middleware to secure this folder? if so, Is it a valid approach if we have to access the files from a Android App?

If you want to secure files, they need to go through Laravel. Accessing the file as you do (using the full path) does not go through Laravel. You can achieve this by creating a route:
Route::group(['middleware' => ['auth']], function () {
Route::get('/secure/file/{file_name}', 'FileController#file');
}
Then, create a Controller to access the file so that you can use Auth to check for permission to access. It also means that you should put the file in an inaccessible location and use the Laravel Filesystem to access the file using PHP:
class FileController extends Controller {
public function file()
{
return Storage::get('path/to/phpunit.xml');
}
}

Laravel 5.2 has introduced HTTP Middleware, i would advise you to do it.
https://laravel.com/docs/5.2/middleware#middleware-groups
this thread might help you to get it to work...
Laravel 5.2 Auth not Working

Use the route below for it:
Route::get('/myapp/public/Asset/Media/{id}', function ($id) {
if (Auth::guest()){
return Redirect::guest('login');
}else{
$img="/myapp/public/Asset/Media/".$id;
if(File::exists($img)) {
return Response::make($img, 200, array('content-type' => 'image/jpg'));
}else{
return false;
}
})->where('id', '.+');

My sample url is here:
http://domainname.com/storage/Asset/Media/1/filename.txt
My route
Route::get('/storage/Asset/Media/{ID}/{file}', array(
'as' => 'Files',
'uses' => 'User\Account\Media\MediaController#DownloadMedia',
));
Controller Action Method
public function DownloadMedia($ID) {
$headers = array(
'Content-Type' => 'application/octet-stream',
'Content-Disposition' => 'attachment; filename=somefile.txt"'
);
return response()->download(base_path("storage/Asset/Media/1/somefile.txt"));
}
Here important thing is I can use application/octet-stream to download any file type.

File in public folder will be accessible to everyone beacause of rewrite rules used by Laravel, Laravel won't even be called when someone access a file in the public folder.
So, you must put your restricted files somewhere else, maybe in storage folder but ultimately it doesn't matter.
After putting all your Asset/Media folder into the storage folder and updating your code who create your folder on the fly (How storage works).
Create a FileController :
PHP
class FileController extends Controller {
public function __construct() {
$this->middleware('auth');
}
public function downloadFile($filename) {
return response()->download(storage_path($filename), null, [], null);
}
}
The configure this route :
Route::get('file/{filename}', 'FileController#downloadFile')->where('filename', '^[^/]+$');
That's it, now only your authenticated user would be able to download asset files thanx to the middleware auth, that will also work for android app.

Related

Laravel 7 Redirect Login Authorization

I'm currently using template for my view using jQuery, HTML, and CSS.
When I tried to use middleware to authenticate the user role before accessing routes ,I stumble upon this error.
Error:
View [authorization.login] not found.
Is it because I use a different name or custom login for my page? How can I change the link to my current login address?
Here are my routes
Route::get('/user-register-page', 'LoginUserController#registerUserPage')->middleware(['auth','auth.admin']);
Route::post('/register-user', 'Auth\RegisterController#create')->name('register');
And here's my controller
class LoginUserController extends Controller
{
public function registerUserPage(){
return view('user-register');
}
public function loginUserPage(){
return view('user-login');
}
// public function registerNewUser(Request $request){
// dd('test');
// }
// public function loginUser(){
// }
}
In your resources folder inside views you will find a folder called auth, in case it is not there, create a folder called auth and drag in your file user-login and rename it to login.

Laravel - Include assets on Middleware Auth

Inside my app, exist a route group called admin, any route inside this group call two resources: public/css/admin.css and public/js/admin.js, but any unauthenticated user has access to these files. How can I include these files inside the Auth Middleware?
My admin routes:
Route::group(['prefix' => 'admin', 'middleware' => ['auth']], function () {
Route::get('/', 'Admin\IndexController#index')->name('panel');
Route::group(['prefix' => 'users'], function() {});
Route::group(['prefix' => 'settings'], function() {});
Route::fallback('Admin\ExceptionController#exception');
});
My resources links:
http://localhost:3000/css/admin.css
http://localhost:3000/js/admin.js
My resources links should be:
http://localhost:3000/admin/css/admin.css
http://localhost:3000/admin/js/admin.js
If I just create the folder admin inside the public folder I just got a 403 error...
What can I do about it?
I suppose you are using it because you don't want unauthenticated users to know the contents of these css/js files.
You shouldn't have any sensitive information in your css/js files, so there is no problem on serving them.
Otherwise, if you want to limit access to a file you should make the file download through PHP. For example you could have the file outside you public folder and make it conditional downloadable through a method that gets file contents and serves for download.
You should can make that public admin folder though, check file permissions and file ownership.
Update: Now we'll use storage instead of public directory.
Although I agree that you should not have any sensitive info in your css/js files but if you really want to serve the files to authenticated users you can do it with this work around.
NOTE: I have made the project publicaly avaiable on git so you can clone from there if you want. Git Repo
Create a directory for admin assets with permission 755
Create a helper function to serve admin assets.
Make the helper function available in blade.
Link the assets using the helper function in order to first authenticate and then serve the file.
Basic Idea:
The basic idea is to have a directory which no one can access via
browser.
Authenticate the user
Copy the files from protected directory.
Paste the files in a new directory (in storage) only associated with the authenticated user.
Delete the associated directory on user logout.
Implementation:
Created a directory called admin_assets in public directory.
Change the permission of the directory to 755.
Created a helper class named CommonHelper, and write functions to serve and delete admin assets.
Served the assets with these helper functions as following:
<link href="{{ asset( CommonHelper::serveAdminAssets('app.css', '/css/') ) }}" rel="stylesheet">
Deleted the files at logout.
Finally, as far as the user is logged in the files will be available for him/her, all files will be deleted from the folder once the user logs out.
CommonHelper class:
<?php
/**
*
*/
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Storage;
class CommonHelper {
public static function serveAdminAssets($fileName, $filePath) {
if( Auth::check() ) {
$adminAssetsBasePath = public_path().'/admin_assets';
$source = $adminAssetsBasePath.$filePath.$fileName;
$destDir = 'public/'.Auth::user()->id.$filePath;
$dest = $destDir.$fileName;
Storage::put($dest, file_get_contents($source));
return Storage::url($dest);
} else {
return '';
}
}
public static function removeAdminAssets($id) {
$destDir = storage_path('app/public/'.Auth::user()->id);
File::cleanDirectory($destDir);
File::deleteDirectory($destDir);
}
}
?>
Notes:
Remember, if you are using the local driver, all files that should be
publicly accessible should be placed in the storage/app/public
directory. Furthermore, you should create a symbolic link at
public/storage which points to the storage/app/public directory. Docs
Before deleting a directory you should empty it first.
Here is an example that you can apply.
Store your assets in storage directory.
then you can check whether it is an admin or Not.
In your view you can u can inject the Admin Assets Like.
<script>
{!! \Storage::disk('urdisk name')->get('admin.js'); !!}
</script>
for your css you can
<style>
{!! \Storage::disk('urdisk name')->get('admin.css'); !!}
</style>
Hope this helps
For applying auth to the asset files you need to go through from the laravel, not by accessing the files using full path, you need to access css/js files via route so that laravel will be able to apply auth on each files inside route group.
P.S Files must be saved inside storage folder, i-e storage/admin.css
Updated route group
Route::group(['prefix' => 'admin', 'middleware' => ['auth']], function () {
Route::get('/', 'Admin\IndexController#index')->name('panel');
Route::get('{file}', 'StaticFileController#serveFile');
Route::group(['prefix' => 'users'], function() {});
Route::group(['prefix' => 'settings'], function() {});
Route::fallback('Admin\ExceptionController#exception');
});
Controller
namespace App\Http\Controllers;
use Illuminate\Http\Request;
Use Response;
use App\Http\Requests;
class StaticFileController extends Controller
{
public function serveFile ($file)
{
$storagePath = storage_path($file);
$mimeType = mime_content_type($storagePath);
if( ! \File::exists($storagePath)){
return view('errorpages.404');
}
$headers = array(
'Content-Type' => $mimeType,
'Content-Disposition' => 'inline; filename="'.$file.'"'
);
return Response::make(file_get_contents($storagePath), 200, $headers);
}
}
Now your resources links would be
http://localhost:3000/admin/css/admin.css
http://localhost:3000/admin/js/admin.js
Hope this helps
For blade templates you can use the #auth directive.
When you're including these files in your blade templates, you can wrap the script tag in an #auth directive and they'll only be rendered when the user is authenticated.
E.g.
#auth
<script src="{{ asset('path/to/your/js') }}"></script>
#endauth
There's also an optional parameter that allows you to set the guard you want to check for e.g. #auth('your-guard').
It's available in Laravel 5.5+
Here's the documentation for a better overview of its use.

How to block laravel from setting session cookies (or any cookies) for specific routes?

We have some static pages that we are looking to cache. We still need them to go through Laravel 4.2. However, if a person visits said page, I don't want any cookies to be sent from Laravel, including the session cookies.
Is this possible?
In your app/Providers/RouteServiceProvider.php you are registering every route in routes/web.php under web middleware. Its the responsible for setting your session cookies.
You can register those routes in routes/api.php instead, or better yet, create another protected method in RouteServiceProvider to register those "cookieless" routes from another file, inside /routes folder.
Laravel 4.*
For laravel 4.* you can change session configuration on each page you need without cookies. You gotta change these configs in __construct
class StatelessPageController extends BaseController {
public function __construct(Config $config)
{
$config::set('session.driver', 'array');
$config::set('cookie.driver', 'array');
}
public function statelessPage()
{
return View::make('stateless.page', ['data' => 'value']);
}
}

Middleware for public storage in Laravel

I have this structure: laravel/storage/app/public/images/id_of_user/
Where id of user is the id of the user.
This is my middleware:
public function handle($request, Closure $next)
{
if($request->user_id == Auth::user()->id{
return $next($request);
}
return back();
}
and made a route like this:
Route::group(['middleware' => 'storagemiddleware'], function () {
Route::get('storage/images/{$user_id});
});
This doesn't work, tho. Everyone can still acess to everyones images. I honestly didn't expect this to work since it seems that storage has its own special routes and settings.
What I want is that only the users are allowed to see the content of their folder in their storage.
Any suggestions?
Read through this section of the documentation: https://laravel.com/docs/5.4/filesystem.
If the idea is to keep files accessible only to those you own them, you'll want to put them somewhere in the storage directory and then access them through a controller. Anything in your public directory is just that, public.
You could setup a directory structure like this storage/user_data/<$user_id>/ to keep things separated, then retrieve them from your route and the specified parameter.

protecting files by generating links with limited life cycle using Spatie URL-Signer

I am developing my project using Laravel (ocotber CMS) and using Spatie URL Signer package to protect my files with limited life links. I upload my file to a protected directory blacklisted by .htaccess file.
my .htaccess:
RewriteRule ^storage/app/uploads/protected/.* index.php [L,NC]
my file is uploaded to:
/storage/app/uploads/protected/58b/d45/789/58bd457897aab778152349.pdf
code that generates the link with expiry date:
UrlSigner::sign('http://localhost:8888/storage/app/uploads/protected/58b/d45/789/58bd457897aab778152349.pdf');
the generated link looks like:
http://localhost:8888/storage/app/uploads/protected/58b/d45/789/58bd457897aab778152349.pdf?expires=1488905432&signature=fd82b06725096b8e6c43221a9616e420
also i added the Route handling code that uses the package's middleware to protect links.
Route::get('protected-route', ['middleware' => 'signedurl', function () {
return 'Hello secret world!';
}]);
however the generated link is not available for download.
I do suspect this is because i have the file in a protected folder. When i try this with a public folder the file is available. but then there will be no protection on my file. because as you can see above the generated link contains the path to my folder.
Since you're handling private files, it's usually a better idea to not let the file system handle those things. Instead, let Laravel do it so you can do all the checks you need and you never have to expose the actual file:
Routes:
// set up a route group with your signedurl middleware
Route::group(['middleware' => 'signedurl'], function () {
// create a new route which references a controller
// the {path} is the location of the file
// so your URLs would look something like
// http://localhost:8888/media/storage/app/uploads/protected/58b/d45/789/58bd457897aab778152349.pdf
Route::get('media/{path}', 'MediaController#getPrivateFile');
// you can also do some validation using
// ->where('path', '.*?');
});
Then in your new controller:
class MediaController extends Controller
{
public function getPrivateFile(Request $request, $pathToFile)
{
// check if file exists
// if (file_exists) {
// # code...
// }
return response()->download($pathToFile);
}
}
This way your files can remain private, you can run your middleware, and you can do any additional checking you need to in the controller.

Categories