1

I have this site built in silverstripe, I have members' pages for members only.

Everything worked fine until I put some pdf links on one of the members' pages, then google search can actually pick those pdf files up and everyone ends up to be able to see those pdf files without being a member.

I notice when it is on members' page, you must log in, the url is www.mysite.com/members/
However when i click on one of the links on that page, the url changes into www.mysite/assets/Uploads/members/books/myfirstbook.pdf

As long as someone google search the book's name eg myfirstbook and mysite name, the pdf link on members' page will show, and you can click through it and view the pdf content.

How can I block non members to view those pdf files? I tried robot.txt and secure files model, they didn't work. Please help. Thanks!

grumpypanda
  • 571
  • 1
  • 10
  • 37

2 Answers2

4

I had something (kind of) similar with one client. They wanted visitors to fill in a form with some info before downloading some files which could be on any page.

Basically what I did is create a folder under AssetAdmin called FilteredDownload and any place in that folder will go through the filtering (form etc...)

I had this rule in the .htaccess (which could be edited...) redirecting any request to any of those file to a Downloader_Controller

RewriteCond %{REQUEST_URI} /assets/FilteredDownload [NC]
RewriteCond %{REQUEST_URI} !/assets_temp [NC]
RewriteCond %{REQUEST_FILENAME} \.(pdf|zip|rar|7z|doc|docx|xls|xlsx|ppt|pptx)$ [NC]
RewriteRule .* downloader?file=%{REQUEST_FILENAME}&%{QUERY_STRING} [L,NC]

Then I just used that controller to server the form and the files.

In your case you could just use that Controller to test if the visitor is logged in and if not redirect him/her to the login page, deny access or whatever...

Not exactly the same as your problem but I could easily see this adapted?


EDIT

Had a better look at it and based on the above, this seems to work:

SilverStripe 3+

Adding this to .htaccess:

RewriteCond %{REQUEST_URI} /assets/MembersOnly [NC]
RewriteCond %{REQUEST_FILENAME} \.(pdf|zip|rar|7z|doc|docx|xls|xlsx|ppt|pptx)$ [NC]
RewriteRule .* filedownloadpermission?file=%{REQUEST_FILENAME}&%{QUERY_STRING} [L,NC]

So every files uploaded under /assets/MembersOnly will first have their request passed through /filedownloadpermission before download.

Define the Director rule in config.yml:

---
Name: myroutes
After: framework/routes#coreroutes
---
Director:
  rules:
    'filedownloadpermission/$Action/$ID/$Name': 'FileDownloadPermission_controller'

Then our controller FileDownloadPermission_controller.php that will check for permissions before serving the file or not:

<?php

class FileDownloadPermission_controller extends ContentController
{
    private static $allowed_actions = array (
    );

    public function init() {
        parent::init();

        if( !$member = Member::currentUser() )
        {
                  Security::permissionFailure();
        }    
    }

    public function index()
    {
        $file = $this->request->getVar('file');
        $fileAssetPath = substr($file, stripos($file, 'assets'));    
                $fileObj = File::get()->filter(array('Filename' => $fileAssetPath))->first();

                if ( $fileObj )
                {
                   $data = file_get_contents( $fileObj->getFullPath() );
                   $name = $fileObj->getFilename();
                   $response = SS_HTTPRequest::send_file($data, $name);
                   return $response;
                }
                else {
                   //Return 404 or whatever...
                }
    }
}

This is actually written for SilverStripe 3.1 but can easily be adapted for 2.4:

  • $allowed_actions should be public
  • Director rule to be added to _config.php instead
  • Update File::get()... to DataObject::get...

So this give us for SilverStripe 2.4+

in _config.php

Director::AddRules(100, array('filedownloadpermission/$Action/$ID/$OtherID' => 'FileDownloadPermission_controller'));

and our controller FileDownloadPermission_controller.php:

<?php

class FileDownloadPermission_controller extends ContentController
{
    static $allowed_actions = array (
    );

    public function init() {
        parent::init();

        if( !$member = Member::currentUser() )
        {
                  Security::permissionFailure();
        }    
    }

