Django - remove www from URLs - django

I've added this to my .htacces:
RewriteEngine on
RewriteCond %{HTTP_HOST} ^www\.
RewriteRule ^(.*)$ http://example.com/$1 [R=301,L]
but then trying to reach www.example.com redirects me to:
http://example.com/example/wsgi.py/
because i have WSGIScriptAlias / home/www/example.com/example/wsgi.py directive in my httpd.conf and of course i get 404 error.
Eventually, i've managed to fix this by adding next line in my urls.py:
url(r'^example/wsgi.py/$', index), (so it redirects to home page)
but i'm not quite sure that this is the right way to do it (because when i try to reach example.com i see that web browser changes address quickly to www.example.com and then again to example.com)
If anyone would ask, yes i've seen this but this didn't help me either, because browser gets url recursive problem (example.com/example.com/example.com/example.com/example.com...)
EDIT : FOLDER STRUCTURE
This is my folder structure:
\mysite\
static\
media\
.htaccess
manage.py
mysite\
templates
templatetags
tinymce
static
urls.py
settigs.py
views.py
wsgi.py
models.py
forms.py
__init__.py

I know this has been answered some time back but to add to the answer given above use
host.startswith('www.')
its more readable and also you should use permanent redirect to give the browser correct response header.
from django import http
class NoWWWRedirectMiddleware(object):
def process_request(self, request):
host = request.get_host()
if host.startswith('www.'):
if request.method == 'GET': # if wanna be a prefect REST citizen, consider HEAD and OPTIONS here as well
no_www_host = host[4:]
url = request.build_absolute_uri().replace(host, no_www_host, 1)
return http.HttpResponsePermanentRedirect(url)

