From 20e2d41563a1f38c4c36b82a434080b3666d5148 Mon Sep 17 00:00:00 2001 From: Yohann D'ANELLO Date: Tue, 10 Mar 2020 00:01:40 +0100 Subject: [PATCH] Use a middleware rather than inspect the stack to get current user and IP --- apps/logs/middlewares.py | 55 ++++++++++++++++++++++++++++++++++ apps/logs/signals.py | 45 ++++------------------------ note_kfet/settings/__init__.py | 6 +++- 3 files changed, 65 insertions(+), 41 deletions(-) create mode 100644 apps/logs/middlewares.py diff --git a/apps/logs/middlewares.py b/apps/logs/middlewares.py new file mode 100644 index 00000000..69bbef92 --- /dev/null +++ b/apps/logs/middlewares.py @@ -0,0 +1,55 @@ +# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from django.conf import settings +from django.contrib.auth.models import AnonymousUser +from threading import local + + +USER_ATTR_NAME = getattr(settings, 'LOCAL_USER_ATTR_NAME', '_current_user') +IP_ATTR_NAME = getattr(settings, 'LOCAL_IP_ATTR_NAME', '_current_ip') + +_thread_locals = local() + + +def _set_current_user_and_ip(user=None, ip=None): + """ + Sets current user in local thread. + Can be used as a hook e.g. for shell jobs (when request object is not + available). + """ + setattr(_thread_locals, USER_ATTR_NAME, user) + setattr(_thread_locals, IP_ATTR_NAME, ip) + + +def get_current_user(): + return getattr(_thread_locals, USER_ATTR_NAME, None) + + +def get_current_ip(): + return getattr(_thread_locals, IP_ATTR_NAME, None) + + +def get_current_authenticated_user(): + current_user = get_current_user() + if isinstance(current_user, AnonymousUser): + return None + return current_user + + +class LogsMiddleware(object): + def __init__(self, get_response): + self.get_response = get_response + + def __call__(self, request): + user = request.user + if 'HTTP_X_FORWARDED_FOR' in request.META: + ip = request.META.get('HTTP_X_FORWARDED_FOR') + else: + ip = request.META.get('REMOTE_ADDR') + + _set_current_user_and_ip(user, ip) + + response = self.get_response(request) + + return response diff --git a/apps/logs/signals.py b/apps/logs/signals.py index 415e7c1c..e4e47e18 100644 --- a/apps/logs/signals.py +++ b/apps/logs/signals.py @@ -1,48 +1,15 @@ # Copyright (C) 2018-2020 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -import inspect - from django.contrib.contenttypes.models import ContentType from django.core import serializers from django.db.models.signals import pre_save, post_save, post_delete from django.dispatch import receiver +from .middlewares import get_current_authenticated_user, get_current_ip from .models import Changelog -def get_request_in_signal(sender): - req = None - for entry in reversed(inspect.stack()): - try: - req = entry[0].f_locals['request'] - # Check if there is a user - # noinspection PyStatementEffect - req.user - break - except: - pass - - if not req: - print("WARNING: Attempt to save " + str(sender) + " with no user") - - return req - - -def get_user_and_ip(sender): - req = get_request_in_signal(sender) - try: - user = req.user - if 'HTTP_X_FORWARDED_FOR' in req.META: - ip = req.META.get('HTTP_X_FORWARDED_FOR') - else: - ip = req.META.get('REMOTE_ADDR') - except: - user = None - ip = None - return user, ip - - EXCLUDED = [ 'admin.logentry', 'authtoken.token', @@ -75,13 +42,11 @@ def save_object(sender, instance, **kwargs): if instance._meta.label_lower in EXCLUDED: return + print("LOGGING SOMETHING") + previous = instance._previous - user, ip = get_user_and_ip(sender) - - from django.contrib.auth.models import AnonymousUser - if isinstance(user, AnonymousUser): - user = None + user, ip = get_current_authenticated_user(), get_current_ip() if user is not None and instance._meta.label_lower == "auth.user" and previous: # Don't save last login modifications @@ -111,7 +76,7 @@ def delete_object(sender, instance, **kwargs): if instance._meta.label_lower in EXCLUDED: return - user, ip = get_user_and_ip(sender) + user, ip = get_current_authenticated_user(), get_current_ip() instance_json = serializers.serialize('json', [instance, ])[1:-1] Changelog.objects.create(user=user, diff --git a/note_kfet/settings/__init__.py b/note_kfet/settings/__init__.py index c1df7477..b370a430 100644 --- a/note_kfet/settings/__init__.py +++ b/note_kfet/settings/__init__.py @@ -73,7 +73,11 @@ if "cas" in INSTALLED_APPS: 'cas_explained', ] AUTHENTICATION_BACKENDS += ('cas.backends.CASBackend',) - + + +if "logs" in INSTALLED_APPS: + MIDDLEWARE += ('logs.middlewares.LogsMiddleware',) + if "debug_toolbar" in INSTALLED_APPS: MIDDLEWARE.insert(1,"debug_toolbar.middleware.DebugToolbarMiddleware") INTERNAL_IPS = [ '127.0.0.1']