I have project in django 1.0.4 - yes I know it is old.
I want to use the lack of access to media (audio) files for users who are not logged in.
After making changes to nginx, logged in users also have no access.
I tried with view and url function - no result
my nginx settings:
location /media/content/audio/ {
deny all;
}
my function and url
#login_required
def protected_serve(request, path, document_root=None, show_indexes=False):
if not request.user.is_authenticated:
raise Http404()
else:
return serve(request, path, document_root, show_indexes)
urlpatterns += patterns('',
(r'^media/content/audio/(?P<path>.*)$', protected_serve),
)
You're very close to having the whole puzzle put together. There are two things you need to do:
Configure NGINX that you do want to be able to serve data from a particular folder, but that said folder isn't public. The authorization to send files from a folder will come from the application behind NGINX, not from external requests to NGINX.
Have your django app send the kind of response to NGINX that NGINX understands to mean "serve this file from the protected area in 1"
The way you achieve the first goal is to use the config directive "internal"
Achieving the second goal is to use the HTTP response header "X-Accel-Redirect" as #ralf states in the comments above.
Here is a blog post on the subject: https://clubhouse.io/developer-how-to/how-to-use-internal-redirects-in-nginx/
A Python project to help you achieve the same goal: https://pypi.org/project/django-transfer/
NGINX Docs: https://www.nginx.com/resources/wiki/start/topics/examples/xsendfile/
Related
I am going to build a Django storage application where users can upload files to the server. Users also can download their files from the server. To download the file, the server returns the URL of the media file to the user. I added a permission class, if the user is authenticated & owner of the file then return the URL of the media file, otherwise return 404. But there is a problem, if any unauthenticated user found the URL of the media file from the history of the browser, then he can download/access the file. How can I handle this case?
The problem you're mentioning is not related to your backend application. It's related to the web server serving your media files (e.g. Nginx). In order to handle this problem, you should generate secure links from your django app which are bound to specific user IPs and have an expiration time.
You can read this article for more info:
https://www.nginx.com/blog/securing-urls-secure-link-module-nginx-plus/
Django's development server does not deal with static/media files
please take a look at this django-sendfile
I personally liked what Michal Májský suggested in his blog here:
from django.conf.urls import patterns, include, url
from django.contrib.auth.decorators import login_required
from django.views.static import serve
from django.conf import settings
#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'^%s(?P<path>.*)$' % settings.MEDIA_URL[1:], protected_serve, {'document_root': settings.MEDIA_ROOT}),
)
This is just verifies that any request to the media folder will go through authentication check, right from the urls.py
Hello everyone I want to redirect URLs like this
from
https://www.example.co.uk/pages/terms-conditions
to
https://www.example.co.uk/terms-conditions
I developed the updated site using the Django framework and now want to redirect old site URLs to new URLs and not losing traffic. what is the best way of doing…?
New site is not published it is on local machine
my view.py
my urls.py
I guess this is not best practice but you can just redirect the user to the right url if he hits the other one in your views.py like:
def old_view(request):
return redirect('https://www.example.co.uk/terms-conditions')
def new_view(request):
return render(request, "your_app/conditions.html")
with urls.py like:
path('pages/terms-conditions/', views.old_view, name='old_view')
path('terms-conditions/', views.new_view, name='new_view')
Assuming you want to remove the application name from the urls. In your project's urls.py NOT your applications urls.py. You should have a line similar to the following:
path('pages/', include('pages.urls', namespace='pages')),
replace the 'pages/' with just ''.
Note: Using the pages/ allows you to provide segregation between your applications to ensure the urls do not conflict
I would like to serve user uploaded media files using nginx on the same host as the Django application rather than a CDN or S3 or similar.
The django-private-storage library can be used to protect media files behind a login: https://github.com/edoburu/django-private-storage
I am deploying my Django application with Dokku.
Dokku says that dokku persistant storage plugin should be used to allow for user uploads to be persisted on the host. https://dokku.com/docs~v0.9.2/advanced-usage/persistent-storage/
My confusion is that django-private-storage requires you to edit the config for nginx.
Specifically, it requires you to set the location of the private media being served to be internal. So that the URL cannot be accessed from the outside by a user who isn't logged in.
The dokku docs don't explain how to use persistant storage behind an application login.
Do I actually need django-persistant-storage to be able to write user uploaded media?
How can I combine these solutions so that my application, which is inside a container, can read and write media files, which media files are served by nginx, and served at an internal location that can only be accessed by a user who is logged into the application?
Updates (Oct 2021)
I am able to deploy my app, upload files and access them at the appropriate URL. But I haven't been able to protect them against unauthenticated access.
I haven't yet used django-private-storage or dokku persistant storage. Once the files are inaccessible I plan to follow these steps to allow authenticated access: https://b0uh.github.io/protect-django-media-files-per-user-basis-with-nginx.html
I created a file my_conf.conf saved to /home/dokku/backend/nginx.conf.d
which contains
location /protected/ {
internal;
alias /home/dokku/backend/;
}
and then rebooted Nginx
I can't actually see the images anywhere on host, but if I run dokku enter backend then my files are there in the container under '/mediafiles/testuploads/'
Here is settings.py
MEDIA_ROOT = os.path.join(BASE_DIR, 'mediafiles')
MEDIA_URL = '/media/'
and models.py
class User(AbstractUser):
profile_image = models.ImageField(upload_to='testuploads/', null=True)
Let's do it by yourself!
You can serve your profile images with your custom view that checks auth!
You may implement the needed feature without additional dependencies. As a bonus, you will understand the whole process.
So, you need to:
Add a path, something like /profiles/<int:user_id>/image/ in your urls.py
Use this link with a proper user_id in your front-end (change a needed template)
Write a class-based or function-based view for this endpoint, as usual, check the user_id parameter, check the auth in request, compare user in request with the user in the user instance and maybe something else.
Response with 401 Not Authorized when you have an unauthorized request.
Response with FileResponse in OK.
from django.http import FileResponse
response = FileResponse(open('myfile.png', 'rb'))
use your user.profile_image property
Theoretically, if the user doesn't know the old path to /media/testuploads/filename.ext this file is "not shared".
But if you want to be sure - don't serve /media/ folder with NGINX or exactly /media/testuploads/ path if you want to serve another media files (return 401 https://$host$request_uri; in NGINX config in the needed block). Such changes need NGINX to be reloaded.
Watch view caching in the next seasons 😀 to improve the performance. But the browser will cache the image if it works in default settings.
in the urls i have this url that leads to the serve_protected_file on each time someone is trying to access a media file
url(r'^%s(?P<path>.*)$' % settings.MEDIA_URL[1:], views.serve_protected_file, {'document_root': settings.MEDIA_ROOT})
the serve_protected_file looks like this
def serve_protected_file(request, path, document_root=None, show_indexes=False):
if request.user.is_authenticated:
return serve(request, path, document_root, show_indexes)
raise Http404()
This only works in the development environment
When i deploy then the files are served with the nginx and it gives me the file trough the url which looks like that
https://staging.mywebsite.com/media/img/531126758844.jpg
how to restrict it everything in my media folder is private and external access to a file should be restricted unless user is authenticated
my staging.nginx.conf looks like
location ~ ^/media.*?/(.*)$ {
alias /data/www/staging/mywesite/media/$1;
access_log off;
}
okay Eurica after 2 hours of head banging...
Since the media files are all confidential, I just deleted the media configuration in staging.nginx.conf and it works like a charm
so now is executing the django view..., nginx was intercepting the django view
Thanks to the answer below, I have a before_request function which redirects a user to /login if they have not yet logged in:
flask before request - add exception for specific route
Here is a copy of my before_request:
#app.before_request
def before_request():
if 'logged_in' not in session and request.endpoint != 'login':
return redirect(url_for('login'))
Files in my static directory are not being served however unless the user is logged in.
On my /login page I am sourcing a css file from the /static directory but it can not be loaded because of this before_request.
I have deployed this application using apache mod_wsgi and in my apache configuration file I have even included the /static directory as the site's DocumentRoot.
How can I add an exception to serve my application's /static files without the user logging in, but still use this before_request for the routes defined by my flask application?
You'll need to add an Alias or AliasMatch directive to your Apache config file (or .htaccess file, should you not have access to the .conf files) to ensure that Apache serves your static files, rather than Flask. Make sure that you have provided a Directory to allow the Apache web server to access your static path. (Also, don't forget to restart Apache if you are editing the .conf files so your changes will be picked up).
As a temporary stop-gap measure (or to make it easy to work with in development) you could also check to make sure that the string /static/ is not in request.path:
if 'logged_in' not in session \
and request.endpoint != 'login' \
and '/static/' not in request.path:
I think that there is a solution cleaner than checking request.path.
if 'logged_in' not in session and request.endpoint not in ('login', 'static'):
return redirect(url_for('login'))
I do agree with the Apache approach, but for a quick fix I the following logic at the start of the before_request() function:
if flask.request.script_root == "/static":
return