django authentication .htaccess static - python

In my app users can upload files for other users.
To make the uploaded files accesible only for the addresse I need some kind of static files authentication system.
My idea, is to create the apache hosted directory for each user and to limit access to this derectory using .htaccess.
This means that each time new django user is created, I need to create a directory and the appropriate .htaccess file in it. I know I should do it using post_save signals on User model but I don't know how to create the .htaccess in the user's directory from python level. Can you help me with that?
Or perhaps you have better solution to my problem?

Use python to rewrite the .htaccess automatically?
Use a database with users and use a Apache sessions to authenticate?

Why not have an PrivateUploadedFile object that has a field for the file and a m2m relation for any Users who are allowed to read that file? Then you don't have to mess with Apache conf at all...
from django.contrib.auth.models import User
from django.db import models
import hashlib
def generate_obfuscated_filename(instance, filename):
hashed_filename = hashlib.sha1(str(filename)) #you could salt this with something
return u"your/upload/path/%s.%s" % (hashed_filename, filename.split(".")[-1]) #includes original file format extension
class PrivateUploadedFile(models.Model):
file = models.FileField(upload_to=generate_obfuscated_filename)
recipients = models.ManyToManyField('User')
uploader = models.ForeignKey('User', related_name="files_uploaded")
def available_to(self, user):
#call this as my_uploaded_file_instance.available_to(request.user) or any other user object you want
return user in self.recipients.all() #NB: not partic. efficient, but can be tuned

Came across this django-sendfile which can be used to serve static files. Might be helpful.

Have Django handle authentication and authorization as normal, then use Apache's mod_xsendfile to have Apache handle sending the actual file. Remember to have the files uploaded to a place that cannot be accessed directly, ideally outside Apache's document root.
This question has a good example of how to implement this behaviour, but it basically boils down to setting response['X-Sendfile'] = file_path in your view.
django-sendfile does the same thing, but for several different web servers (and convenience shortcuts), and django-private-files is the same, but also implements PrivateFileField

Add a view that controls the authentication of the user, and serve the file via django's static files serving tools:
def get_file(request, some_id):
# check that the user is allowed to see the file
# obtain the file name:
path = path_from_id(some_id)
# serve the file:
return django.views.static.serve(request, path, document_root=your_doc_root)
This is a perfectly secure solution, but perhaps not ideal if you serve an enormous of files in that way.
Edit: the disclaimer on the django page does not apply here. Obviously, it would be inefficient to serve all your files with static.serve. It is however secure in the sense that you only serve the files to the users that are allowed to.

Related

Django folder structure for created file, static? media? or another one?

I want to create the file(.wav) by django and let it be downloaded by user.
Currently I create the file under /myproj/static directory.
However it is mixed with the other jpg/img files, so,, it's not good design?
then, I read the document about /myproj/media directory but it is said used for user upload files.
So,,, how is the good directory design for server created file?
Should I create such as /myproj/create_wav?
but how can user access this url in template?
Thank you for any idea or helps.
According to Django's documentation,
By default, Django stores files locally, using the MEDIA_ROOT and MEDIA_URL settings. (...) However, Django provides ways to write custom file storage systems that allow you to completely customize where and how Django stores files.
So, in OP's shoes I'd simply use the media folder, since that's used not only for user uploads but also for user downloads.

apache authentication against django application

