django-auditlog/auditlog/context.py
Alieh Rymašeŭski 47a268eef9 Clean up project structure
Cherry-picking ee8a700b1b
2020-11-12 16:15:05 +03:00

55 lines
1.6 KiB
Python

import contextlib
from functools import partial
import time
import threading
from django.contrib.auth import get_user_model
from django.db.models.signals import pre_save
from auditlog.models import LogEntry
threadlocal = threading.local()
@contextlib.contextmanager
def set_actor(actor, remote_addr=None):
"""Connect a signal receiver with current user attached."""
# Initialize thread local storage
threadlocal.auditlog = {
'signal_duid': ('set_actor', time.time()),
'remote_addr': remote_addr,
}
# Connect signal for automatic logging
set_actor = partial(_set_actor, user=actor, signal_duid=threadlocal.auditlog['signal_duid'])
pre_save.connect(set_actor, sender=LogEntry, dispatch_uid=threadlocal.auditlog['signal_duid'], weak=False)
try:
yield
finally:
try:
auditlog = threadlocal.auditlog
except AttributeError:
pass
else:
pre_save.disconnect(sender=LogEntry, dispatch_uid=auditlog['signal_duid'])
def _set_actor(user, sender, instance, signal_duid, **kwargs):
"""Signal receiver with an extra 'user' kwarg.
This function becomes a valid signal receiver when it is curried with the actor.
"""
try:
auditlog = threadlocal.auditlog
except AttributeError:
pass
else:
if signal_duid != auditlog['signal_duid']:
return
auth_user_model = get_user_model()
if sender == LogEntry and isinstance(user, auth_user_model) and instance.actor is None:
instance.actor = user
instance.remote_addr = auditlog['remote_addr']