From 6f67b855ac19280f1c8a7d187f58ac59b592833e Mon Sep 17 00:00:00 2001 From: Alexandre Iooss Date: Thu, 23 Sep 2021 11:41:35 +0200 Subject: [PATCH] Require login if not in authorized IP range --- photo21/middleware.py | 48 +++++++++++++++++++++++++++++++++++++++ photo21/settings.py | 4 ++++ photo21/urls.py | 4 +++- photologue_custom/urls.py | 2 +- 4 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 photo21/middleware.py diff --git a/photo21/middleware.py b/photo21/middleware.py new file mode 100644 index 0000000..1c316d8 --- /dev/null +++ b/photo21/middleware.py @@ -0,0 +1,48 @@ +from django.http import HttpResponseRedirect +from django.conf import settings +import ipaddress +import re + + +class LoginRequiredMiddleware: + """ + If user is not accessing the site from an authorized IP, force + authentification. + """ + def __init__(self, get_response): + """Init middleware""" + self.get_response = get_response + self.whitelist_re = re.compile("^/accounts/.*$") + + def __call__(self, request): + """ + If user is not authenticated and external, redirect to login view + before calling the view. + """ + if not request.user.is_authenticated and not self.check_ip(request): + if not self.whitelist_re.match(request.path_info): + return HttpResponseRedirect(settings.LOGIN_URL) + + response = self.get_response(request) + return response + + def check_ip(self, request): + """ + Return true if IP is in authorized range + """ + # Get IP address + if 'HTTP_X_REAL_IP' in request.META: + ip = request.META.get('HTTP_X_REAL_IP') + elif 'HTTP_X_FORWARDED_FOR' in request.META: + ip = request.META.get('HTTP_X_FORWARDED_FOR').split(', ')[0] + else: + ip = request.META.get('REMOTE_ADDR') + ip = ipaddress.ip_address(ip) + + # Check against ranges + if hasattr(settings, 'LOGIN_EXEMPT_IP_RANGE'): + for ip_range in settings.LOGIN_EXEMPT_IP_RANGE: + net = ip_network(ip_range) + if ip in net: + return True + return False diff --git a/photo21/settings.py b/photo21/settings.py index 05c1cf3..3cdb9d1 100644 --- a/photo21/settings.py +++ b/photo21/settings.py @@ -57,6 +57,7 @@ MIDDLEWARE = [ 'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.locale.LocaleMiddleware', 'django.contrib.sites.middleware.CurrentSiteMiddleware', + 'photo21.middleware.LoginRequiredMiddleware', ] ROOT_URLCONF = 'photo21.urls' @@ -163,3 +164,6 @@ SITE_ID = 1 # Photologue PHOTOLOGUE_GALLERY_SAMPLE_SIZE = 1 + +# IP range whitelist +LOGIN_EXEMPT_IP_RANGE = ["185.230.76.0/22", "2a0c:700::/32"] diff --git a/photo21/urls.py b/photo21/urls.py index e705827..aacd159 100644 --- a/photo21/urls.py +++ b/photo21/urls.py @@ -17,13 +17,15 @@ from django.contrib import admin from django.urls import include, path from django.conf import settings from django.conf.urls.static import static +from django.contrib.auth.decorators import login_required from .views import IndexView +# photologue_custom overrides some photologue patterns urlpatterns = [ path('', IndexView.as_view(), name='index'), + path('photologue/', include('photologue_custom.urls')), path('photologue/', include('photologue.urls', namespace='photologue')), - path('photologue_custom/', include('photologue_custom.urls')), path('accounts/', include('django.contrib.auth.urls')), path('i18n/', include('django.conf.urls.i18n')), path('admin/', admin.site.urls), diff --git a/photologue_custom/urls.py b/photologue_custom/urls.py index 741b84c..939a305 100644 --- a/photologue_custom/urls.py +++ b/photologue_custom/urls.py @@ -3,5 +3,5 @@ from django.urls import path from .views import TagDetail urlpatterns = [ - path('tags//', TagDetail.as_view(), name='tag-detail'), + path('tag//', TagDetail.as_view(), name='tag-detail'), ]