I find it much simpler to accomplish no-www redirects with middleware that with with Apache mod_rewrite config.
The middleware that you linked to looks like it does the trick. I'm guessing your problems came from Apache config - make sure you remove all mod_rewrite commands (Rewrite* stuff) and then restart the apache server (ref. Apache docs but check for your OS, might be specific).
There is only one additional tweak that you should to: make sure you don't redirect any POST requests, because that might result in lost data (tangent ref: Why doesn't HTTP have POST redirect?).
Anyways, this is what I use, worked quite well for me:
from django.http import HttpResponseRedirect
class NoWWWRedirectMiddleware(object):
def process_request(self, request):
if request.method == 'GET': # if wanna be a prefect REST citizen, consider HEAD and OPTIONS here as well
host = request.get_host()
if host.lower().find('www.') == 0:
no_www_host = host[4:]
url = request.build_absolute_uri().replace(host, no_www_host, 1)
return HttpResponseRedirect(url)
To use it, put in a file somewhere, maybe mysite/mysite/middleware.py.
Then make sure it's run, in your settings.py:
MIDDLEWARE_CLASSES = (
'mysite.middleware.NoWWWRedirectMiddleware',
# ... other middleware ...
If there is no MIDDLEWARE_CLASSES in your settings.py then copy the defaults from here in the Django docs but make you're looking at the correct version of Django, there are some changes in 1.7!

Modified version that works with Django 1.11+ style middleware:
from django.http import HttpResponsePermanentRedirect
class NoWWWRedirectMiddleware:
def __init__(self, get_response=None):
self.get_response = get_response
def __call__(self, request):
response = self.process_request(request)
return response or self.get_response(request)
def process_request(self, request):
host = request.get_host()
if host.startswith('www.'):
if request.method == 'GET':
no_www = host[4:]
url = request.build_absolute_uri().replace(host, no_www, 1)
return HttpResponsePermanentRedirect(url)

This Apache configuration works for me:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www.example.com$ [NC]
RewriteRule ^(.*)$ http://example.com$1 [R=301,L]
WSGIScriptAlias / /home/www/example.com/example/wsgi.py
WSGIPythonPath /home/www/example.com/example
<Directory /home/www/example.com/example>
<Files wsgi.py>
Require all granted
</Files>
</Directory>

Related

How to secure media files in django in poduction?

I am trying to make a project and have some media files which should only be accessed by their owner.
In production, media and static files are served by apache (or nginx but I am using apache).
I was looking for some solutions and I am not able to apply.
On djangosnippets website, I found this code,
from mod_python import apache
from django.core.handlers.base import BaseHandler
from django.core.handlers.modpython import ModPythonRequest
class AccessHandler(BaseHandler):
def __call__(self, req):
from django.conf import settings
# set up middleware
if self._request_middleware is None:
self.load_middleware()
# populate the request object
request = ModPythonRequest(req)
# and apply the middleware to it
# actually only session and auth middleware would be needed here
for middleware_method in self._request_middleware:
middleware_method(request)
return request
def accesshandler(req):
os.environ.update(req.subprocess_env)
# check for PythonOptions
_str_to_bool = lambda s: s.lower() in ("1", "true", "on", "yes")
options = req.get_options()
permission_name = options.get("DjangoPermissionName", None)
staff_only = _str_to_bool(
options.get("DjangoRequireStaffStatus", "on")
)
superuser_only = _str_to_bool(
options.get("DjangoRequireSuperuserStatus", "off")
)
settings_module = options.get("DJANGO_SETTINGS_MODULE", None)
if settings_module:
os.environ["DJANGO_SETTINGS_MODULE"] = settings_module
request = AccessHandler()(req)
if request.user.is_authenticated():
if superuser_only and request.user.is_superuser:
return apache.OK
elif staff_only and request.user.is_staff:
return apache.OK
elif permission_name and request.user.has_perm(
permission_name
):
return apache.OK
return apache.HTTP_UNAUTHORIZED
But I am not able to install mod_python. Please tell me how to do that first
And I changed my .conf file for apache which is as below.
<VirtualHost *:80>
ServerName podcast.com
ServerAdmin srpatel980#gmail.com
DocumentRoot /home/username/Documents/secret_media
ErrorLog ${APACHE_LOG_DIR}/error.log
CustomLog ${APACHE_LOG_DIR}/access.log combined
Alias /static /home/username/Documents/secret_media/static
<Directory /home/username/Documents/secret_media/static>
Require all granted
</Directory>
Alias /media /home/username/Documents/secret_media/media
<Directory /home/username/Documents/secret_media/media>
Require all granted
</Directory>
<Directory /home/username/Documents/secret_media/secret_media>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
WSGIScriptAlias / /home/username/Documents/secret_media/secret_media/wsgi.py
WSGIDaemonProcess secret_media python-path=/home/username/Documents/secret_media python-home=/home/username/Documents/secret_media/venv
WSGIProcessGroup secret_media
LoadModule auth_basic_module modules/mod_auth_basic.so
LoadModule authz_user_module modules/mod_authz_user.so
<Location "/media/images">
PythonPath /home/username/Documents/secret_media
PythonOption DJANGO_SETTINGS_MODULE secret_media.settings
PythonAccessHandler secret_media.wsgi.py #this should point to accesshandler
SetHandler None
</Location>
</VirtualHost>
There are some settings which are repeated, don't know why, please explain
X-Sendfile
I do not know about the method you use above, but I have been using mod_xsendfile to do the same. Whats the principle: request to a url is handeled by a view that checks access rights ... the view returns a response with a key "X-Sendfile" & file ... this triggers Apache on the way back to serve the media file.
I just show you the code without testing syntax .... please ask if something is not clear
Apache httpd.conf
LoadModule xsendfile_module modules/mod_xsendfile.so
in Apache remove the usual "alias media ..."
Apache httpd-vhosts.conf
<VirtualHost *:80>
# ... all your usual Django Config staff
# remove the usual alias /media/
# Alias /media/ d:/WEBSPACES/dieweltdahinter_project/media/
XSendFile on
XSendFilePath D:/your_path/media/
<Directory "D:/your_path/media">
Order Deny,Allow
Allow from all
</Directory>
</VirtualHost>
urls.py
urlpatterns = [
.....,
re_path(r'^media/(?P<folder>[A-Za-z0-9\-_]+)/(?P<filename>[A-Za-z0-9\-_]+).(?P<extension>[A-Za-z0-9]+)\/?$', app_views.media_xsendfile, name='media-xsendfile'),
.....
]
views.py
# add decorators to manage access
#
def media_xsendfile(request, folder='', filename=None, extension=None):
# add an kind of user check ....
# only server certain type of files
if extension in ('jpg', 'png', 'gif'):
response['Content-Type'] = 'image/'+extension
elif extension == 'mp3':
response['Content-Type'] = 'audio/mpeg'
else:
return
if not folder == '':
folder = '/'+folder+'/'
response = HttpResponse()
# this is the part of the response that Apache reacts upon:
response['X-Sendfile'] = smart_str(settings.MEDIA_ROOT + folder + filename + "." + extension)
# need to handover an absolute path to your file to Apache!!
return response
One way to do it is using a so-called X-Sendfile.
In simple words:
User requests URL to get a protected file (so you need to keep your public and protected files separately, and then either proxy a request to Django for protected files, or serve files directly from the apache/nginx if they are public)
Django view decides which file to return based on URL, and checks user permission, etc.
Django returns an HTTP Response with the 'X-Sendfile' header set to the server's file path
The web server finds the file and returns it to the requester
The setup will be different for nginx and apache, according to my findings you need to install mod_xsendfile for apache, nginx supports it out of the box. Hopefully, that helps, ask any additional questions if needed.

Token Authorization Django {"detail":"Authentication credentials were not provided."}

I am trying to implement Token based Authorization at my server APIs. But, when I fire a query then it returns {"detail": "Authentication credentials were not provided."}
I have done almost all the settings recommended at various posts and also at the Django documentation.
In my settings.py:
INSTALLED_APPS = [
'rest_framework.authtoken',
]
And also,
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
}
In my views file:
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
authentication_classes = [TokenAuthentication]
permission_classes = [IsAuthenticated]
And in my Apache Config file at WSGI
WSGIPassAuthorization On
RewriteEngine on
RewriteCond %{HTTP:Authorization} ^(.*)
RewriteRule .* - [e=HTTP-AUTHORIZATION:%1]
I don't know if I am missing anything other than this. The token is pre-generated at super user level using command line.
The default Django Rest Framework HTML that you see when you go to your website in the browser does not work with token authorization. This is because you need to put Authorization: Token <insert your token here> in your header when sending a request. This is an issue because the form you fill out has no place to change headers.
I would suggest either Postman (nice GUI), or curl (command) to test out your API since you need to edit the HTTP headers of your requests.
Have you seen this related S.O. question?
If you're using mod_wsgi, leave out the REWRITE section, put WSGIPassAuthorization On below the WSGIScriptAlias, WSGIDaemonProcess and WSGIProcessGroup directives. Just got my Apache + mod_wsgi configuration working, looks something like this:
...
WSGIScriptAlias / /var/www/html/base/wsgi.py
WSGIDaemonProcess django_app python-path=/var/www/html python-home=/var/www/html/env
WSGIProcessGroup django_app
WSGIPassAuthorization On
...

