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

files in a FTP folder downloadable for connected users only WordPress

  • SOLVED

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 ?

Answers (3)

2017-09-27

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 &#8212; 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 &#8212; 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 &#8212; 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


EarnGate 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 &#8212; 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 &#8212; 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.


EarnGate 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


EarnGate 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


EarnGate 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 ?


EarnGate 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.


EarnGate comments:

We have finished our solution.

2017-09-30

EarnGate 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.


EarnGate 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...

2017-10-02

Cesar Contreras answers:

Do you still need help?


Sébastien | French WordpressDesigner comments:

yes I'm waiting the end of the solution of Reigel


EarnGate 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/