Ask your WordPress questions! Pay money and get answers fast! (more info)

Protecting File Downloads in WordPress based on user role WordPress

  • SOLVED

Hello,

There are many download manager plugins for WordPress, but I want to find a more custom way to do below things:

1. Lets say I create a downloads folder and upload zip files like 1.zip, 2.zip
2. I then create a Download page on my WordPress site.

I want register users to download the files based on their access level. I also need to hide the actual download path and protect the folder using some .htaccess file. So now for e.g. if I want to give a download link I could give www.mywebsite.com/download/1 which would then check if the user belongs to a certain level and if not, show a warning.

Some things I read online about this are:
http://www.ardamis.com/2008/06/11/protecting-a-download-using-a-unique-url/
http://ramui.com/articles/hotlink-protected-php-download-script.html
http://stackoverflow.com/questions/3120174/php-protect-a-folder

Answers (1)

2013-01-18

Dbranes answers:

Hi, try this (add into functions.php) :

add_action('template_redirect','restrict_downloads');
function restrict_downloads() {

$dir="/your/path/to/download/dir/"; // EDIT
$access_level="level_7"; // EDIT
// check out the user levels here:
// http://codex.wordpress.org/Roles_and_Capabilities

$download_id=0;
if(isset($_GET['download'])){
$download_id=(int)$_GET['download']; // only allow numbers as input
}

$private_file=$dir.$download_id.".zip";
$public_file_name="file_".$download_id.".zip";

if ($download_id >0 && file_exists($private_file) && current_user_can($access_level)) {
header('content-type: application/octet-stream');
header("Content-Disposition: attachment; filename=".$public_file_name);
header("Content-Length: " . filesize($private_file));
header("Pragma: no-cache");
header("Expires: 0");
readfile($private_file);
exit();
}
}


The download-links will have the form

http://example.com/?download=123

where <em>download </em>will be integers > 0 and you can have the download directory outside the public html directory.

Hope this helps.


Harish Chouhan comments:

@Dbranes,

The number for file download was just an example. It would more likely be more detailed name for the zip files, like say "theme-simple.1.2.zip"

There needs to be sanitization to ensure, no one ensure no one adds any malicious code instead of the file name.

Lastly, would you also be able to tell me what the OR statement, if I want to allow a file to be able to downloadable by more than 2 access levels. Would I need this? or I assume everyone below level 7 would anyways be able to download the file right?


Dbranes comments:

ok, try this:

add_action('template_redirect','restrict_downloads');
function restrict_downloads() {

$dir="/your/path/to/download/dir/"; // EDIT
$access_level="publish_posts"; // EDIT (currently allows 'authors')
// check out the user levels here:
// http://codex.wordpress.org/Roles_and_Capabilities

$download_id="";
if(isset($_GET['download'])){
$download_id=sanitize_file_name($_GET['download']); // input
}

$private_file=$dir.$download_id.".zip";
$public_file_name="file_".$download_id.".zip";

if (strlen($download_id)>0){
if(current_user_can($access_level) && file_exists($private_file)) {
header('content-type: application/octet-stream');
header("Content-Disposition: attachment; filename=".$public_file_name);
header("Content-Length: " . filesize($private_file));
header("Pragma: no-cache");
header("Expires: 0");
readfile($private_file);
exit();
}else{
echo "<h1>Sorry, no access!</h1>";
exit();
}
}
}


Check out

[[LINK href="http://codex.wordpress.org/Roles_and_Capabilities"]]http://codex.wordpress.org/Roles_and_Capabilities[[/LINK]]

for more info about the access levels (Capabilities). They are "accumulative from above", so admins can access editor-levels, and admins and editors can access author-levels, and so on. The code snippet above uses 'publish_posts', so you have to be at least 'author' to access the download.


Harish Chouhan comments:

Can $dir="/your/path/to/download/dir/"; // EDIT be an absolute path?


Dbranes comments:

I have only tested this for an absolute path.


Harish Chouhan comments:

@Dbranes,

The first line worked.
$dir="/home/dreaain/public_html/download/"; // EDIT


but something like this did not work.
$dir="http://www.dreamsmedia.in/download/"; // EDIT

I tried outputting the file (public & private) to see if the path was right and it was. But the download did not start.

Secondly, would it be possible to you to suggest how to add .htaccess (I mean the code) so that direct download of the file is not possible.

http://www.dreamsmedia.in/?download=basic (example), & actual file path is www.dreamsmedia.in/download/basic.zip


Dbranes comments:

did you try

$dir="/home/dreaain/download/"; // EDIT

if that does not work, you can try adding a .htaccess file with:

deny from all

in to this directory

$dir="/home/dreaain/public_html/download/"; // EDIT


Harish Chouhan comments:

@Dbranes Thanks for all the help. Do not wish to pull this further as you have helped more than I offered to pay. I will select your answer now. Would appreciate if you could contact me through my website for future reference.


Dbranes comments:

ok no problem

good luck with your project

thanks

cheers


Harish Chouhan comments:

I have voted your answer, but not sure if you got the funds or if I need to do anything else. Please advise.


Harish Chouhan comments:

Hello,
Just another related question about JS, but i understand if its not possible to answer.

I wish to give the download link from say www.website.com/theme_01/

Now when the user clicks on the download link on the above example page, it either starts the download or if it fails, should show a javascript alert box. But the user should continue to stay on www.website.com/theme_01/. is this possible?