split19's avatar

Protect all files in a directory - user auth

I'm planning to build a review site for my clients with Laravel. I'll be uploading things like static HTML pages or microsites which I will upload to a subdirectory. eg /public/files/client id/project id/version/

I want to protect all files inside a client's "files" directory such that only the logged in client can view something like /public/files/client1/microsite1/v1/index.html and all files that load into the page (images, etc). I'll also be placing .zips of deliverable project files there. So I need to be able to protect the .zips from being downloaded by non-authenticated users. i don't believe I can do with this Laravel's Auth.

Is there a way to do this with Laravel? Or do I need to use .htaccess somehow? Or am I approaching this the wrong way? Any suggestions are welcome.

I have an older client review site that uses .htaccess to check a cookie to make sure the user is logged in, only then allowing files to be served. can i do something like that, maybe using the CSRF token or something?

Thanks!

0 likes
7 replies
JeroenVanOort's avatar

I solved a similar problem by using an Openstack object store. I upload all files to a private container and in my application, I redirect authorized users to a temporary URL on the object store. This way users can download the files directly from the object store.

pmall's avatar

You have to serve your files through a controller (create a route then use response()->download()) and protect it like any other route.

bashy's avatar

@pmall That would be preferred in most cases but for this one I think there's no easy answer. They have multiple sub sites in sub directories (not using Laravel?) but files attached to those sites which only that site should have access to.

split19's avatar

i went in a different direction, which seems to work well. sort of what @pmall suggested... i created a controller which handles all requests to a non-existent directory called project-assets and pulls each file (html, css, images, etc) from the storage_path() instead, where all uploaded assets are kept. so when the user goes to /project-assets/1/index.html, the file is pulled from storage_path() . /app/projects/1/index.html. same for css, images, etc. performance is probably slower, but this is only for client review purposes, not a public site.

here's the code in case it will help someone. i do have a question below about how to optimize this.

ProxyController.php

<?php namespace App\Http\Controllers;

use App\Http\Requests;
use App\Http\Controllers\Controller;

use Illuminate\Http\Request;

class ProxyController extends Controller {
     
    public function index($project_id, $filename)
    {
        $path = storage_path() . '/app/projects/' . $project_id . '/' . $filename;
                
        return $this->_getFile($path);
    }

    public function d1($project_id, $d1, $filename)
    {
        $path = storage_path() . '/app/projects/' . $project_id . '/' . $d1 . '/' . $filename;

        return $this->_getFile($path);
    }

    public function d2($project_id, $d1, $d2, $filename)
    {
        $path = storage_path() . '/app/projects/' . $project_id . '/' . $d1 . '/' . $d2 . '/' . $filename;

        return $this->_getFile($path);
    }
    
    public function d3($project_id, $d1, $d2, $d3, $filename)
    {
        $path = storage_path() . '/app/projects/' . $project_id . '/' . $d1 . '/' . $d2 . '/' . $d3 . '/' . $filename;

        return $this->_getFile($path);
    }

    private function _getFile($path)
    {
        $finfo = finfo_open(FILEINFO_MIME_TYPE);
        $mime = finfo_file($finfo, $path);
        finfo_close($finfo);
        
        // CSS files return text/plain
        $path_parts = pathinfo($path);
        if ($path_parts['extension'] == "css")
        {
            $mime = "text/css";
        }
        
        $response = response(file_get_contents($path), 200)->header('Content-Type', $mime);

    return $response;

    }


}

routes.php

// PROJECT ASSETS PROXY
Route::get('project-assets/{project_id}/{filename}', 'ProxyController@index');
Route::get('project-assets/{project_id}/{d1}/{filename}', 'ProxyController@d1');
Route::get('project-assets/{project_id}/{d1}/{d2}/{filename}', 'ProxyController@d2');
Route::get('project-assets/{project_id}/{d1}/{d2}/{d3}/{filename}', 'ProxyController@d3');

my question is: how can i optimize this so i don't have to have the multiple methods and routes with different numbers of directory depths?

pmall's avatar

Man you want to protect a whole website ? Even the css etc ? Just configure a basic http auth with apache/nginx.

What you are doing is really overkill.

split19's avatar

@pmall well, they're not really whole websites. they're simple landing pages and also many versions of Flash banners, as well as .zip downloads. yes, of course i could do an ugly apache login but i want to avoid that.

pmall's avatar

of course i could do an ugly apache login but i want to avoid that

But this is what you need here. Not a crazy controller that returns all the files.

Please or to participate in this conversation.