I have the following problem:
I wrote an application in Django, basically it’s some kind of management platform for my choir. Now we have some files that we want to provide to our members, but only to them (i.e. registered users). Additionally, there are some files that should only be accessible to the management. Ideally, I want to be able to define in the database who can access which files.
Here is some information about the environment:
Server OS Debian 8.7
Python version 2.7
Django version 1.10
Mod_wsgi version 4.3
What have I tried so far?
Django documentation provides a very, very small example of how to authenticate against the database.
The part where I only validate whether the user is a valid user works. Although it kind of sucks, since the users have to login again every time they access a file.
The part where I validate whether the user is a group doesn't work at all. Apache says:
Unknown Authz provider: group
Then I started to look up this problem, which led me here. When I use the option wsgi-group the apache configuration doesn't cause any problems, but it apparently doesn't validate whether the user is in the specified group either. (I tried to access the file with a user who is not in the specified group and succeeded...)
Now here are some questions I have to this problem:
Has anyone else experienced these problems with the Require group/Require wsgi-group option and has found a solution?
Is there a possibility to "link" the session of the website with the session of the apache file access? As I already mentioned, it is not very user-friendly if the users have to login again every time they want to access a file..
Is there a possibility to write a custom authentication function? Technically, looking at an example implementation for the authentication method check_password (here "auth.wsgi"), I could overwrite this function, provided that the environ variable contains the URL that the user wants to access. (does it?)
This would probably be the most dynamic solution, but I don't quite know if this is possible. The basic idea ist that I render a button in the view for each downloadable file, that sends a POST to the server. The server checks whether the session user has the rights to access this file, and, if true, returns the file. This would mean that the file can only be accessed by the application and not through the apache. Is this possible?
I hope someone can help me, thank you in advance anyways =)
Edit: I did some research and found an answer for my last question here (Having Django serve downloadable files). With the mod_xsendfile this is apparently manageable pretty easily. That leaves just 3 more questions ;)
I have also used X-Send for various purposes within my Django App (usually to serve up files stored in FileFields) but for hosting a suite of static pages such as sphinx-generated pages and supporting media, proxying via X-Send has lead me into more problems than solutions.
As I was researching my own question I discovered yours and realized that mine is the solution to your question #1 and #2: Use mod_wsgi's WSGIAccessScript config with the allow_access(environ, host) function to instantiate a Django WSGI Request which can be programatically distilled to a boolean using whatever business logic you require. I hope someone else can answer my more specific concerns about the approach, but it is functional.
Your question #3 suggests I point you towards Django's Custom Authentication Backend documentation. I have written a custom backend that uses email addresses to authenticate a user rather than requiring users to remember an arbitrary username.
#Django Imports
from django.conf import settings
from django.contrib.auth.models import User
class EmailAuthBackend(object):
"""
Email Authentication Backend
Allows a user to sign in using an email/password pair rather than
a username/password pair.
"""
def authenticate(self, username=None, password=None):
""" Authenticate a user based on email address as the user name. """
try:
user = User.objects.get(email__iexact=username)
if user.check_password(password):
return user
except User.DoesNotExist:
return None
def get_user(self, user_id):
""" Get a User object from the user_id. """
try:
return User.objects.get(pk=user_id)
except User.DoesNotExist:
return None

Django - Protecting Media files served with Apache with custom login_required decorator

