Hello,

I am confused about how to use IMCE with the private file system. What is the best way to use IMCE with the private file system?

I cannot upload files into nodes with the IMCE browser unless I uncheck the box "Disable serving of private files" in the 'Common Settings' of IMCE's configuration page. And if this setting is unchecked, can anyone with the URL access those files? This somewhat defeats the purpose of the private file system, doesn't it?!?

Are there modules I could use to restrict access to those files? Or do I not understand this correctly?

Thanks for your help for a newbie. :)
Andre

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

ufku’s picture

IMCE does not apply any restriction while serving private files. You need a custom module for that. I don't know of any.

rconstantine’s picture

This only applies to when you have multiple untrustworthy users using IMCE, while they use IMCE, right? I mean, when the node displays, the image still gets served by Drupal via the private route if I understand this correctly. And if you trust all of your IMCE users, then who cares how the file gets served to the file browser. Am I wrong?

ufku’s picture

@rconstantine, it's about how you want to restrict access to the site files. If you don't want any restrictions, there is no reason to use the private file system. Public files are served by apache which is way faster than serving private files by PHP.

OTH, if you want some kind of restriction, you will need a custom module that prevents serving of files by applying a set of predefined rules.

Miao1994’s picture

Hi,
I'm not sure if this is the right thread, but I have a matter using IMCE and private file system. Module seems work well, nevertheless when Users click on a file to download the browser doesn't show the correct filename in the downloading windows. For instance, Firefox 7.01 shows a random mix of letters (containing file extension); IE9 shows IP or name server. This behaviour doesn't appear using public file system: filename showed is correct.
Thanks in advance.
Claudio, Italy.

Miao1994’s picture

Hi,
I just solved the issue described above enabling rewrite rules and clean-urls. I'm not skilled enough to understand the reason; I hope this may help someone.

yoclaudio’s picture

If you don't find any module #1 is saying, this worked fine for me:

Edit imce.module file and change:

function imce_file_download($uri) {
  $serve = file_uri_scheme($uri) == 'private' && !variable_get('imce_settings_disable_private', 1) &&  file_exists($uri) && strpos(basename($uri), '.');
  if ($serve) {
    return array(
      'Content-type' => file_get_mimetype($uri),
      'Content-Length' => filesize($uri),
    );
  }
}

to this that will limit private file access to authenticated users (role ID = 2):

function imce_file_download($uri) {
  $serve = file_uri_scheme($uri) == 'private' && !variable_get('imce_settings_disable_private', 1) &&  file_exists($uri) && strpos(basename($uri), '.');
  if ($serve) {
  
	global $user;
   
	if (array_key_exists('2', $user->roles)) {
  
		return array(
		  'Content-type' => file_get_mimetype($uri),
		  'Content-Length' => filesize($uri),
		);
		
	}
  }
}

In #266549: directory protection from leeching there is a more interesting patch, but for IMCE in Drupal 6.

peterx’s picture

Would it be possible for IMCE to call something to check? We could then put hooks into nopremium and similar modules for IMCE.

hook_imce_can_i_display_this_image($user, $page, $image);

The add other modules can then decide based on the user or page or image.

ufku’s picture

@petrex, you don't need IMCE to define a hook for file access. You can use hook_file_download().

MickC’s picture

#6 worked to stop anonymous access, but not to control specific user access - one user could see another users files.

The test was to:
1 - log in as user A, double click a file - file opens in new tab showing URL - copy URL
2 - logout - get access denied when trying to access the URL - no anonymous access
3- log in as user B - access to the url is granted

@yoclaudio, thanks for this workaround - a good start, but I wonder if it's possible to improve the workaround for individual user access?
It may not be a massive problem, as it's unlikely user B will know specific URL's for user A's files.
However we know from this test it is not as secure as we'd like.

Any thoughts?

romainj’s picture

I changed some of the code of #6 to restrict access to anyone except the owner of the private file.

ORIGINAL CODE

function imce_file_download($uri) {
  $serve = file_uri_scheme($uri) == 'private' && !variable_get('imce_settings_disable_private', 1) &&  file_exists($uri) && strpos(basename($uri), '.');
  if ($serve) {
    return array(
      'Content-type' => file_get_mimetype($uri),
      'Content-Length' => filesize($uri),
    );
  }
}

MODIFIED CODE

function imce_file_download($uri) {
  $serve = file_uri_scheme($uri) == 'private' && !variable_get('imce_settings_disable_private', 1) &&  file_exists($uri) && strpos(basename($uri), '.');
  if ($serve) {
	  global $user;
	  $file_uid = db_query("SELECT uid FROM {file_managed} WHERE uri=:uri",
	                        array(':uri' => $uri))->fetchField();
	  if ($user->uid == $file_uid) {
		  return array(
		    'Content-type' => file_get_mimetype($uri),
		    'Content-Length' => filesize($uri),
		  );
	  }
	  else return -1;
  }
}

Remember that we shouldn't modify modules' code!

Hope this will help.

borys’s picture

Hi,
as romainj said modifying of modules' code is not good idea, so you can add this hook in your own module.

I needed to add some restrictions to IMCE (version 6 of module, but it should be the same for 7) files, and this is my solution:

  1. Uncheck "Disable serving of private files" in Common Settings of IMCE module.
  2. Add hook_file_download() hook into your module.
