django-auditlog/auditlog/middleware.py

98 lines
3.3 KiB
Python
Raw Normal View History

import threading
2014-02-07 19:59:16 +00:00
import time
from functools import partial
2014-02-07 19:59:16 +00:00
from django.apps import apps
from django.conf import settings
2013-10-20 13:25:48 +00:00
from django.db.models.signals import pre_save
from django.utils.deprecation import MiddlewareMixin
2016-08-17 20:45:02 +00:00
from auditlog.models import LogEntry
2013-10-20 13:25:48 +00:00
threadlocal = threading.local()
2016-08-17 20:45:02 +00:00
class AuditlogMiddleware(MiddlewareMixin):
2013-10-20 13:25:48 +00:00
"""
Middleware to couple the request's user to log items. This is accomplished by currying the signal receiver with the
user from the request (or None if the user is not authenticated).
"""
def process_request(self, request):
2013-11-06 19:48:16 +00:00
"""
Gets the current user from the request and prepares and connects a signal receiver with the user already
attached to it.
"""
# Initialize thread local storage
threadlocal.auditlog = {
2020-12-06 20:29:24 +00:00
"signal_duid": (self.__class__, time.time()),
"remote_addr": request.META.get("REMOTE_ADDR"),
}
# In case of proxy, set 'original' address
2020-12-06 20:29:24 +00:00
if request.META.get("HTTP_X_FORWARDED_FOR"):
threadlocal.auditlog["remote_addr"] = request.META.get(
"HTTP_X_FORWARDED_FOR"
).split(",")[0]
# Connect signal for automatic logging
2020-12-06 20:29:24 +00:00
if hasattr(request, "user") and getattr(
request.user, "is_authenticated", False
):
set_actor = partial(
self.set_actor,
user=request.user,
signal_duid=threadlocal.auditlog["signal_duid"],
)
pre_save.connect(
set_actor,
sender=LogEntry,
dispatch_uid=threadlocal.auditlog["signal_duid"],
weak=False,
)
2013-10-20 13:25:48 +00:00
def process_response(self, request, response):
2013-11-06 19:48:16 +00:00
"""
Disconnects the signal receiver to prevent it from staying active.
"""
2020-12-06 20:29:24 +00:00
if hasattr(threadlocal, "auditlog"):
pre_save.disconnect(
sender=LogEntry, dispatch_uid=threadlocal.auditlog["signal_duid"]
)
2014-02-07 19:59:16 +00:00
return response
def process_exception(self, request, exception):
"""
Disconnects the signal receiver to prevent it from staying active in case of an exception.
"""
2020-12-06 20:29:24 +00:00
if hasattr(threadlocal, "auditlog"):
pre_save.disconnect(
sender=LogEntry, dispatch_uid=threadlocal.auditlog["signal_duid"]
)
2013-11-06 19:48:16 +00:00
2014-02-07 20:02:03 +00:00
return None
2013-10-20 13:25:48 +00:00
@staticmethod
2016-11-02 13:30:05 +00:00
def set_actor(user, sender, instance, signal_duid, **kwargs):
2013-11-06 19:48:16 +00:00
"""
Signal receiver with an extra, required 'user' kwarg. This method becomes a real (valid) signal receiver when
it is curried with the actor.
"""
2020-12-06 20:29:24 +00:00
if hasattr(threadlocal, "auditlog"):
if signal_duid != threadlocal.auditlog["signal_duid"]:
return
try:
2020-12-06 20:29:24 +00:00
app_label, model_name = settings.AUTH_USER_MODEL.split(".")
auth_user_model = apps.get_model(app_label, model_name)
except ValueError:
2020-12-06 20:29:24 +00:00
auth_user_model = apps.get_model("auth", "user")
if (
sender == LogEntry
and isinstance(user, auth_user_model)
and instance.actor is None
):
instance.actor = user
2020-12-06 20:29:24 +00:00
instance.remote_addr = threadlocal.auditlog["remote_addr"]