Django Middleware that redirects to https - django

I'm trying to build some middleware in my Django project that redirects the user to a 'https' link if their request isn't initially to https. The code below is not redirecting the user under any of the tests that I've run (i.e. user enters 'www.example.com', 'http://example.com,', http://www.example.com', etc.
Can any of you spot the problem with this code? Normally, I'd use print statements to see what the path is being set to, but I can't do that on my live server (or at least I don't know how to):
from django.http import HttpResponseRedirect
from django.utils.deprecation import MiddlewareMixin
class RedirectMiddleware(MiddlewareMixin):
def process_request(self, request):
host = request.get_host()
if host == 'www.example.com' or 'example.com':
path = request.build_absolute_uri()
domain_parts = path.split('.')
if domain_parts[0] == 'http://www':
path = path.replace("http://www","https://www")
return HttpResponseRedirect(path)
elif domain_parts[0] == 'www':
path = path.replace("www","https://www")
return HttpResponseRedirect(path)
elif domain_parts[0] == 'http://example':
path = path.replace("http","https")
return HttpResponseRedirect(path)
elif domain_parts[0] == 'example':
path = path.replace("example","https://www.example")
return HttpResponseRedirect(path)
else:
pass
else:
pass
Thanks again guys

You can add the following to your settings file
SECURE_SSL_REDIRECT = True
See https://docs.djangoproject.com/en/2.1/ref/middleware/#ssl-redirect
You'll also need to add the following to your middlewares
'django.middleware.security.SecurityMiddleware',
It's a settings available from Django 1.8+

Related

Check url status without opening it

At Now when url is opened (without a slash - example.com/blog), a slash is automatically added at the end (there are 301 redirects). The question is, can I somehow do it so that the check first goes to see if the page exists (without a slash - example.com/blog). If so, open it. If not, then check whether the page exists with a slash (only without 301 - example.com/blog/). If so, then redirect 301, and if not, then throw 404.
Now just if there is no page (example.com/blog), then a slash is added to the end first (example.com/blog/), 301 redirects go and only then a 404 error is thrown. In this case, the 404 error must be thrown immediately, without a 301 redirect.
The dispatch was rewritten as follows.
def is_normal_slash_count(url):
temp_url = url
slash_count = 0
while temp_url.endswith('/'):
slash_count += 1
temp_url = temp_url[:-1]
return (slash_count == 1, slash_count)
def replace_bad_slash(url, slash_count):
if slash_count == 2:
return url.replace('//', '/')
return url.replace('/'*(slash_count-1), '')
def normalize_url(url):
if len(url) > 1:
if not url.endswith('/'):
return url + '/'
# replace the url like /contacts//// to /contacts/
good_slash, slash_count = is_normal_slash_count(url)
if not good_slash:
url = replace_bad_slash(url, slash_count)
return url
def is_bad_url(url):
if len(url) > 1:
good_slash, slash_count = is_normal_slash_count(url)
if not good_slash:
return True
return False
class RedirectMixinView:
def dispatch(self, *args, **kwargs):
url = self.request.path
redirect_setting = RedirectSettings.objects.filter(url_from=url).first()
if redirect_setting:
return redirect(redirect_setting.url_to, permanent=True)
if is_bad_url(url):
return redirect(normalize_url(url), permanent=True)
return super(RedirectMixinView, self).dispatch(*args, **kwargs)
Is this realistic?
I think in the direction of writing middleware.
Updated
projects.urls
url(r'^page/', include('pages.urls')),
pages.urls
url(r'^$', PageView.as_view(), name='page'),
test
try:
resolve('/page/')
except:
raise Http404
return redirect('/page/')
I'm tried /page/, /page, page/, page, http://127.0.0.1:8000/page/, http://127.0.0.1:8000/page
You need to remove RedirectMixinView from LandingView.
Comment out the middleware CommonMiddleware.
Add RedirectMiddleware to the list of middleware (preferably in the top).
Create RedirectMiddleware
The code is written jointly with #dirkgroten (most of his contribution).
import re
from django.http import HttpResponsePermanentRedirect
class RedirectMiddleware(object):
response_redirect_class = HttpResponsePermanentRedirect
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
path = re.sub("/+", "/", request.path)
if response.status_code == 404:
if not path.endswith('/'):
request.path = path # to force using the cleaned path
else:
request.path = path[:-1] # to force using the cleaned path
try:
full_path = request.get_full_path(force_append_slash=True) # add the slash, keeping query parameters
r = resolve(full_path)
new_response = r.func(request, args=r.args, kwargs=r.kwargs)
if new_response.status_code == 200:
return redirect(full_path)
except Resolver404:
pass # this will fall through to `return response`
# Add the Content-Length header to non-streaming responses if not
# already set.
if not response.streaming and not response.has_header('Content-Length'):
response['Content-Length'] = str(len(response.content))
return response
Add to ngnx config of project
if ($request_uri ~* "\/\/") {
rewrite ^/(.*) $scheme://$host/$1 permanent;
}
# merge_slashes off;
It does what you need, and also removes duplicate slashes if this page exists.
First make sure you set APPEND_SLASH to False in your settings.py. This will disable the automatic 301 redirects to the URLs with slash.
Then use resolve() to check if the URL with slash exists before redirecting. Do this in a Middleware class where you handle the case that the response status code is 404.
from django.urls import resolve
try:
resolve(url_with_slash)
except Resolver404:
raise Http404
return redirect(url_with_slash)
Note that resolve(url) will not raise an exception when there is a path matching the url, even if the view might afterwards still raise a 404. This is the case for example if you have a DetailView for an object where the object's pk is in the URL. Say you have /objects/<pk>/ as the path to show your objects, then the url /objects/4/ will always match even if object with pk=4 does not exist. The view will still raise a 404 after the redirect.
So if you really want to also catch those 404's, you could actually call the view function yourself to check the response:
try:
r = resolve(url_with_slash)
response = r.func(request, args=r.args, kwargs=r.kwargs)
if response.status_code == 200:
return redirect(url_with_slash)
except Resolver404:
pass

django-nocaptcha-recaptcha always shows additional verification box

I installed django-nocaptcha-recaptcha and integrated it into my form:
from nocaptcha_recaptcha.fields import NoReCaptchaField
class ClientForm(forms.ModelForm):
captcha = NoReCaptchaField()
It shows up fine on the form, but whenever I click on it an additional dialog pops up asking to enter some text and verify. It happens every time. I tested it from another computer on another network and it still asks for additional verification after clicking the box.
This is what it looks like: additional verification dialog box
Here's how I'm handling the form:
#xframe_options_exempt
def registration(request):
if request.method == 'POST':
clientform = ClientForm(request.POST)
# check whether it's valid:
if clientform.is_valid():
new_client = clientform.save()
...
What am I doing wrong? Is it a problem with django-nocaptcha-recaptcha? Should I use something else?
P.S. I'm using django 1.7.1 with python 3.4
Another alternative: Minimalist and non framework dependant.
This is the code, in case you want to rewrite it.
'''
NO-CAPTCHA VERSION: 1.0
PYTHON VERSION: 3.x
'''
import json
from urllib.request import Request, urlopen
from urllib.parse import urlencode
VERIFY_SERVER = "www.google.com"
class RecaptchaResponse(object):
def __init__(self, is_valid, error_code=None):
self.is_valid = is_valid
self.error_code = error_code
def __repr__(self):
return "Recaptcha response: %s %s" % (
self.is_valid, self.error_code)
def __str__(self):
return self.__repr__()
def displayhtml(site_key, language=''):
"""Gets the HTML to display for reCAPTCHA
site_key -- The site key
language -- The language code for the widget.
"""
return """<script src="https://www.google.com/recaptcha/api.js?hl=%(LanguageCode)s" async="async" defer="defer"></script>
<div class="g-recaptcha" data-sitekey="%(SiteKey)s"></div>
""" % {
'LanguageCode': language,
'SiteKey': site_key,
}
def submit(response,
secret_key,
remote_ip,
verify_server=VERIFY_SERVER):
"""
Submits a reCAPTCHA request for verification. Returns RecaptchaResponse
for the request
response -- The value of response from the form
secret_key -- your reCAPTCHA secret key
remote_ip -- the user's ip address
"""
if not(response and len(response)):
return RecaptchaResponse(is_valid=False, error_code='incorrect-captcha-sol')
def encode_if_necessary(s):
if isinstance(s, str):
return s.encode('utf-8')
return s
params = urlencode({
'secret': encode_if_necessary(secret_key),
'remoteip': encode_if_necessary(remote_ip),
'response': encode_if_necessary(response),
})
params = params.encode('utf-8')
request = Request(
url="https://%s/recaptcha/api/siteverify" % verify_server,
data=params,
headers={
"Content-type": "application/x-www-form-urlencoded",
"User-agent": "reCAPTCHA Python"
}
)
httpresp = urlopen(request)
return_values = json.loads(httpresp.read().decode('utf-8'))
httpresp.close()
return_code = return_values['success']
if return_code:
return RecaptchaResponse(is_valid=True)
else:
return RecaptchaResponse(is_valid=False, error_code=return_values['error-codes'])
Restart the server and don't forget to clear your browser's cache. Hope this helps.

Prevent Django from blocking while proxying an HTTP request

I'm working on a Django site that allows connecting to devices in restricted networks through a cloud service. The devices connect to a cloud server through a VPN or SSH tunnel and clients connect to a virtual host via HTTP. The Django part is required for managing complex organization-role-access-user relationships.
Currently I'm doing access control in a custom Django middleware module that parses HTTP_HOST, does authentication, gets the page and forwards it to the original requester. The problem is that while a request is going on, Django is not handling any other requests. Celery does not solve the problem because this isn't really a background task. Clients are served through a single address and port, making firewall rules unsuitable for this task.
The relevant code is below:
class NodeProxyMiddleware:
def process_request(self, request, *args, **kwargs):
if not 'HTTP_HOST' in request.META:
return None
hardware_id = match_hwid.match(request.META["HTTP_HOST"])
if not hardware_id:
return None
kwargs["hardware_id"] = hardware_id.group("hwid")
if not authenticate(request, *args, **kwargs):
return HttpResponseForbidden("No access")
return proxy_request(request, *args, **kwargs)
#csrf_exempt
def proxy_request(request, *args, **kwargs):
# Get the port of target Node
hardware_id = kwargs.get("hardware_id", "")
try:
port = Node.objects.filter(hardware_id=hardware_id)[0].port
except IndexError: # Node with given hwid was not found
raise Http404
# We have to convert request.META back to original form manually
headers = convert_headers(request) # HTTP_FOO_BAR to Foo-Bar
headers["connection"] = "close"
connection = httplib2.Http(timeout=5)
url = "http://127.0.0.1:%d%s" % (port, request.META['PATH_INFO'])
method = request.method
# GET -- url ?d=a&t=a has to be urlencoded
if method == "GET":
data = None
if request.GET:
url += "?" + request.GET.urlencode()
# POST -- body has to be urlencoded
elif method == "POST":
data = request.POST.urlencode()
headers["content-type"] = "application/x-www-form-urlencoded"
try:
response, content = connection.request(
url, method, data, headers=headers)
except Exception as e:
print e
return HttpResponse(content=e, status=503)
django_response = HttpResponse(
content=content,
status=int(response["status"]),
mimetype=response["content-type"],
)
# Strip hop-by-hop headers -- See RFC2616 semantically transparent
# proxying. Also, WSGI forbids passing such headers back to it.
hop_by_hop_headers = [
"connection",
"keep-alive",
"proxy-authenticate",
"proxy-authorization",
"te",
"trailers",
"transfer-encoding",
"upgrade",
]
for key, value in response.iteritems():
if key.lower() in hop_by_hop_headers:
continue
django_response[key] = value
return django_response
Is it possible to do this kind of proxying at all in Django by tweaking the code above or other settings? The software stack I'm running on is Nginx + uWSGI + Django 1.6. The uWSGI configuration is:
[uwsgi]
chdir = /home/foo/production/
file = /home/foo/production/wsgi.py
home = /home/foo/virtualenv
master = true
processes = 8
socket = /var/nginx/foo.socket
chmod-socket = 666
vacuum = true
daemonize = /home/foo/production/uwsgi.log

Why does my redirect middleware fail?

I have a middleware that redirects mobile users to a mobile site, but I want to direct them to the the full site if the url is /property/. The mobile redirect is working, but /property/ is not being excluded.
Here is the current middleware.
middleware.py
# Adapted from http://djangosnippets.org/snippets/2001/
import re
from django.conf import settings
from django.http import HttpResponseRedirect
class MobileRedirectMiddleware(object):
"""
Redirects mobile users to a different site.
"""
def process_request(self, request):
if self._is_mobile(request):
return HttpResponseRedirect(settings.MOBILE_SITE_URL)
def _is_mobile(self, request):
is_mobile = False
NON_MOBILE_REDIRECT_URLS = getattr(settings, 'NON_MOBILE_REDIRECT_URLS', [])
if request.path in NON_MOBILE_REDIRECT_URLS:
return False
if request.META.has_key('HTTP_USER_AGENT'):
user_agent = request.META['HTTP_USER_AGENT']
# Test common mobile values.
pattern = "(up.browser|up.link|mmp|symbian|smartphone|midp|wap|phone|windows ce|pda|mobile|mini|palm|netfront)"
prog = re.compile(pattern, re.IGNORECASE)
match = prog.search(user_agent)
if match:
is_mobile = True
else:
# Nokia like test for WAP browsers.
# http://www.developershome.com/wap/xhtmlmp/xhtml_mp_tutorial.asp?page=mimeTypesFileExtension
if request.META.has_key('HTTP_ACCEPT'):
http_accept = request.META['HTTP_ACCEPT']
pattern = "application/vnd\.wap\.xhtml\+xml"
prog = re.compile(pattern, re.IGNORECASE)
match = prog.search(http_accept)
if match:
is_mobile = True
if not is_mobile:
# Now we test the user_agent from a big list.
user_agents_test = ("w3c ", "acs-", "alav", "alca", "amoi", "audi",
"avan", "benq", "bird", "blac", "blaz", "brew",
"cell", "cldc", "cmd-", "dang", "doco", "eric",
"hipt", "inno", "ipaq", "java", "jigs", "kddi",
"keji", "leno", "lg-c", "lg-d", "lg-g", "lge-",
"maui", "maxo", "midp", "mits", "mmef", "mobi",
"mot-", "moto", "mwbp", "nec-", "newt", "noki",
"xda", "palm", "pana", "pant", "phil", "play",
"port", "prox", "qwap", "sage", "sams", "sany",
"sch-", "sec-", "send", "seri", "sgh-", "shar",
"sie-", "siem", "smal", "smar", "sony", "sph-",
"symb", "t-mo", "teli", "tim-", "tosh", "tsm-",
"upg1", "upsi", "vk-v", "voda", "wap-", "wapa",
"wapi", "wapp", "wapr", "webc", "winw", "winw",
"xda-",)
test = user_agent[0:4].lower()
if test in user_agents_test:
is_mobile = True
return is_mobile
in settings.py I have this:
MOBILE_SITE_URL = 'http://mobile.somesite.com/'
NON_MOBILE_REDIRECT_URLS = ['/property/']
It may not be enough just to avoid redirecting to mobile. at the given url. If the user is already coming from mobile.somesite.com/..../, you will need to actively redirect them to www. to get away from the mobile site.
This is untested, but should be pretty close:
class MobileRedirectMiddleware(object):
"""
Redirects mobile users to a different site.
"""
def process_request(self, request):
was_mobile = settings.MOBILE_SITE_URL in request.META.HTTP_REFERER
NON_MOBILE_REDIRECT_URLS = getattr(settings, 'NON_MOBILE_REDIRECT_URLS', [])
if request.path in NON_MOBILE_REDIRECT_URLS and was_mobile:
# redirect them to 'www.somesite.com/.../'
return HttpResponseRedirect(settings.MAIN_SITE_URL + request.path.lstrip('/'))
if self._is_mobile(request):
return HttpResponseRedirect(settings.MOBILE_SITE_URL)
def _is_mobile(self, request):
is_mobile = False
# no longer need to check urls in here
if request.META.has_key('HTTP_USER_AGENT'):
...

How can I pass data to any template from any view in Django?

Like a good little coder, all of my Django templates inherit from a base.html. Now I would like to add some functionality to the base to always show some interesting things. Some user statistics, or random posts, or feeds, etc.
All of my views look like this:
def viewname(request) :
template_vales = {}
// Stuff
return render_to_response('some_file_name.html', template_values)
How can I make it so that the values of template_values are always populated for all my views? Do I have to do this at the start of all of my views? As in:
import utils
def viewname(request) :
template_values = {}
utils.addDefaults(template_values)
// Stuff
return render_to_response('some_file_name.html', template_values)
Or is there a better way?
You should use context processors:
http://docs.djangoproject.com/en/dev/ref/templates/api/
http://www.b-list.org/weblog/2006/jun/14/django-tips-template-context-processors/
In my settings.py, I add a couple of functions to the standard ones (see the last two):
TEMPLATE_CONTEXT_PROCESSORS = (
"django.core.context_processors.request",
"django.core.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"thetrailbehind.context_processors.canonical_url",
"thetrailbehind.context_processors.gmapkey",)
The first one I add defines the canonical URL for the view, and the second switches between GMap keys. Here's that function:
def gmapkey(request):
url = request.META['HTTP_HOST']
key = ""
if url == "127.0.0.1:8000":
key = "ABQIAAAAGFSvsJjnPmsGb7IcfqoamBTpH3CbXHjuCVmaTc5MkkU4wO1RRhTaJZRNQLjBhGtJlm6eE4gJtku-Rw"
elif url == "192.168.11.3:8000":
key = "ABQIAAAAGFSvsJjnPmsGb7IcfqoamBTm8-wcGRt2V-0p00qdRdGeyDhtGBSRTbk2s1ciA8vzdxGeAnqq6g-F4g"
elif url == "192.168.11.17:7000":
key="ABQIAAAAmHGaJpkZhJ6huJ93yfaYERTmT93Y0kqi8UE3J2QowoLz6rHdtxTHqeJ0nRoENl5LY5gCqHhRK9Yasg"
elif url == "192.168.1.200:8000":
key="ABQIAAAAmHGaJpkZhJ6huJ93yfaYERR5_sKpsr8Ui4YjC4HGOe8xaUDeVhSxGV1r1rIL1OvmVMAGUQBoUK0H2w"
elif url == "192.168.1.73:8000":
key = "ABQIAAAAGFSvsJjnPmsGb7IcfqoamBR7_CRKSBu49YjvDOLq_-DZQHSIYBSip9sO5IHlFIoZMtDpVcduFQCnWg"
elif url == "www.trailbehind.com":
key="ABQIAAAAGFSvsJjnPmsGb7IcfqoamBQxFGSDsNggDdRtUnAb8L8sJ910FhSKwoOpNaUlGCQIhyl6Dy5Cbyb0lQ"
elif url == "dev.trailbehind.com":
key="ABQIAAAAmHGaJpkZhJ6huJ93yfaYERQzqIbhF_xOwOwM1oDO_kQqYhag7BRsoTInq2lBuE7fsgDN2xfyD2IL5A"
elif url == "trailbehind.com":
key = "ABQIAAAAGFSvsJjnPmsGb7IcfqoamBQL9YYTGyB2pLTiscy54DOfsaXeHBQqMBmq7UvWAZVenmRMtNr_bo3TMQ"
elif url == "tenuki.trailbehind.com":
key = "ABQIAAAAGFSvsJjnPmsGb7IcfqoamBQ5SkJUKVREyqcvaNQJsRscGi2yVhSj0mJSTasDiWec8Awxb_TUxOdElw"
elif url == "cabin.trailbehind.com":
key = "ABQIAAAAmHGaJpkZhJ6huJ93yfaYERSU-76xxg1tvy-8taAiiF1qqcGi1xSmjUhmAs_v2XAuGxKX_Y-4-gDP3Q"
elif url == "ec2-174-129-167-234.compute-1.amazonaws.com":
key = "ABQIAAAAmHGaJpkZhJ6huJ93yfaYERStHq7nubctzsNDgkYc34LoSNrRNhQVCNy2KFFm2BT1sG2yrXrw38ycNg"
For this I use context processors. For example, if I want get variable MEDIA_URL for each view, I define context_processors.py like this:
def media_url(request):
from django.conf import settings
return {'MEDIA_URL': settings.MEDIA_URL}
in settings.py you must have
TEMPLATE_CONTEXT_PROCESSORS = (
....
'django.core.context_processors.request',
'myaplication.context_processors.menuitems',
)
in view you must have render_to_response and context_instance=RequestContext(request)
For example:
def my_view(request):
return render_to_response('base.html',{},
context_instance=RequestContext(request))