mirror of
https://github.com/Hopiu/django-tos.git
synced 2026-03-16 20:10:24 +00:00
Use reverse_lazy instead of reverse, as reverse('tos_check_tos') can return the URL in a wrong language (the language the current thread is in, instead of the language of the users request) in combination with i18n.
96 lines
3.4 KiB
Python
96 lines
3.4 KiB
Python
from django.conf import settings
|
|
from django.contrib.auth import SESSION_KEY as session_key
|
|
from django.contrib.auth import REDIRECT_FIELD_NAME
|
|
from django.core.cache import caches
|
|
from django.http import HttpResponseRedirect
|
|
from django.urls import reverse_lazy
|
|
from django.utils.cache import add_never_cache_headers
|
|
from django.utils.deprecation import MiddlewareMixin
|
|
|
|
from .models import UserAgreement
|
|
|
|
|
|
cache = caches[getattr(settings, 'TOS_CACHE_NAME', 'default')]
|
|
tos_check_url = reverse_lazy('tos_check_tos')
|
|
|
|
|
|
class UserAgreementMiddleware(MiddlewareMixin):
|
|
"""
|
|
Some middleware to check if users have agreed to the latest TOS
|
|
"""
|
|
|
|
def __init__(self, get_response=None):
|
|
self.get_response = get_response
|
|
|
|
def process_request(self, request):
|
|
if self.should_fast_skip(request):
|
|
return None
|
|
|
|
# Grab the user ID from the session so we avoid hitting the database
|
|
# for the user object.
|
|
# NOTE: We use the user ID because it's not user-settable and it won't
|
|
# ever change (usernames and email addresses can change)
|
|
user_id = request.session['_auth_user_id']
|
|
|
|
# Get the cache prefix
|
|
key_version = cache.get('django:tos:key_version')
|
|
|
|
# Skip if the user is allowed to skip - for instance, if the user is an
|
|
# admin or a staff member
|
|
if cache.get(f'django:tos:skip_tos_check:{user_id}', False, version=key_version):
|
|
return None
|
|
|
|
# Ping the cache for the user agreement
|
|
user_agreed = cache.get(f'django:tos:agreed:{user_id}', None, version=key_version)
|
|
|
|
# If the cache is missing this user
|
|
if user_agreed is None:
|
|
# Check the database and cache the result
|
|
user_agreed = self.get_and_cache_agreement_from_db(user_id, key_version)
|
|
|
|
if not user_agreed:
|
|
# Confirm view uses these session keys. Non-middleware flow sets them in login view,
|
|
# so we need to set them here.
|
|
request.session['tos_user'] = user_id
|
|
request.session['tos_backend'] = request.session['_auth_user_backend']
|
|
|
|
response = HttpResponseRedirect('{0}?{1}={2}'.format(
|
|
tos_check_url,
|
|
REDIRECT_FIELD_NAME,
|
|
request.path_info,
|
|
))
|
|
add_never_cache_headers(response)
|
|
return response
|
|
|
|
return None
|
|
|
|
def should_fast_skip(self, request):
|
|
'''Check if we should skip TOS checks without hitting the cache or database'''
|
|
# Don't get in the way of any mutating requests
|
|
if request.method != 'GET':
|
|
return True
|
|
|
|
# Ignore ajax requests
|
|
if request.META.get('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest':
|
|
return True
|
|
|
|
# Don't redirect users when they're trying to get to the confirm page
|
|
if request.path_info == tos_check_url:
|
|
return True
|
|
|
|
# If the user doesn't have a user ID, ignore them - they're anonymous
|
|
if not request.session.get(session_key, None):
|
|
return True
|
|
|
|
return False
|
|
|
|
def get_and_cache_agreement_from_db(self, user_id, key_version):
|
|
# Grab the data from the database
|
|
user_agreed = UserAgreement.objects.filter(
|
|
user__id=user_id,
|
|
terms_of_service__active=True).exists()
|
|
|
|
# Set the value in the cache
|
|
cache.set(f'django:tos:agreed:{user_id}', user_agreed, version=key_version)
|
|
|
|
return user_agreed
|