How do you Require Login for Media Files in Django - django

I'm serving "sensitive" information in downloadable PDF's and Spreadsheets within a user registration section of a site.
Is there a way to allow the django authentication to secure this media without serving it (and not have to manually login using basic auth)?
I'm guessing theres (fingers crossed) not a way to do it with the psuedo code below, but it helps better illustrate the end goal.
#urls.py
(r'^protected_media/(?P<filename>.*)$', 'protected_media')
#views.py
from django.contrib.auth.decorators import login_required
#login_required
def protected_media(request, filename):
# #login_required bounces you out to the login url
# if logged in, serve "filename" from Apache

It seems to me that the method you outlined in your code should work. It's really no different than any other protected resource: your views can serve files from disks, records from databases, rendered templates or anything. Just as the login_required decorator prevents unauthorized access to other views, it will prevent such access to your view serving protected media.
Am I missing something from your question here? Please clarify if that's the case.
EDIT: With regard to the django doc link in your comment: that's the method for simply serving any request file from a particular directory. So, in that example URLS like /site_media/foo.jpg, /site_media/somefolder/bar.jpg will automatically look for files foo.jpg and somefolder/bar.jpg under document_root. Basically, every thing under document_root will be publicly available. That's obviously insecure. So you avoid that with your method.
It's also considered inefficient because django is just adding a lot of unnecessary overhead when all you need is something like Apache to take a URL request and map it to a file on the hard drive. (You don't need django sessions, request processing, etc.)
In your case, this may not be such a big concern. First, you've secured the view. Second, it depends on your usage patterns. How many requests do you anticipate for these files? You're only using django for authentication -- does that justify other overhead? If not, you can look into serving those files with Apache and using an authentication provider. For more on this, see the mod_wsgi documentation:
http://code.google.com/p/modwsgi/wiki/AccessControlMechanisms
see the section "Apache Authentication Provider" and search for django
There are similar mechanisms available under mod_python I believe. (Update: just noticed the other answer. Please see Andre's answer for the mod_python method.)
EDIT 2: With regard to the code for serving a file, please see this snippet:
http://www.djangosnippets.org/snippets/365/
The send_file method uses a FileWrapper which is good for sending large static files back (it doesn't read the entire file into memory). You would need to change the content_type depending on the type of file you're sending (pdf, jpg, etc).

Read this Django ticket for more info. Start at the bottom to save yourself some time. Looks like it just missed getting into Django 1.2, and I assume also isn't in 1.3.
For Nginx, I found this Django snippet that takes advantage of the X-Accel-Redirect header, but haven't tried it yet.

If I understand your question correctly you want to restrict access to files that are not being served by Django, for example, with an Apache server?
What you would then require is some way for this Apache server to use Django as an authentication source.
This django snippet describes such a method. It creates an access handler in Django which is used by Apache when a request for a static file comes in that needs to be protected:
<Location "/protected/location">
PythonPath "['/path/to/proj/'] + sys.path"
PythonOption DJANGO_SETTINGS_MODULE myproj.settings
PythonOption DjangoPermissionName '<permission.codename>'
PythonAccessHandler my_proj.modpython #this should point to accesshandler
SetHandler None
</Location>
Hope this helps, the snippet was posted a while ago, so things might have changed between Django versions :)

More efficient serving of static files through Django is being looked at currently as part of Google SOC project. For WSGI this will use wsgi.file_wrapper extensions for WSGI if available, as it is for mod_wsgi, and req.sendfile() if using mod_python. It will also support returning of headers such as 'Location', 'X-Accel-Redirect' and others, which different web hosting mechanisms and proxy front ends accept as a means of serving up static files where location is defined by a backend web application, which isn't as effecient as front end for serving static files.
I am not sure if there is a project page for this in Django wiki somewhere or not, but the code changes are being committed into the branches/soc2009/http-wsgi-improvements branch of Django source code repository.
You needn't strictly wait for that stuff. It is just putting a clean and portable interface in place across the different mechanisms. If using nginx as front end in front of Apache/mod_wsgi, you could use X-Accel-Redirect now. If using Apache/mod_wsgi 3.0 and daemon mode, you could use Location now, but do need to ensure you set up Apache correct. Alternatively, you could implement your own WSGI middleware wrapper around the Django application which looks for some response header of your own to indicate file to be returned and which uses wsgi.file_wrapper to return that instead of actual response returned from Django.
BTW, the authentication hook mechanisms listed for both mod_python and mod_wsgi by others would use HTTP basic authentication, which isn't what you wanted. This is presuming you want files to be protected by Django form based login mechanism using cookies and backend sessions.

Related

Integrating Sphinx and Django in order to require users to log in to see the documentation

I am curious if it is possible to hide sphinx documentation inside a django app so that only people who log in can see it. It seems to me that since sphinx creates its own structure and that Django uses the urlconf to define which pages a user can see, that it wouldn't be possible to combine the two. Although there must be some combining since the Django website likely uses django and sphinx. I am wondering if anyone has any insight or if they can point me in the right direction.
Thank You in Advance!
Sphinx builds your docs into HTML files, so in most cases this docs should be served by your web server rather then Django. However Django is able to serve static files as well.
You can use the django.views.static.serve function to do this and wrap this function with login_required. E.g:
from django.views.static import serve
from django.contrib.auth.decorators import login_required
urlpatterns += patterns('',
url(r'^docs/(?P<path>.*)', login_required(serve), {'document_root': '/path/to/sphinx/build/html'}, 'docs'),
)
However this configuration will be considered a bad practice in production environment as in this case Django will serve both html and css/js files from your sphinx theme.
The first improvement you can do here is to serve /path/to/sphinx/build/html/_static/ with apache/nginx or whatever you use.
The more proper way is to serve docs with apache/nginx and make it handle the auth itself. Unfortunately I made a quick Google search but did not find a way to use Django's User model to handle http_auth in apache or other. Alternatively you can use something like mod_sendfile or X-Accel modules - http://www.wellfireinteractive.com/blog/nginx-django-x-accel-redirects/ In a nutshell - Django app checks permission if user can view the file and add special header to response containing file path. Webserver will serve this file instead of original message from django

Serve media file from Django

I know that it's not a good way to serve directly file and picture from django via views and urls dispatch, but if these files and pictures are served via the server (Apache), the whole world can see them. What if some files and pictures are private for the user, and only the connected user can see these files or pictures? In this case, I need to serve by django itself?
To serve private documents, you should use a Python view that does the security checks.
Here is an example.
If you are using Apache with mod_wsgi then you can use mod_xsendfile
You are essentially looking to run the authorisation for some resources via Django, pass a header back to Apache saying 'Hey dude, lighten up. This user is okay to access this' Apache will then handle returning the resource.
Rough steps (as in, rough enough that you will need to do a little more research using the links I provide as a starting point)
Apache needs to know which resources are public and which aren't. Create a sub directory under media for both of these types (Why not go crazxy and call them /media/public/ and /media/private/)
Set up an alias for the public directory and a WSGIScriptAlias for the protected dir, the protected alias will be pointing to your main site handler (probably django.wsgi)
Add settings to vhost:
XSendFile On
XSendFileAllowAbove On
Add an urlconf to your Django app that handles /media/protected/{whatever} and routes it through your auth Django app auth logic. An example of this is here
A useful snippet for the above is here
and another example for good measure here

Restricting access to static files in Django/Nginx

I am building a system that allows users to generate a documents and then download them. The documents are PDFs (not that it matters for the sake of this question) and when they are generated I store them on my local file system that the web server is running on with uuid file names
c7d43358-7532-4812-b828-b10b26694f0f.pdf
but I know "security through obscurity" is not the right solution ...
I want to restrict access to they files on a per account basis if possible. One thing I think I could do is upload them to S3 and provide a signed URL, but I want to avoid that for now if possible.
I am using Nginx/Django/Gunicorn/EC2/S3
What are some other solutions?
If you are serving small files, you can indeed use Django to serve them directly, writing the file into the HttpResponse object.
If you're serving large files however, you might want to leave that task to your webserver, you can use the X-Accel-Redirect header on Nginx (and X-Sendfile for Apache & Lighttpd) to have your webserver serve the file for you.
You can find more information about the header itself in Nginx's documentation here, and you could find some inspiration as to how to use that in Django here.
Once you're done sending files through Django views, enforcing user authentication should be pretty straightfoward using Django's auth framework.
How about enforcing user==owner at the view level, preventing access to the files, storing them as FileFields, and only retrieving the file if that condition is met.
e.g. You could use the #login_required decorator on the view to allow access only if logged in. This could be refined using request.user to check against the owner of the file. The User Auth section of the Django documentation is likely to be helpful here.
The other option, as you mention is via S3 itself, generating urls within Django which have a querystring allowing an authenticated user access to download a particular s3 object with a time limit. Details on that can be found at the s3 documentation. A similar question has been asked before here on SO.
I've used django-private-files with great success, it enforces protection at the view level and uses differente backends to do the actual file transfer.

Protecting static files from non logged in users in Django

I have an existing site with a number of documents being served staticly. Client wants to add login protection to the site - not a problem using django_auth. However, the files being served from apache are still downloadable?
Is there a way to restrict access?
Ideally, this would require the path to these docs not changing on the site.
Was thinking of removing the alias from the apache config and having that route to a view that has the #login_required decorator on it, and then forwarding on.
See Having Django serve downloadable files on how to set up Django to work with Apache X-Sendfile. You can wrap the X-Sendfile header sending with some authentication checks and you should be good to go.

Django: control access to "static" files

Ok, I know that serving media files through Django is a not recommended. However, I'm in a situation where I'd like to serve "static" files using fine-grained access control through Django models.
Example: I want to serve my movie library to myself over the web. I'm often travelling and I'd like to be able to view any of my movies wherever I am, provided I have internet access. So I rip my DVDs, upload them to my server and build this simple Django application coupled with some embeddable video player.
To avoid any legal repercussions, I'd like to ensure that only logged-on users with the proper permissions (i.e. myself and people living in the same household, which can, like me, access the real DVDs at their convenience), but denies it to other users (i.e. people who posted comments on my blog) and returns an HTTP 404.
Now, serving these files directly using Apache and mod_wsgi is rather troublesome because when an HTTP request for the media files (i.e. http://video.mywebsite.com/my-favorite-movie/) comes in, I need to validate against my user database that the person at the other end has the proper permissions.
Question: can I achieve this effect without serving the media files directly through a Django view? What are my options?
One thing I did think of is to write a simple script that takes a session ID and a video's slug and returns some boolean indicating if the user may (or may not) access the video file. Then, somehow request mod_wsgi to execute this script before accessing the requested URL and return an HTTP 404 if the script failed. However, I don't have a clue if this is even possible.
Edit: Posting this question clarified some of my ideas for search and I've come across mod_python's file wrapper extension. Does anyone have enough experience with that to validate that it is a viable solution?
Yes, you can hook into Django's authentication from Apache. See this how-to:
Authenticating against Django’s user database from Apache