Hello,
Sometimes you need to have nodes that you want to be readable by everyone and show attachment's existance but the attachments must be available for downloading to pre-selected roles or users only. The attachments must be protected in such a case even from direct request with the URL from a user not having the access privileges.
Drupal has a public and a private way to get and download files but only through private way we can insert restrictions. Multiple solutions can exist to the above situations:
a) An idea is to have a subdirectory with a ".access" file that has roles and/or user ids that are allowed to access files in this directory. This will allow easy management of files with a "filemanager" module.
b) An other idea is to have also a "filename".access file for each file available for downloading with an access restriction based on roles/users. If this files does not exist, it is available to every body.

I am a newbie to Drupal, but any ideas about changes that are needed to implemented are welcome.

Comments

dcoun’s picture

Any help, which file/module handles the file downloading in private method ?

dcoun’s picture

Assigned: Unassigned » dcoun

I have done some progress. I am not a programmer in real life, so any contribution is needed.

Lets first describe the idea:
We have nodes that are viewable from selected roles or everyone, but we want their file attachments to be visible but only selected roles can download them.

Method:
We use private method so all files that are going to be downloaded must be "validated" before with hook_file_download($file). A database table can be used to keep permissions for each file. A module can do all this job.

Implementation
Lets name our module fileaccesscontrol. We create a database table:

function fileaccesscontrol_install() {         $created=false;
  switch ($GLOBALS['db_type']) {
    case 'mysql':
    case 'mysqli':
      $query1=db_query("CREATE TABLE {file_access_control} (
          file varchar( 255 ) NOT NULL default '',     //the file or directory name
          role varchar( 255 ) NOT NULL default '',   //a comma delimited sting with allowed rid of roles
          user mediumtext NOT NULL default '',      //a comma delimited string with allowed user ids
          isfile tinyint(1) NOT NULL default '0',       //index:the file field contains a file or a directory?
          PRIMARY KEY (file(20)), KEY iisfile (isfile)
        ) /*!40100 DEFAULT CHARACTER SET utf8 */;");
      if ($query1) $created=true;  break;

    case 'pgsql':
      $query1=db_query("CREATE TABLE {file_access_control} (
          file varchar( 255 ) NOT NULL default '',
          role varchar( 255 ) NOT NULL default '',
          user mediumtext NOT NULL default '',
          isfile tinyint(1) NOT NULL default '0',
          PRIMARY KEY (file(20))
        )");
      $query2=db_query("CREATE INDEX {file_access_control}_idx ON {fileaccesscontrol} (file) (isfile)");
      if ($query1 && $query2) $created=true; break;
  }
if ($created) { drupal_set_message(t('File Access Control database table installed')); }                  
    else { drupal_set_message(t('Table install for File Access Control was unsuccess$ }             
}

The above is a database table having in mind different ways of setting permissions, not yet implemented.
The following can exist in our module:

function fileaccesscontrol_file_download($file) {  global $user; 
  if (user_access('access content')) { $u=$user;
    $rol=(is_array($user->roles) ? array_keys($user->roles) : array('1'));
    $fileok = db_fetch_array(db_query_range("SELECT file,role,user FROM {file_access_control} WHERE isfile=1 AND file = '%s'", $file,0,1)); 
    if (is_array($fileok)) {  
       $frol=explode(',',$fileok['role']); 
       foreach ($rol as $key) { if (in_array($key,$frol)) {return array('');} }
       $frol=explode(',',$fileok['user']); $rol=$user->uid;
       if (in_array($rol,$frol)) {return array('');}
       return -1;
    } else { return array('');} //no entry in db means free access
  }  else {return -1; } //no access to content
}

The above code gives by default access to the file when no entry exists in the database for the requested file. We need permissions which roles can do this job. So, we add the following:

function fileaccesscontrol_perm() {
return array('Set roles file access on upload'); } 

The problem is now how to fill the database table with the files to whom access is granted only for selected roles.
An idea is to use hook_form_alter and include a checkbox with allowed roles. Can the following code do the job?

function fileaccesscontrol_form_alter($form_id, &$form) { global $user;
  if (isset($form['#id']) && $form['#id'] == 'node-form') {
      if ($user->uid == 1 || user_access('Set roles file access on upload')) {
 $res=db_query("SELECT rid,name FROM {role}"); $rol=array(); 
 while ($r=db_fetch_object($res)) $rol+=array($r->rid=>$r->name); 
 $form['attachments']['facroles'] = array(
  '#type' => 'checkboxes',
  '#title' => t('Roles with access to this file'),
  '#options' => $rol,
  '#description' => t('File will be accessible to all if no role is selected. User that belongs to a selected role has access to the file.'),);
   } 
  }
}

The problem is that the above should exist for each attachment in the node's attachments section. How can we have it for each attachment?

dcoun’s picture

Status: Active » Closed (fixed)