Django serve static files with user verification [duplicate]

I have been fumbling around with trying to protect Django's media files with no luck so far! I am simply trying to make it where ONLY admin users can access the media folder. Here is my Nginx file.
server {
listen 80;
server_name xxxxxxxxxx;
location = /favicon.ico {access_log off; log_not_found off;}
location /static/ {
alias /home/{site-name}/static_cdn/;
}
location /media/ {
internal;
root /home/{site-name}/;
}
location / {
this is setup and working. Didn't include Code though
}
My Url File
urlpatterns = [
url(r'^media/', views.protectedMedia, name="protect_media"),
]
And my view
def protectedMedia(request):
if request.user.is_staff:
response = HttpResponse()
response['Content-Type'] = ''
response['X-Accel-Redirect'] = request.path
return response
else:
return HttpResponse(status=400)
This is producing a 404 Not Found Nginx error. Does anything look blatantly wrong here? Thanks!
BTW, I have tried adding /media/ to the end of the root URL in the Nginx settings.
This is what fixed this issue thanks to #Paulo Almeida.
In the nginx file I changed what I previosly had too...
location /protectedMedia/ {
internal;
root /home/{site-name}/;
}
My url is...
url(r'^media/', views.protectedMedia, name="protect_media"),
And the View is...
def protectedMedia(request):
if request.user.is_staff:
response = HttpResponse(status=200)
response['Content-Type'] = ''
response['X-Accel-Redirect'] = '/protectedMedia/' + request.path
return response
else:
return HttpResponse(status=400)
This works perfectly! Now only admin users can access the media files stored in my media folder.
This helped me a lot, just a small update and modification:
urls.py:
re_path(r'^media/(?P<path>.*)', protectedMedia, name="protect_media")
views.py:
from urllib.parse import quote
from django.http import HttpResponse
from django.contrib.admin.views.decorators import staff_member_required
#staff_member_required
def protectedMedia(request, path):
response = HttpResponse(status=200)
response["Content-Type"] = ''
response['X-Accel-Redirect'] = '/protectedMedia/' + quote(path)
return response
I had to change the nginx config to the following:
location /protectedMedia/ {
internal;
alias /home/{site-name}/;
}
Notes:
I prefer using the decorator, as it automatically redirects to the login page (when specified in settings) and sets the "next" page.
url() gets deprecated in Django 3.1 so just use re_path() instead
alias instead of root in nginx config: I don't want to have "/protectedMedia/" appear in the url (and it didn't work), see also nginx docs
edit from 2021-06-14: German-Umlauts (äüö etc.) didn't work in media paths so I edited the above answer to include quote from urllib.parse
If you're still stuck somewhere, this gave me further backround information: https://wellfire.co/learn/nginx-django-x-accel-redirects/

Django POST/GET, GET works, POST doesn't from MATLAB (urlread). Both work using django.test.client