    public function index()
    {
        $file = $this->request->getVar('file');
        $fileAssetPath = substr($file, stripos($file, 'assets')); 
                $fileObj = DataObject::get(
                                 "File",
                                 "Filename = '".$fileAssetPath."'",
                                 null, null, "1");

                if ( $fileObj )
                {
                    $fileObj = $fileObj->shift();

                    $data = file_get_contents( $fileObj->getFullPath() );
                    $name = $fileObj->getFilename();
                    $response = SS_HTTPRequest::send_file($data, $name);
                    return $response;
                }
                else {
                    //Return 404 or whatever...
                }
    }
}

This is quite a quick solution and can probably be a bit better but should give you a good start_

colymba
  • 2,644
  • 15
  • 15
  • Yes your Director::addRules is correct. This is to be added to mysite/_config.php. And create the FileDownloadPermission_controller.php file under mysite/code (it's a good place to have it). Remember to flush the cache with ?flush=all – colymba May 27 '13 at 16:05
  • Thanks Colymba, yes that's what I did, now I've got a server error showing me the error page, and it logs me out of the members page. Where do you think I should look for the problem here? Any more places I need to change on your code or on the site code for it to work? Thank you. – grumpypanda May 27 '13 at 16:27
  • What kind of server error? 500? 404? Do you have a log? I haven't got a SS2.4 install I could test so it's hard to tell. Let's see what error you're getting first. – colymba May 27 '13 at 16:51
  • Finally got to test this in SS2.4 and there was some extra things to downgrade sorry. Mainly DataObject::get()... I've updated the answer with the full SS2.4 code. Update your files and see how it goes. – colymba May 27 '13 at 17:30
  • You are a legend, thanks, it works. Just one further question is there any way to make the pdfs not to be downloaded, but shown on another tab after you click the link? Or does it have to be downloaded for this method to work. I really appreciate your help, thank you! – grumpypanda May 28 '13 at 01:14
  • glad it worked. if you can mark the answer as accepted, this will help others too. To output the file directly you might have to change SS_HTTPRequest::send_file to something else that output the file straight to the buffer like fpassthru – colymba May 28 '13 at 06:13
  • Yes will select yours as answer for sure as you've saved my day, thank you. Just before I close it, how would you use something like fpassthru instead of SS_HTTPRequest::send_file in Silverstripe? (I used it, but the pdf page turned blank) It would be great to open those pdfs in another tab rather than downloading if you could help me a bit further on this. Thanks a lot, I do appreciate it. – grumpypanda May 28 '13 at 07:01
  • Note that it probably will depend on the user's browser config if a file is shown or downloaded. Though, I haven't used fpassthru here are a few examples: http://php.net/manual/en/function.fpassthru.php / http://stackoverflow.com/questions/1597732/php-force-file-download-and-ie-yet-again and also readfile could work maybe http://php.net/manual/en/function.readfile.php . you'll probably have to set the header properly though... sorry I can't help much more on this.. – colymba May 28 '13 at 07:46
  • All Good, thanks so much for all your help, you've been great. I hope one day I can be like you to help others, many many thanks. – grumpypanda May 28 '13 at 07:54
0

robot.txt will need some time to be applied. However, it doesn't really secure anything, it just removes it from a search engine's index.

Which secure file module have you tried? https://github.com/hamishcampbell/silverstripe-securefiles What's the specific problem with "it didn't work"?

This will only work with SilverStripe 2.x. Do you need a solution for 3.x?

xeraa
  • 10,456
  • 3
  • 33
  • 66
  • Thanks Xeraa, yes I tried your link of the secure file module, the problem is that the site is using 'KickAssets' to upload all the files for the asset folder, I have difficulties to implement both together. Can you please teach me how to combine "secure files" and "kickassets" together? Many thanks. – grumpypanda May 24 '13 at 13:53
  • Btw I'm using SS 2.4.5, so it shouldn't be a problem. Do you think I should use robot.txt as well as 'secure files' for my purpose? If you could give me some help/tips on how to combine "secure files" and "kickassets" together would be greatly appreciated. Thank you. – grumpypanda May 24 '13 at 14:20
  • I don't think there will be any problems with KickAssets - it's just a new backend for managing files, but it doesn't change the internal structure. For 2.4 I'd go with Hamish's module (probably leave out KickAssets for getting started) and you won't need a custom robots.txt. – xeraa May 26 '13 at 23:15