Override django get_current_site - Work with subdomain - django

I have a Django project with Sites enabled.
This project will handle multiple domains and multiple sub-domains.
I have domain.com in Sites table, but when I access sub.domain.com/admin, I get the error Site matching query does not exist. If I add sub.domain.com to Sites table, everything works fine.
Site.objects.create(domain='domain.com', name='Test1')
# Site.objects.create(domain='sub.domain.com', name='Test2')
The problem is that get_current_site(request) does not consider sub-domains and I don't want to create several instances in Sites in order to represent every single sub-domain and domain.
Is there any way I can override get_current_site(request)? I've already built my version but Django itself calls the original one when accessing Django admin and that's a problem.
def get_current_site(request: HttpRequest = None):
from django.contrib.sites.models import Site
host = request.get_host()
extract = tldextract.extract(host)
domain = extract.registered_domain # removes subdomain
return Site.objects.get(domain__iexact=domain)

Related

DRF - How do I whitelist a site for endpoint requests

This is my scenario:
Hosted Django application running DRF
External site hosted elsewhere. Could be multiple sites.
My question is: How do I whitelist any Javascript POST request from an external site? For example I want to add a Javascript post request on a forms confirmation page. How do I do this so my DRF application only accepts requests from this site only? I'm trying not to use server site code. Just Javascript so it easily applied.
My concern is anyone that views the source will be able to view the API endpoint and would be able to submit POST requests.
You can implement a whitelist by creating a custom permissions class, here is an example from the docs.
from rest_framework import permissions
class BlacklistPermission(permissions.BasePermission):
"""
Global permission check for blacklisted IPs.
"""
def has_permission(self, request, view):
ip_addr = request.META['REMOTE_ADDR']
blacklisted = Blacklist.objects.filter(ip_addr=ip_addr).exists()
return not blacklisted
For a whitelist implementation you would want to grab the REMOTE_HOST from the incoming request and compare it against a predefined list of urls, which could be stored in the database or in a list in the class. it might look something list this:
# Model
class Whitelist(models.Model):
host = models.CharField()
# Permission
class WhitelistPermission(permissions.BasePermission):
"""
Global permission check for whitelisted IPs.
"""
def has_permission(self, request, view):
domain = request.META['REMOTE_HOST']
whitelisted = Whitelist.objects.filter(host=domain).exists()
return whitelisted
Or you could whitelist based off IP be replacing REMOTE_HOST with REMOTE_ADDR
Maybe you can configure this in your web server. So that request is blocked at the entry level itself.
If you're using nginx then something like this https://support.hypernode.com/knowledgebase/blocking-allowing-ip-addresses-in-nginx/

Example.com in django password reset

I have set up my django project to enable password reset but when the password reset mail is sent, i get the line https://example.com/accounts/reset/Mg/... . Exampple.com is not in any way related to my site. I have tried to remove it such that it reads my site url but to no avail
For future reference, django contrib auth uses current site domain provided by Site model in django.contrib.sites.models.
To check which one do U have configured, enter into the shell with the desired settings and type:
from django.contrib.sites.models import Site
current_site = Site.objects.get_current()
print(current_site.domain)
This will show what domain will be used to construct absolute links like in this case, to reset the password.
Check admin. You can see site domain in 'Sites'. Default of the site domain is 'example.com', so if you change the site domain to your domain, you can resolve it.

Restrict urls on certain apps in Django

I've looked for an approach to add specific urls to a certain app. And restrict the urls for being used on other apps in Django.
I use Mezzanine and when a user goes to sub.domain.com he will see templates that are specific to that site. But when the user tries to go to the url on sub.domain.com/example he will see the url that is intended to be on domain.com/example. I want that to be a 404 for the user instead on the app.
Sorry for my bad english, hope you understand what I'm talking about.
You can get the current domain name (that the user is accessing) from request.META['HTTP_HOST']. Then based on that, raise 404 when the url is visited on certain domains.
from django.http import Http404
def my_restricted_view(request):
domain = request.META['HTTP_HOST']
if domain == 'sub.domain.com':
return render(request, "template_name", {})
else:
raise Http404

how can I configure my app to only accept connections from three explicitly-specified clients?