I deployed a Django app using Apache, and I check for authentication in most views using a decorator.
#custom_decorator
def myView(request):
bla bla bla...
It's not the #login_required decorator that comes with Django, but it's almost the same thing, except that allows access only to users from certain groups. This works as intended.
Also, I'm serving media (user uploaded) files with Apache, like this:
Alias /media /path/to/media
<Directory /path/to/media>
Require all granted
</Directory
I can access the media files just fine, but the problem is that I can access them even if I'm not logged in, simply by typing the url manually, like:
mySite/media/myFile.png
Is there a way to limit access to the media files, hopefully using the custom decorator?
I stumbled across a similar question: How do you Require Login for Media Files in Django, but unfortunately the answer went way over my head.
Thanks in advance!
When you mention media path to the apache, those files are served directly by Apache (or Nginx or any other web server). Those requests do not even goes through your Django application. Hence you do not have a control over those requests or the data served by them.
One way is to create your separate API to serve the static/media files. In that API, use the same validation that you do for other content.
Even better, if you have AWS (Amazon Web Services) or GCP (Google Cloud Platform) account, store the static files on the S3 or Cloud Storage respectively and serve their URL of files via your API.
PS: Do not forget to remove the media path from the Apache configuration. Else, Apache will keeps on serving those file.
Alternatively, as mentioned in Sarafeim's answer to Restricting access to private file downloads in Django which requires modification in both sever and application side. You need a way for your HTTP server to ask the application server if it is ok to serve a file to a specific user requesting it. You may achieve this using django-sendfile which uses the X-SendFile mechanism. As per the django-sendfile's README:
This is a wrapper around web-server specific methods for sending files to web clients. This is useful when Django needs to check permissions associated files, but does not want to serve the actual bytes of the file itself. i.e. as serving large files is not what Django is made for.
To understand more about the sendfile mechanism, read: Django - Understanding X-Sendfile
Okay, so based on #MoinuddinQuadri answer and links, it seems that the easiest solution is to serve the files using a regular Django view, and apply the desired decorator, like this:
#custom_decorator
viewFile(request, objectID):
object = MyModel.object.get(id = objectID)
return HttpResponse(object.file, content_type = "image/png")
(In my case, I wanted to serve a FileField related to a Model, so in the view I pass the ID of the object instead of the file name).
Also, I commented out the corresponding code in the Apache conf:
### Alias /media /path/to/media
### <Directory /path/to/media>
### Require all granted
###</Directory
I had to change some templates to use the new view instead of the URL of the media file, but now it works as intended, locking out non-logged users.
However, this no longer uses Apache to serve the files, it uses Django itself, which according to the docs, is inneficient and not recommended.
Ideally you want to still serve the files using Apache and just use the view to protect its access, and for that you can use mod_xsendfile for Apache, or simply use Django Sendfile, which is a wrapper for the module just mentioned.
I tried the latter, but unfortunately it has problems with file names that have non-ascii characters. As my target are spanish-speaking users, I had to resort of just serving the files with Django, at least for now.
I used solution #1 in the post "Django protected media files". There are two other solutions described here as well: "Unpredictable Urls" and "X-Sendfile", but the one I'm describing was my choice.
As #Sauvent mentioned, this causes the files to be served by Django and not by a web server (e.g. Apache). But it's quick and easy if you're not dealing with a lot of traffic or large files.
Basically, add the following to your urls.py:
#login_required
def protected_serve(request, path, document_root=None, show_indexes=False):
return serve(request, path, document_root, show_indexes)
urlpatterns = patterns('',
url(r'^{}(?P<path>.*)$'.format(settings.MEDIA_URL[1:]), protected_serve, {'document_root': settings.MEDIA_ROOT}),
)
In my case I edited it to the following because my directories are set up differently and I use Login Required Middleware to ensure login is required everywhere (Django: How can I apply the login_required decorator to my entire site (excluding static media)?:
urlpatterns = patterns('',
url(r'^media/(?P<path>.*)$', "django.views.static.serve", {'document_root': settings.MEDIA_ROOT}),
)

Can I use Django to prevent direct access to an image file?

I'd like to prevent my web users from simply right clicking an image and copying/sharing the URL. Certain authenticated users have access to certain images, and I'd like to enforce this as much as possible. Non authenticated users should have no access to image files.
It's generally recommended to avoid storing/fetching images from a DB, due to performance issues, from what I have read.
I've considered having a function that reads the file (server side, in python) and inserts it into the webpage (base64 encoded, possibly, or some other way) in the Django view functions. Combined with an .htaccess file that denies external requests, this would likely work, but it seems like it'd be resource intensive.
Is there any other options for enforcing this rule? I realize users can screenshot, save images, etc, but it's my responsibility to enforce these restrictions as much as possible, what are my best options?
edit: I have no experience using a CDN, but would be willing to use this if it's a viable option that covers these needs.
I'll bite.
Session Middleware - not elegant, but it will work
You'll want the images you don't want served publicly to not be served through your standard apache/django static files config.
your session middleware can then check all incoming requests for the path and if the path is your image directory (such as /privateimg/) and the user is not authenticated you can bounce them back out or replace it inline with another image (such as one that has a watermark).
You can check out the django docs on how session middleware works https://docs.djangoproject.com/en/dev/topics/http/sessions/
People can still pass your links around, but only authenticated users can actually see the contents of said links (called gating your content)
To elaborate:
settings.py
GATED_CONTENT = (
'/some_content_dir/', # This is a directory we want to gate
'.pdf', # maybe we want to gate an entire content type
)
MIDDLEWARE_CLASSES = (
... # Out of the box middleware...blah blah
'yourapp.somemodule.sessionmiddleware.GatedContent',
)
Then you have the following app structure
yourapp
|-somemodule
|-sessionmiddleware.py
Now to the meat (yum!)
sessionmiddleware.py
class GatedContent(object):
"""
Prevents specific content directories and types
from being exposed to non-authenticated users
"""
def process_request(self, request):
path = request.path
user = request.user # out of the box auth, YMMV
is_gated = False
for gated in settings.GATED_CONTENT:
if path.startswith(gated) or path.endswith(gated):
is_gated = True
break
# Validate the user is an authenticated/valid user
if is_gated and not user.is_authenticated():
# Handle redirect
You might be interested in XSendfile.
This is most [elegant and] performance choice IMO: actual files will be served by you webserver, while access control to this files will be done using your Django app.
You may google for "django xsendfile", there are lot of useful posts.

Django - Privatizing Code - Which files are served to a user?

I am using Django to develop an API using an algorithm I wrote.
When someone requests a url, my urls.py calls a function in views.py which serves
page that returns a JSON string.
If my algorithm is in my views.py file, or in another file on my server, would it be possible for a user to view the contents of this file, and then see my algorithm?
In other words, when using Django, which files will never be served to a user, and which files will be?
Is there any way I can stop someone from viewing my algorithm if it's in a .py file? Other than Chmodding the file or encrypting the code?
Thank you for your time.
Django only serves the responses that you explicitly create and return from your views. There is no general ability to request files from it.
Make sure your source code isn't in a directory that your web server is configured to serve from, and make sure your settings.py value for DEBUG is False, and you should be fine. Oh, and just in case - don't try to use the Django development server in production.
As long as nobody has shell access to your server, people will never see more than the actual HTML output of your page. .py files are not shown to the user that has requested an url in the browser.

Categories