mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-11-04 01:12:08 +01:00 
			
		
		
		
	Use a middleware rather than inspect the stack to get current user and IP
This commit is contained in:
		
							
								
								
									
										55
									
								
								apps/logs/middlewares.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								apps/logs/middlewares.py
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
				
			||||||
@@ -1,48 +1,15 @@
 | 
				
			|||||||
# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
					# Copyright (C) 2018-2020 by BDE ENS Paris-Saclay
 | 
				
			||||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
					# SPDX-License-Identifier: GPL-3.0-or-later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import inspect
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.contrib.contenttypes.models import ContentType
 | 
					from django.contrib.contenttypes.models import ContentType
 | 
				
			||||||
from django.core import serializers
 | 
					from django.core import serializers
 | 
				
			||||||
from django.db.models.signals import pre_save, post_save, post_delete
 | 
					from django.db.models.signals import pre_save, post_save, post_delete
 | 
				
			||||||
from django.dispatch import receiver
 | 
					from django.dispatch import receiver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from .middlewares import get_current_authenticated_user, get_current_ip
 | 
				
			||||||
from .models import Changelog
 | 
					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 = [
 | 
					EXCLUDED = [
 | 
				
			||||||
    'admin.logentry',
 | 
					    'admin.logentry',
 | 
				
			||||||
    'authtoken.token',
 | 
					    'authtoken.token',
 | 
				
			||||||
@@ -75,13 +42,11 @@ def save_object(sender, instance, **kwargs):
 | 
				
			|||||||
    if instance._meta.label_lower in EXCLUDED:
 | 
					    if instance._meta.label_lower in EXCLUDED:
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    print("LOGGING SOMETHING")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    previous = instance._previous
 | 
					    previous = instance._previous
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    user, ip = get_user_and_ip(sender)
 | 
					    user, ip = get_current_authenticated_user(), get_current_ip()
 | 
				
			||||||
 | 
					 | 
				
			||||||
    from django.contrib.auth.models import AnonymousUser
 | 
					 | 
				
			||||||
    if isinstance(user, AnonymousUser):
 | 
					 | 
				
			||||||
        user = None
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if user is not None and instance._meta.label_lower == "auth.user" and previous:
 | 
					    if user is not None and instance._meta.label_lower == "auth.user" and previous:
 | 
				
			||||||
        # Don't save last login modifications
 | 
					        # Don't save last login modifications
 | 
				
			||||||
@@ -111,7 +76,7 @@ def delete_object(sender, instance, **kwargs):
 | 
				
			|||||||
    if instance._meta.label_lower in EXCLUDED:
 | 
					    if instance._meta.label_lower in EXCLUDED:
 | 
				
			||||||
        return
 | 
					        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]
 | 
					    instance_json = serializers.serialize('json', [instance, ])[1:-1]
 | 
				
			||||||
    Changelog.objects.create(user=user,
 | 
					    Changelog.objects.create(user=user,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -74,6 +74,10 @@ if "cas" in INSTALLED_APPS:
 | 
				
			|||||||
    ]
 | 
					    ]
 | 
				
			||||||
    AUTHENTICATION_BACKENDS += ('cas.backends.CASBackend',)
 | 
					    AUTHENTICATION_BACKENDS += ('cas.backends.CASBackend',)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if "logs" in INSTALLED_APPS:
 | 
				
			||||||
 | 
					    MIDDLEWARE += ('logs.middlewares.LogsMiddleware',)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if "debug_toolbar" in INSTALLED_APPS:
 | 
					if "debug_toolbar" in INSTALLED_APPS:
 | 
				
			||||||
    MIDDLEWARE.insert(1,"debug_toolbar.middleware.DebugToolbarMiddleware")
 | 
					    MIDDLEWARE.insert(1,"debug_toolbar.middleware.DebugToolbarMiddleware")
 | 
				
			||||||
    INTERNAL_IPS = [ '127.0.0.1']
 | 
					    INTERNAL_IPS = [ '127.0.0.1']
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user