So I have a Django app that serves as the backend for another app that I've written. I only want my Django app to be accessed from this other app, which will probably have a handful of versions (production, staging, dev1, dev2). How can I configure my Django app to only accept connections from these handful of clients?
If I understand you well you want to control the access to your Django App, one option is to add a custom Middleware to check the IP of the user and if it's not allowed you can show an error, or redirect to other site or whatever you want.
class CheckIPMiddleware(object):
# Check if client IP is allowed
def process_request(self, request):
allowed_ips = ['192.168.1.1', '123.123.123.123', etc...]
ip = request.META.get('REMOTE_ADDR') # Get client IP
if ip not in allowed_ips:
# Here you can raise a 403 Forbidden
# or redirect to any other site/page
# If user is allowed nothing happens
return None
If this is useful to you you have to remember 2 things:
Add code to a file following this path: your_project/middleware/checkipmiddleware.py
Edit your settings and add your_project_name.middleware.checkipmiddleware.CheckIPMiddleware into your middleware ( look for MIDDLEWARE_CLASSES = (... in your settings.py )

django multiple domains, single app

I am trying to set my Django app work with multiple domains (while serving slightly different content)
I wrote this middleware:
class MultiSiteMiddleware(object):
def process_request(self, request):
host = request.get_host()
host_part = host.split(':')[0].split('.com')[0].split('.')
host = host_part[len(host_part)-1] + '.com'
site = Site.objects.get(domain=host)
settings.SITE_ID = site.id
settings.CURRENT_HOST = host
Site.objects.clear_cache()
return
In views I use this:
def get_site(request):
current_site = get_current_site(request)
return current_site.name
def view(request, pk):
site = get_site(request)
if site == 'site1':
# serve content1
...
elif site == 'site2'
# serve content2
...
But now there are 404 errors (I sometimes find them in logs, don't see them while browsing my site manually) where they aren't supposed to be, like my site sometimes is serving content for wrong domains, can they happen because of some flaw in the above middleware and view code or I should look somewhere else?
I had a similar requirement and decided not to use the django sites framework. My middleware looks like
class MultiSiteMiddleware(object):
def process_request(self, request):
try:
domain = request.get_host().split(":")[0]
request.site = Site.objects.get(domain=domain)
except Site.DoesNotExist:
return http.HttpResponseNotFound()
then my views have access to request.site
If you're seeing 404's for sites that aren't yours in your logs it would seem like somebody has pointed their domain at your servers IP address, you could use apache/nginx to filter these out before they hit your app, but your middleware should catch them (though possibly by raising an uncaught 500 error instead of a 404)
Serve multiple domain from one website (django1.8 python3+)
The goal is, obviously, to serve multiple domains, from one Django instance. That means, we use the same models, the same database, the same logic, the same views, the same templates, but to serve different things.
Searching the interwebs, I came to the idea of using the sites framework. The sites framework was designed to do exactly that thing. In fact, the sites framework has been used to do exactly that. But I haven't been able to know on which version of Django it was, and actually, I came to the idea the sites framework was just a vestigial module. Basically, it is just a table, with SITE_ID and SITE_URL, that you can easily access with a function. But I've been unable to find how, from that, you can do a multi-domain website.
So my idea has been, how to modify the url resolver. The idea behind all these is easy : www.domain1.com/bar is resolved to /domain1/bar, and www.domain2.foo is resolved to /domain2/foo. This solves the problem because, if you want to serve multiple domains, you just have to serve multiple folders.
In Django, to achieve this, you have to modify two things :
* the way Django routes requests
* the way django write urls
The way Django routes requests
Django routes requests with middlewares. That's it. So we just have to write a middleware that re-route requests.
To make it easier, middlewares can have a process_request method that process requests (WOW), before requests are handled. So let's write a DomainNameMiddleware
#!python
#app/middleware.py
class DomaineNameMiddleware:
"""
change the request path to add the domain_name at the first
"""
def process_request(self, request):
#first, we split the domain name, and take the part before the extension
request_domain = request.META['HTTP_HOST'].split('.')[-2]
request.path_info = "/%s/%s" % (request_domain, request.path.split('/')[1:])
The way Django writes URL
When I'm talking about django writing url, i'm principally thinking of the {% url %} template tag, the get_absolute_url methods, the resolve and resolve_lazy functions, and those basic Django thing. If we rewrite the way Django handle url, we have to tell Django to write url that way.
But basically it's pretty easy, thanks to Django.
You can easily rewrite basic Django function by just rewriting them, typically in init.py files of modules you added as apps. So :
#!python
#anyapp/__init__.py
from django.core import urlresolvers
old_reverse = urlresolvers.reverse
def new_reverse(viewname, urlconf=None, args=None, kwargs=None, current_app=None):
"""
return an url with the first folder as a domain name in .com
"""
TLD = 'com'
old_reverse_url = old_reverse(viewname, urlconf, args, kwargs, current_app)
# admin will add itself everytime you reload an admin page, so we have to delete it
if current_app == 'admin':
return '/%s' % old_reverse_url[len('admin'):].replace('adminadmin', 'admin')
return '//%s.%s/%s' % (app, TLD, path)
How to use it ?
I use it with base urls.py as dispatchedr.
#!python
#urls.py
from django.conf.urls import include, url
from domain1 import urls as domain1_urls
from domain2 import urls as domain2_urls
urlpatterns = [
url(r'^domain1/', include(domain1_urls, namespace='domain1')),
url(r'^domain2/', include(domain2_urls, namespace='domain2)),
]
Django's Sites framework has built-in middleware to accomplish this.
Simply enable the Sites framework and add this to your MIDDLEWARE:
'django.contrib.sites.middleware.CurrentSiteMiddleware'
This automatically passes a request object to Site.objects.get_current() on every request. It solves your problem by giving you access to request.site on every request.
For reference, the code as of 1.11 is:
from django.utils.deprecation import MiddlewareMixin
from .shortcuts import get_current_site
class CurrentSiteMiddleware(MiddlewareMixin):
"""
Middleware that sets `site` attribute to request object.
"""
def process_request(self, request):
request.site = get_current_site(request)
I will suggest you to use django-multisite .It will fulfill your requirement.
Try using the "sites" framework in django to get the domain name. You already know this I guess.
Take a look here: https://docs.djangoproject.com/en/1.7/ref/contrib/sites/#getting-the-current-domain-for-full-urls
See this:
>>> Site.objects.get_current().domain
'example.com'
Without the https://www or http://www.. Probably your domains will end in .org or some country .pe .ru etc not just .com.
There might be a case when people don't point to your domain but to your IP address for some reason, maybe development of testing so you should always raise an exception with Site.DoesNotExist