I am learning Django so I've set up a very simple form/view/url example
Django Version 1.5.1
MATLAB Version R2012A
forms.py
from django import forms
import json
class json_input(forms.Form):
jsonfield = forms.CharField(max_length=1024)
def clean_jsonfield(self):
jdata = self.cleaned_data['jsonfield']
try:
json_data = json.loads(jdata)
except:
raise forms.ValidationError("Invalid data in jsonfield")
return jdata
views.py
from django.http import HttpResponse
from rds.forms import json_input
def testpost(request):
if request.method == 'GET':
form = json_input(request.GET)
if form.is_valid():
return HttpResponse('Were Good Get',mimetype='text/plain')
elif request.method == 'POST':
form = json_input(request.POST)
if form.is_valid():
return HttpResponse('Were Good Post',mimetype='text/plain')
else:
return HttpResponse('Not GET or POST.',mimetype='text/plain')
This view is mapped to the url in urls.py
url(r'^test2$','rds.views.testpost'),
So when I jump into the python manage.py shell on the local machine django is on I can issue the following commands and get the expected responses:
>>> from django.test.client import Client
>>> c = Client()
>>> r = c.post('/test2',{'jsonfield': '{"value":100}'})
>>> print r
Content-Type: text/plain
Were Good Post
>>> r = c.get('/test2',{'jsonfield': '{"value":100}'})
>>> print r
Content-Type: text/plain
Were Good Get
However when I jump into MATLAB on an external machine and issue the following commands. (Note doing this from MATLAB is a project requirement)
json = '{"value":100}';
% GET METHOD FOR JSON FORM
[gresponse,gstatus]=urlread('http://aq-318ni07.home.ku.edu/django/test2','Get',{'jsonfield' json});
% POST METHOD FOR JSON FORM
[presponse,pstatus]=urlread('http://aq-318ni07.home.ku.edu/django/test2','Post',{'jsonfield' json});
>> gresponse
gresponse =
Were Good Get
>> presponse
presponse =
''
I have searched around for a solution and really cant find anything. I've hit on it potentially being an issue with the CSRF (which I am still figuring out). Any hints or thoughts would be much appreciated.
Thank you.
EDIT:
Django is exposed through Apache, here is the configuration.
################################################
# Django WSGI Config
################################################
WSGIScriptAlias /django /var/django/cdp/cdp/wsgi.py
WSGIPythonPath /var/django/cdp
<Directory /var/django/cdp/cdp>
<Files wsgi.py>
Order deny,allow
Allow from all
</Files>
</Directory>
################################################
how are you exposing your django app for MATLAB? The very first thing is to check your access logs, is your server even getting a request? If so anything in its error logs?
Are you using the built in developkment server?
python manage.py runserver 0.0.0.0:8000
If so make sure that you can accept requests on that port
If you are serving it through another server i believe you have to whitelist the IP that you are making request from MATLAB, by adding it to ALLOWED_HOSTS

Django in a Subdirectory using fcgi

I'm trying to avoid having to somehow add a context_processor to add the base url of my project to every url I use in all of my templates.
My django project is nestled under a subdirectory(lets call it foo) and a lot of my urls in my templates are similar to this:
<a href='/somepath'>whatever</a>
This works fine for a project that is hosted on the root of a domain, but in this case since I am under the subdirectory foo the url would actually point to
www.example.com/somepath
The interesting thing is that the Admin site works fine. All of its url's are pointing to
foo/admin/...
I suspect I'm not searching for the correct terms to find the answer to this.
.htaccess
AddHandler fcgid-script .fcgi
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ foo.fcgi/$1 [L]
.fcgi
#!/usr/bin/python
import sys, os
# Current Path
cwd = os.path.join( os.path.dirname( os.path.abspath( __file__ ) ) )
# Activate current virtualenv
activate_this = os.path.join( cwd, 'bin', 'activate_this.py' )
execfile( activate_this, dict( __file__ = activate_this ) )
# Add a custom Python path.
sys.path.insert( 0, os.path.join( cwd, 'foo' ) )
os.environ['DJANGO_SETTINGS_MODULE'] = 'foo.settings'
from django.core.servers.fastcgi import runfastcgi
runfastcgi(method="threaded", daemonize="false")
Most url's I can use the {% url %} and FORCE_SCRIPT_NAME to have work correctly, but for the url's that I do not have an entry in urls.py to reverse() with, I'm not sure if I should just use the FORCE_SCRIPT_NAME variable in the template for them or is there a better way?
Here is how I solved the problem. Not sure if this is the best way to do it, but it will do for now.
Ensure you have FORCE_SCRIPT_NAME set to whatever your sub directory is
Created context_processors.py inside same directory as settings.py
Added context_processors.url_prefix to settings.py under CONTEXT_PROCESSORS
Anywhere in a template that you cannot use the {% url %} tag, just prepend with {{ URL_PREFIX }}
context_processors.py
from django.conf import settings # import the settings file
def url_prefix(request):
# return the value you want as a dictionnary. you may add multiple values in there.
return {'URL_PREFIX': settings.FORCE_SCRIPT_NAME}
settings.py
CONTEXT_PROCESSORS = (
...,
'context_processors.url_prefix',
)