forked from ungleich-public/dynamicweb
159 lines
6.1 KiB
Python
159 lines
6.1 KiB
Python
import json
|
|
import re
|
|
|
|
from django.conf import settings
|
|
from django.http import HttpResponse
|
|
from django.urls import set_urlconf
|
|
from django.utils.cache import patch_vary_headers
|
|
from django.utils.deprecation import MiddlewareMixin
|
|
from urllib import parse as urlparse
|
|
|
|
from cms.utils.apphook_reload import reload_urlconf
|
|
|
|
|
|
class CMSMultiSiteMiddleware(MiddlewareMixin):
|
|
"""
|
|
Picks the urlconf for the requested host (MULTISITE_CMS_URLS, with
|
|
MULTISITE_CMS_ALIASES and MULTISITE_CMS_FALLBACK). Derived from
|
|
djangocms-multisite (BSD, Copyright (c) 2013, Iacopo Spalletti), whose
|
|
ungleich fork is no longer installed.
|
|
"""
|
|
|
|
def process_request(self, request):
|
|
urls = getattr(settings, 'MULTISITE_CMS_URLS', {})
|
|
aliases = getattr(settings, 'MULTISITE_CMS_ALIASES', {})
|
|
fallback = getattr(settings, 'MULTISITE_CMS_FALLBACK', '')
|
|
host = urlparse.urlparse(
|
|
request.build_absolute_uri()).hostname.split(':')[0]
|
|
urlconf = urls.get(host)
|
|
if not urlconf:
|
|
for domain, hosts in aliases.items():
|
|
if host in hosts and domain in urls:
|
|
urlconf = urls[domain]
|
|
break
|
|
if not urlconf and fallback in urls:
|
|
urlconf = urls[fallback]
|
|
if urlconf:
|
|
request.urlconf = urlconf
|
|
# for code that does not know about the request (get_absolute_url())
|
|
set_urlconf(urlconf)
|
|
reload_urlconf()
|
|
|
|
def process_response(self, request, response):
|
|
patch_vary_headers(response, ('Host',))
|
|
# back to the default urlconf
|
|
set_urlconf(None)
|
|
return response
|
|
|
|
|
|
class MultipleProxyMiddleware(MiddlewareMixin):
|
|
FORWARDED_FOR_FIELDS = [
|
|
'HTTP_X_FORWARDED_FOR',
|
|
'HTTP_X_FORWARDED_HOST',
|
|
'HTTP_X_FORWARDED_SERVER',
|
|
]
|
|
|
|
def process_request(self, request):
|
|
"""
|
|
Rewrites the proxy headers so that only the most
|
|
recent proxy is used.
|
|
"""
|
|
for field in self.FORWARDED_FOR_FIELDS:
|
|
if field in request.META:
|
|
if ',' in request.META[field]:
|
|
parts = request.META[field].split(',')
|
|
request.META[field] = parts[-1].strip()
|
|
|
|
|
|
class DevSiteSwitcherMiddleware(MiddlewareMixin):
|
|
"""
|
|
Dev/test only (enabled from settings/local.py, i.e. DEBUG=True).
|
|
|
|
The project picks a site from the request's Host header. To test every
|
|
site from one server without DNS or /etc/hosts entries, the site is
|
|
chosen, in this order, from:
|
|
|
|
1. the host: "<domain>.<anything>", e.g. digitalglarus.ch.localhost:8000
|
|
or digitalglarus.ch.203.0.113.5.sslip.io:8000. Every site is then its
|
|
own browser origin, so tabs and cookies never interfere. Preferred.
|
|
2. ?site=<domain>, remembered in a cookie ("?site=" clears it). Redirects
|
|
keep the parameter so the choice survives them.
|
|
3. the cookie set by 2.
|
|
|
|
/dev-sites/ lists every configured site as a link.
|
|
"""
|
|
COOKIE = 'dev_site'
|
|
|
|
def domains(self):
|
|
try:
|
|
found = [d for d in json.loads(settings.UNGLEICH_SITE_CONFIGS)
|
|
if ':' not in d]
|
|
except ValueError:
|
|
found = []
|
|
return sorted(found, key=len, reverse=True)
|
|
|
|
def site_from_host(self, request):
|
|
host = request.get_host().split(':')[0].lower()
|
|
for domain in self.domains():
|
|
if host == domain or host.startswith(domain + '.'):
|
|
return domain
|
|
return ''
|
|
|
|
def process_request(self, request):
|
|
if request.path == '/dev-sites/':
|
|
return self.sites_page(request)
|
|
site = self.site_from_host(request)
|
|
if not site:
|
|
if 'site' in request.GET:
|
|
site = request.GET['site'].strip()
|
|
else:
|
|
site = request.COOKIES.get(self.COOKIE, '')
|
|
request.META['DEV_SITE_SWITCH'] = site
|
|
if site:
|
|
request.META['HTTP_HOST'] = site
|
|
request.META.pop('HTTP_X_FORWARDED_HOST', None)
|
|
|
|
def process_response(self, request, response):
|
|
site = request.META.get('DEV_SITE_SWITCH', '')
|
|
# dev only: never let a browser reuse a page or redirect from another
|
|
# site, and show which site the server picked (see browser dev tools)
|
|
response['Cache-Control'] = 'no-store'
|
|
response['X-Dev-Site'] = site or '(default)'
|
|
if 'site' not in request.GET:
|
|
return response
|
|
if site:
|
|
response.set_cookie(self.COOKIE, site)
|
|
location = response.get('Location', '')
|
|
if response.status_code in (301, 302) and location.startswith('/') \
|
|
and 'site=' not in location:
|
|
response['Location'] = location + (
|
|
'&' if '?' in location else '?') + 'site=' + site
|
|
else:
|
|
response.delete_cookie(self.COOKIE)
|
|
return response
|
|
|
|
def sites_page(self, request):
|
|
"""A page listing every configured site as a link, on any host"""
|
|
host, _, port = request.get_host().partition(':')
|
|
port = ':' + port if port else ''
|
|
# opened on a site's own address (e.g. blog.ungleich.ch.localhost):
|
|
# drop that site's prefix so links are not nested
|
|
for domain in self.domains():
|
|
if host.lower().startswith(domain + '.'):
|
|
host = host[len(domain) + 1:]
|
|
break
|
|
if re.match(r'^\d+\.\d+\.\d+\.\d+$', host):
|
|
# an IP has no subdomains; sslip.io resolves <anything>.<ip>.sslip.io
|
|
base = host + '.sslip.io'
|
|
else:
|
|
base = host
|
|
items = ''.join(
|
|
'<li><a href="http://{0}.{1}{2}/">{0}</a></li>'.format(
|
|
d, base, port) for d in sorted(self.domains()))
|
|
return HttpResponse(
|
|
'<!doctype html><title>Test sites</title>'
|
|
'<h1>Test sites</h1><p>Each site opens on its own address, so '
|
|
'you can open several at once.</p><ul>{}</ul>'
|
|
'<p>Fallback without wildcard names: '
|
|
'<code>/?site=<domain></code> (<a href="/?site=">clear</a>)'
|
|
'</p>'.format(items))
|