After once again having a web site that is 99% public and 1% private, I decided to have another look at finding a true workaround to this issue in Drupal 6. After a day of hacking, I came up with the following.
There are 5 main areas to look at:
1) Settings
A fairly trivial one, hook into the system modules file settings form and append an element for the private directory.
2) Hook into the FileField instance settings
Taking inspiration from the Insert module, I discovered the field settings alter hook. I use this to appends a checkbox field using the form / save operations.
3) Saving the file
Using hook_nodeapi(), insert / update hooks, all content types are checked for filefields. If one is found, and has the flag from step 2 set, the file is moved into the private files directory.
This was accomplished using core Drupal file functions and tricking Drupal into believing that the new private directory was the real file directory. This was done via:
Saving the current settings from the $config global
Updating these using the new directory settings
Moving the file and updating the database {files} table
Restoring the system settings
Notes
- The normal variable_get / variable_set functions would trigger a db save, so any concurrent HTTP requests would get the wrong file path settings. Using $config directly prevents this.
- Restores the settings, as other file calls are very likely, even if you use exit() due to shut-down functions like poor-mans cron.
4) Theming the items
As the new path no longer resides inside the system file directories, all formatters will fail to format the new link. Using hook_nodeapi() again, all node file fields are parsed again to check for the private flag. If found, the file items theming function is changed. The existing theming function is stored for latter use.
In the new custom theme function, the same $config trick is used and the file download method is set to private. Then the original theming function is called to render the element.
The standard private file download will not work as this checks the file directory against the real file directory, (see step 5), so all href and src attributes are parsed to replace the system download link with a custom download link. This URL is based on the node nid and fid for easy security checks during the download.
5) Download
The custom download does a user check on view node permission, and then tries to find the file in the nodes content fields. If found, it runs through the standard private file access checks. A second hook is called to allow modules to interact directly with this method of download, hook_file_private_download(), and has the same API as hook_file_download().
The file_transfer() function required the same $config trick to work.
Issues
Forcing CCK 2.5 or higher requirement - for hook_field_settings_alter() API [check]
Weight not being set right, so the new element is below the submit buttons on the file system settings page.
Bulk updates? Public to private and back again.
Use better logic to compare file directory paths (nested paths could cause issues)
Todo
Code clean up
Implement a more refined permission system
Will normal 'file_download' checks fail or return false results? CVS search may be required. Move the $config change may be required.
See what happens with images [Remove the force download on image files / tag src URLs maybe]
Love to hear feedback from anyone about this. It is still at very early testing stages, but if it passes the tests, I will consider publishing it, if Drupal 7 doesn't come out first!
The complete module is attached below. It will get some good stress test in the next week or two.
Comments
----------------------------
---------------------------- filefield_private.info -----------------------------
-------------------------- filefield_private.module ----------------------------
Alan Davison
Interesting -- will give it a
Interesting -- will give it a whirl soon.
You should create a project for this!
I was hoping to get the time
I was hoping to get the time to develop and test this more over the last few weeks, but a higher priority project has come up at work and it has been absorbing all of my time. Hoping to get back to this in a week or two.
For the meantime I've committed the code here:
http://drupalcode.org/viewvc/drupal/contributions/modules/filefield_priv...
Alan Davison
_
just an fyi (i use the method described at http://drupal.org/node/189239 for d6) -- this is now built in to core for d7 (filefield replaces upload).
Trying to be fully independant of the web server
That is how we have done it in the past. We have some clients that do not have .htaccess permissions and other clients on window boxes, it is all so painful! I think that is how private_downloads sort of works too. (I have not used that module)
The last company that I worked on had a stressed shared server, and it had many attacks, mainly ssl dos attacks against the shops / betting sites. For whatever reason, apache occasionally lost its access control and started listings directories. (We had to script regular restarts to minimise the risks)
So I have always used the .htaccess method on sites where it would not be the end of the world if this happened, but used true Drupal private downloads if the info was really commercially sensitive and just accepted the server overhead.
This also has the benefit of controlling FTP access if required :)
Alan Davison
Thanks for posting this very useful information
@alanD. Thanks for posting this very useful information.
I do not have the coding experience and even after two years of using Drupal feel I am just a novice, learning more every day.
I have some existing sites that have been using the public method and I would like to move them to private download. I have been trying to understand the best way to do this and tried reading everything I can find on this. Some of the stuff I've found is a bit complicated.
I would like to map out the simplest way to make the change public to private and then extend it as needed but ensure the first steps are the best to avoid problems later.
I don't want to hijack this thread and should maybe just listen in a bit.
Some of the things I have come across that look interesting are:
make private download method support css/js aggregation, color module and js translations
and the x-send file module http://drupal.org/project/xsend
and I have been trying to understand the direction D7 is going in, through the "recipe for mixed private/public"
I haven't done a lot with filefield_paths module and maybe need to get a better handle on that.
But, I'm really hoping to find a method that isn't complicated for the users of the site and performs well for both anonymous and authenticated users while securing the files that only authenticated users need access to.
Thanks very much for your post. I will follow with interest.
Izzy
We've released it as an
We've released it as an "alpha" module here http://drupal.org/projects/filefield_private, but it is an alpha!! Use with care. But there are a few links to the .htaccess pages that you may find useful
Switching to private from public requires a lot of work, this includes the db etc.
Alan Davison
Thanks @alan , PS : the link
Thanks @alan ,
PS : the link does not exists.
sina.salek.ws, Software Manager & Lead developer
Feel freedom with open source softwares
Try
Try http://drupal.org/project/filefield_private
Alan Davison
Thanks,
Thanks,
sina.salek.ws, Software Manager & Lead developer
Feel freedom with open source softwares
this is nice. i am thinking
this is nice. i am thinking about making a new cck filefield that uses the path settings from the settingspage (add a private text input field like d7). The only problem i have is the same as you hat but solved with manipulationg global $conf.
Simple solution
Hi Alan,
Just stumbled upon this post when trying to mix files with FileField. In D6 I tried creating a different architecture (using private_upload and uploadpath) in which a private subdirectory (with .htaccess written into it) was created when files had to be private. And the URL was rewritten (using the 'system' addition) when it pointed to a private file.
This way the file system could still be set to public and you would have all the attachments kept together, whether they are public or not. Further advantages: force files private when the corresponding OG is private, do not display private files entries in nodes when you do not have permission for the files themselves.
If you are interested, see http://drupal.org/node/197590#comment-1176337.
Regards, Ed
Best regards,
Ed