<?php
/**
 * Implementation of hook_file_download().
 */
function my_module_file_download($filepath) {
    $filepath = file_create_path($filepath);
    // Check permissions only for IMCE files
    $result = db_query("SELECT f.* FROM {files} f INNER JOIN {imce_files} i ON f.fid = i.fid WHERE filepath = '%s'", $filepath);
    global $user;
    while ($file = db_fetch_object($result)) {
      if ($filepath !== $file->filepath) {
        // Since some database servers sometimes use a case-insensitive
        // comparison by default, double check that the filename is an exact
        // match.
        continue;
      }
      
      // Here you can add your own rules for accessing files.
      // For example: allow access only to files that user had uploaded
      if ($file->uid == $user->uid) 
      {
        return array(
          'Content-Type: ' . $file->filemime,
          'Content-Length: ' . $file->filesize,
        );
      }
      else {
        return -1;
      }
    }
}
?>

p.s: Inspired by drupal core Upload module.

valentin schmid’s picture

Issue summary: View changes

Hi,
here's the drupal7 version of borys hook in #11

/**
 * Implements hook_file_download().
 * Support private downloads for imce files.
 * 
 * DO NOT use imce_file_download by enabling imce_settings_disable_private!
 * It will enable private download for all private files!
 */
function my_module_file_download($uri) {
  // Check permissions only for private IMCE files
  if (file_uri_scheme($uri) == 'private') {
    $result = db_query("SELECT fm.* FROM {file_managed} fm INNER JOIN {file_usage} fu ON fm.fid = fu.fid AND fu.module='imce' WHERE fm.uri = :uri", array(':uri' => $uri));
    foreach ($result as $file) {
      if ($uri !== $file->uri) {
        // Since some database servers sometimes use a case-insensitive
        // comparison by default, double check that the filename is an exact
        // match.
        continue;
      }
      // Here you can add your own rules for accessing files.
      // For example: allow access only to files that user had uploaded
      if ($file->uid == $user->uid) {
        return(file_get_content_headers($file));
      }
      else {
        return -1;
      }
    }
  }
}
vistree’s picture

Hi Valentin,
your code works fine for Drupal 7. But there is a small bug: you have to define $user before the line using it.
So insert

     global $user;

before

      if ($file->uid == $user->uid) {

If you only want to secure files from being accessed by anonymouse user use

     if (user_is_logged_in()) {

instead of

      if ($file->uid == $user->uid) {
MatthijsB’s picture

Valentin's code in #12, combined with the first fix in #13, works for me too (Drupal 7.34, IMCE 7.x-1.9, IMCE for FileField 7.x-1.0).

gaborpeter’s picture

In our project we need to use iMCE with private file system where also other files not added by IMCE represent.
So we did the following changes in the module:

file_download hook
first checks if the current user has access to iMCE, if so it matches the file path with the path given in the IMCE profile directory list, thus not giving access to files outside to the directory
This checks makes available to see all files / thumbnails in this upload dir for users who are logged in, while editing nodes etc.

The 2nd part of the function is for users who don't have access to an IMCE profile, let's say anonymous users. In this case, the file_usage table is used.

node operation hooks (insert, update, delete)
Managing the file_usage table for files attached as images to textareas in nodes. Thus if you add an image with IMCE to a node, only those users will see the image who have access to the node as well. In our case this is very handy as we have per node based grant settings.

We have also removed the checkbox for turning on and off global access to private files, as actually these changes should be a replacement solution for that.

In the future this solution should be upgraded to any entity not only to nodes.

Comments and feedbacks highly appreciated!

Drupa1ish’s picture

Component: User interface » Code
Status: Active » Needs review
Peter Arius’s picture

Status: Needs review » Needs work

Patch #15 looks very promising, but is there an update?

Trying with
IMCE 7.x-1.9
IMCE for FileField 7.x-1.1
Drupal core 7.41
PHP 5.5

Files uploaded via File Field (core upload, not via IMCE yet) to the private file system are not served at all (white screen).

There are lots of PHP messages, like:

Notice: Undefined variable: file in imce_file_download() (line 149 of .../sites/all/modules/imce/imce.module).
Notice: Trying to get property of non-object in file_get_content_headers() (line 2554 of .../includes/file.inc).

Notice: Undefined index: node in imce_create_node_file_usage() (line 744 of .../sites/all/modules/imce/inc/imce.admin.inc).
Warning: Invalid argument supplied for foreach() in imce_create_node_file_usage() (line 744 of .../sites/all/modules/imce/inc/imce.admin.inc).

MmMnRr’s picture

Hi everyone,

I tried with solution from valentin schmid (comment #12) and it worked perfectly (with changes from comment #13).
So the point is to develop a custom module that implements "hook_file_download(...)" and define some roles and IMCE profiles.
Then, you can use the IMCE media browser by visiting either of the following URL's:
http://yourbase.url.com/imce/public
http://yourbase.url.com/imce/private

Private files downloads will be controlled by the custom module.

Peter Arius’s picture

There is a companion module IMCE private files which seems to do just the right thing. Haven't finished testing it yet, though.

thalles’s picture

This looks solved!

thalles’s picture

Status: Needs work » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.