Reigel have solve a problem for me here : https://wpquestions.com/How_to_Protect_Uploads_if_User_is_not_Logged_In/27456
I have now a similar problem.
I use Woocommerce and "Groups for WooCommerce".
I put downloadable files in a folder in my FTP, via Filezilla.
But this folder should be accessible only to certain users (users with the role "my_customer") and only when they are connected.
For now, the folder is accessible to everyone by its URL. It's a problem.
Could someone solve this problem ?
Reigel Gallarde answers:
You can use the same approach.. just add something to dl-file.php to check for the user role...
<?php
require_once('wp-load.php');
is_user_logged_in() || auth_redirect();
$user = wp_get_current_user(); // get the current user
if ( !in_array( 'my_customer', (array) $user->roles ) ) { // check the user's role...
status_header( 404 );
die( '404 — File not found.' );
}
.... rest of the codes follows ...
Gabriel Reguly comments:
Cool solution :-)
Sébastien | French WordpressDesigner comments:
the folder is wp-content/documents.
Could you write the file dl file and the htaccess please.
Sébastien | French WordpressDesigner comments:
dl-file must be like this ?
<?php
require_once('wp-load.php');
is_user_logged_in() || auth_redirect();
$user = wp_get_current_user(); // get the current user
if ( !in_array( 'my_customer', (array) $user->roles ) ) { // check the user's role...
status_header( 404 );
die( '404 — File not found.' );
}
$mime = wp_check_filetype( $file );
if( false === $mime[ 'type' ] && function_exists( 'mime_content_type' ) )
$mime[ 'type' ] = mime_content_type( $file );
if( $mime[ 'type' ] )
$mimetype = $mime[ 'type' ];
else
$mimetype = 'image/' . substr( $file, strrpos( $file, '.' ) + 1 );
header( 'Content-Type: ' . $mimetype ); // always send this
if ( false === strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS' ) )
header( 'Content-Length: ' . filesize( $file ) );
$last_modified = gmdate( 'D, d M Y H:i:s', filemtime( $file ) );
$etag = '"' . md5( $last_modified ) . '"';
header( "Last-Modified: $last_modified GMT" );
header( 'ETag: ' . $etag );
header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + 100000000 ) . ' GMT' );
// Support for Conditional GET
$client_etag = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ? stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ) : false;
if( ! isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) )
$_SERVER['HTTP_IF_MODIFIED_SINCE'] = false;
$client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
// If string is empty, return 0. If not, attempt to parse into a timestamp
$client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0;
// Make a timestamp for our most recent modification...
$modified_timestamp = strtotime($last_modified);
if ( ( $client_last_modified && $client_etag )
? ( ( $client_modified_timestamp >= $modified_timestamp) && ( $client_etag == $etag ) )
: ( ( $client_modified_timestamp >= $modified_timestamp) || ( $client_etag == $etag ) )
) {
status_header( 304 );
exit;
}
// If we made it this far, just serve the file
readfile( $file );
Sébastien | French WordpressDesigner comments:
and the access in wp-content/documents must be like that ?
#pour empecher l'acces à ce dossier pour les extensions doc|docx|pdf|txt|csv pour les users non-connectés
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s
RewriteRule ([_0-9a-zA-Z-]+/)([_0-9a-zA-Z-]+/)((.*)\.(doc|docx|pdf|txt|csv))$ ../../dl-file.php?file=$3 [QSA,L]
</IfModule>
that's it, Reigel ?
Reigel Gallarde comments:
yes... but not quite right...
You have to insert the codes for checking user's role.. don't remove anything from the dl-file.php...
I can see you have removed these lines of codes...
$file = rtrim( $basedir, '/' ) . '/' . str_replace( '..', '', isset( $_GET[ 'file' ] ) ? $_GET[ 'file' ] : '' );
if ( ! $basedir || ! is_file( $file ) ) {
status_header( 404 );
die( '404 — File not found.' . $file );
}
Sébastien | French WordpressDesigner comments:
ah ok !
the folder I need to protect is the folder "private", here : /public_html/sites/default/files/private
and dl-files is here /public_html/dl-files.php
what must I change in dl-file.php and in the .htaccess, please ?
Reigel Gallarde comments:
where is your private folder located in relation to wordpress folder?
I need to know because dl-files.php needs to locate wp-load.php which is inside the root folder of wordpress...
Sébastien | French WordpressDesigner comments:
/public_html/dl-files.php
/public_html/wp-load.php
/public_html/sites/default/files/private
you see ?
Reigel Gallarde comments:
can you give me an example url of the file located here?
/public_html/sites/default/files/private
something like this?
http://sites.com/default/files/private/file.txt
Reigel Gallarde comments:
is this multisite?
Reigel Gallarde comments:
have you tried this already?
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s
RewriteRule ([_0-9a-zA-Z-]+/)([_0-9a-zA-Z-]+/)((.*)\.(doc|docx|pdf|txt|csv))$ ../../dl-file.php?file=$3 [QSA,L]
</IfModule>
I think this should work already... just put this inside the private folder...
Sébastien | French WordpressDesigner comments:
it's not a musltisite.
Sébastien | French WordpressDesigner comments:
no, url is not something like this :
http://sites.com/default/files/private/file.txt
it's something like this :
http://sites.com/sites/default/files/private/file.txt
Mohamed Ahmed comments:
With a simple and security steps,
Check if user role is 'my_customer' -> Set a cookie with value
1-Add this to theme functions.php file
/**
* Check user role
**/
function get_my_customer_role( $user = null ) {
if ( !is_user_logged_in() ) return;
$user = $user ? new WP_User( $user ) : wp_get_current_user();
return $user->roles ? $user->roles[0] : false;
}
/**
* Set a cookie with user role ' my_customer'
**/
add_action('init', function() {
if (!isset($_COOKIE['my_customer_role']) && (get_my_customer_role() != 'my_customer') ) {
setcookie('my_customer_role', get_my_customer_role(), time() + 3600, COOKIEPATH, COOKIE_DOMAIN );
}
if (isset($_COOKIE['my_customer_role']) && (!is_user_logged_in())) {
setcookie('my_customer_role', '', time() + 3600, COOKIEPATH, COOKIE_DOMAIN );
}
});
2-Add this code to .htaccess
# BEGIN WordPress
RewriteEngine On
RewriteBase /
# Your Private Folder Path
RewriteCond %{REQUEST_URI} /wp-content/private
# Check Our Cookie
RewriteCond %{HTTP_COOKIE} !^.*my_customer_role.*$ [NC]
# Not the target user so redirect it to any page
RewriteRule .* http://mydomain.com [L]
This code already tried by us, It will meet your needs.
Sébastien | French WordpressDesigner comments:
huuuuuummmm... EarnGate, that doesn't work
Sébastien | French WordpressDesigner comments:
No sorry, that works... the path in your .htaccess was not good
Sébastien | French WordpressDesigner comments:
huuummmm.... EarnGate : For my test, I have just replace
if (!isset($_COOKIE['my_customer_role']) && (get_my_customer_role() != 'my_customer') ) {
by
if (!isset($_COOKIE['my_customer_role']) && (get_my_customer_role() != 'administrator') ) {
If I am not logged in, when I go to http://site.com/sites/default/files/prive/123/file.txt, I redirect to http://site.com
But when I am logged in as administrator, that is the same thing.
Sébastien | French WordpressDesigner comments:
Reigel, I have transform your htaccess into
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s
RewriteRule ([_0-9a-zA-Z-]+/)([_0-9a-zA-Z-]+/)((.*)\.(doc|docx|pdf|jpg))$ ../../../../dl-file.php?file=$3 [QSA,L]
</IfModule>
this file is in /www/sites/default/files/prive
the file dl-file.php is like that :
<?php
require_once('wp-load.php');
is_user_logged_in() || auth_redirect();
$user = wp_get_current_user(); // get the current user
if ( !in_array( 'administrator', (array) $user->roles ) ) { // check the user's role...
status_header( 404 );
die( '404 — File not found.' );
} else {status_header( 403 );die( '403' );}
is_user_logged_in() || auth_redirect();
$upload_dir = wp_upload_dir();
$basedir = $upload_dir[ 'basedir' ] . '/';
$file = rtrim( $basedir, '/' ) . '/' . str_replace( '..', '', isset( $_GET[ 'file' ] ) ? $_GET[ 'file' ] : '' );
if ( ! $basedir || ! is_file( $file ) ) {
status_header( 404 );
die( '404 from dl-file.php — File not found.' . $file );
}
$mime = wp_check_filetype( $file );
if( false === $mime[ 'type' ] && function_exists( 'mime_content_type' ) )
$mime[ 'type' ] = mime_content_type( $file );
if( $mime[ 'type' ] )
$mimetype = $mime[ 'type' ];
else
$mimetype = 'image/' . substr( $file, strrpos( $file, '.' ) + 1 );
header( 'Content-Type: ' . $mimetype ); // always send this
if ( false === strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS' ) )
header( 'Content-Length: ' . filesize( $file ) );
$last_modified = gmdate( 'D, d M Y H:i:s', filemtime( $file ) );
$etag = '"' . md5( $last_modified ) . '"';
header( "Last-Modified: $last_modified GMT" );
header( 'ETag: ' . $etag );
header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + 100000000 ) . ' GMT' );
// Support for Conditional GET
$client_etag = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ? stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ) : false;
if( ! isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) )
$_SERVER['HTTP_IF_MODIFIED_SINCE'] = false;
$client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
// If string is empty, return 0. If not, attempt to parse into a timestamp
$client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0;
// Make a timestamp for our most recent modification...
$modified_timestamp = strtotime($last_modified);
if ( ( $client_last_modified && $client_etag )
? ( ( $client_modified_timestamp >= $modified_timestamp) && ( $client_etag == $etag ) )
: ( ( $client_modified_timestamp >= $modified_timestamp) || ( $client_etag == $etag ) )
) {
status_header( 304 );
exit;
}
// If we made it this far, just serve the file
readfile( $file );
but I can access to the URL http://mysite.com/sites/default/files/prive/123/file.txt when I am logged in and when I am not.
Mohamed Ahmed comments:
Sébastien
1- We Have added the path in .htaccess for an example if we will using private folder and you can add your custom path
2- We have used (get_my_customer_role() != 'my_customer')
Not
(get_my_customer_role() != 'administrator')
because you have to ask
accessible only to certain users (users with the role "my_customer")
and it's working like a charm
Sébastien | French WordpressDesigner comments:
my_customer or administrator, that's the same thing.
Administrator is in $user->roles like administrator, isn't it ?
and i have replace the path, but that doesn't work
Sébastien | French WordpressDesigner comments:
Iknow why your code doesn't work.
There is a mistake in your code.
You write ) && (get_my_customer_role() != 'my_customer') ) {
but the code must be :
) && (get_my_customer_role() == 'my_customer') ) {
if the user have the role "my_customer" and the cookie doesn't exist, I create the cookie.
With your code you create a cookie for everyone is connected without the role "my_customer".
Then there is no cookie if you have the role "my_customer" and you can't acces to http://site.com/sites/default/files/prive/123/file.txt
Mohamed Ahmed comments:
That's our effective idea.
if have a role set cookie will access to download from a folder else you can't access.
Sébastien | French WordpressDesigner comments:
hummm... but that doesn't work on my site...
Is my htaccess ok, please ?
# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_URI} /sites/default/files/prive
RewriteCond %{HTTP_COOKIE} !^.*my_customer_role.*$ [NC]
RewriteRule .* http://sebastien.com [L]
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>
# END WordPress
Mohamed Ahmed comments:
Hello,
We have tried it and it works fine.
Can you send to us the URL and demo login info that you have tried
Sébastien | French WordpressDesigner comments:
Do you have an email adress ?
Mohamed Ahmed comments:
meno010 #yahoo
Sébastien | French WordpressDesigner comments:
So, conclusion after this discussion by email: I was right.
You wrote
) && (get_my_customer_role() != 'my_customer') ) {
but the code must be :
) && (get_my_customer_role() == 'my_customer') ) {
Thanks :)
Reigel... could you finish your solution ? I like this way of solving the problem.
Mohamed Ahmed comments:
We have finished our solution.
Mohamed Ahmed answers:
With a simple and security steps,
Check if user role is 'my_customer' -> Set a cookie with value
1-Add this to theme functions.php file
/**
* Check user role
**/
function get_my_customer_role( $user = null ) {
if ( !is_user_logged_in() ) return;
$user = $user ? new WP_User( $user ) : wp_get_current_user();
return $user->roles ? $user->roles[0] : false;
}
/**
* Set a cookie with user role ' my_customer'
**/
add_action('init', function() {
if (!isset($_COOKIE['my_customer_role']) && (get_my_customer_role() == 'my_customer') ) {
setcookie('my_customer_role', get_my_customer_role(), time() + 3600, COOKIEPATH, COOKIE_DOMAIN );
}
if (isset($_COOKIE['my_customer_role']) && (!is_user_logged_in())) {
setcookie('my_customer_role', '', time() + 3600, COOKIEPATH, COOKIE_DOMAIN );
}
});
2-Add this code to .htaccess
# BEGIN WordPress
RewriteEngine On
RewriteBase /
# Your Private Folder Path
RewriteCond %{REQUEST_URI} /wp-content/private
# Check Our Cookie
RewriteCond %{HTTP_COOKIE} !^.*my_customer_role.*$ [NC]
# Not the target user so redirect it to any page
RewriteRule .* http://mydomain.com [L]
This code already tried by us, It will meet your needs.
Mohamed Ahmed comments:
If you need server-side solution
You can tell your customers to download with this URL
http://Yoursite.com/?download
1- Add these to functions.php
[code]
/**
* Check user role
**/
function get_my_customer_role( $user = null ) {
if ( !is_user_logged_in() ) return;
$user = $user ? new WP_User( $user ) : wp_get_current_user();
return $user->roles ? $user->roles[0] : false;
}
/**
* Limit access for logged-in users with 'my_customer' role.
**/
add_action('template_redirect','restrict_downloads');
function restrict_downloads() {
$dir= home_url()."/wp-content/documents"; // Edit Downloads directory
$access_level = get_my_customer_role();
if(isset($_GET['download'])){
if(($access_level == 'my_customer') && is_user_logged_in() ) {
// Redirect to Downloads folder
header ("Location: $dir");
exit();
}else{
// Redirect to home page
header ("Location: ".home_url());
//echo "<h1>Sorry, no access!</h1>";
exit();
}
}
}
[/code]
sorry for any comments make inconvenience caused.
Best wishes
Sébastien | French WordpressDesigner comments:
It's a strange solution... you want to add ?download in the url ?
And if someone use the url without ?download, he can access to the file. This function don't protect the file...
Cesar Contreras answers:
Do you still need help?
Sébastien | French WordpressDesigner comments:
yes I'm waiting the end of the solution of Reigel
Mohamed Ahmed comments:
But our solution is working fine and we have tried it on your website by ourself with demo user with role 'my_customer' and can login to download.
Other editor account redirected and can't download.
This is you have requested.
Sébastien | French WordpressDesigner comments:
Hey EarnGate, could you create your self thread like everyone, please.
I prefer a solution like Reigel, a server-side solution rather than a client-side solution (cookie). Securing an access by a cookie seems a bit light as security. No ?
Cesar Contreras comments:
some time ago use this article to solve a similar problem, such as I could help you:
http://top-frog.com/2010/07/01/a-simple-way-to-limit-file-downloads-to-only-logged-in-users-in-wordpress/