130 lines
4 KiB
Python
130 lines
4 KiB
Python
import uuid
|
|
|
|
from django.conf import settings
|
|
from django.db.models.signals import post_delete, post_save
|
|
from django.dispatch import receiver
|
|
|
|
from .models import ModelChangeLog, SecurityEvent
|
|
from .signals import security_event_occurred
|
|
|
|
|
|
def _get_excluded_models():
|
|
config = getattr(settings, "INFRASYNTH_AUDIT", {})
|
|
return set(config.get("EXCLUDED_MODELS", []))
|
|
|
|
|
|
def _get_excluded_fields():
|
|
config = getattr(settings, "INFRASYNTH_AUDIT", {})
|
|
return set(config.get("EXCLUDED_FIELDS", []))
|
|
|
|
|
|
def _get_request_id(request=None):
|
|
if request:
|
|
return getattr(request, "request_id", "") or str(uuid.uuid4())[:8]
|
|
return str(uuid.uuid4())[:8]
|
|
|
|
|
|
@receiver(post_save)
|
|
def track_model_change(sender, instance, created, raw, **kwargs):
|
|
if raw:
|
|
return
|
|
label = sender._meta.label
|
|
if label in _get_excluded_models():
|
|
return
|
|
config = getattr(settings, "INFRASYNTH_AUDIT", {})
|
|
if not config.get("ENABLE_MODEL_CHANGE_TRACKING", True):
|
|
return
|
|
|
|
if created:
|
|
ModelChangeLog.objects.create(
|
|
model_label=label,
|
|
object_id=str(instance.pk),
|
|
action="create",
|
|
changes=_get_created_changes(instance),
|
|
actor=_get_actor_from_instance(instance),
|
|
request_id=_get_request_id(),
|
|
)
|
|
else:
|
|
if hasattr(instance, "_previous_state"):
|
|
changes = _compute_changes(instance._previous_state, instance)
|
|
if changes:
|
|
ModelChangeLog.objects.create(
|
|
model_label=label,
|
|
object_id=str(instance.pk),
|
|
action="update",
|
|
changes=changes,
|
|
actor=_get_actor_from_instance(instance),
|
|
request_id=_get_request_id(),
|
|
)
|
|
|
|
|
|
@receiver(post_delete)
|
|
def track_model_delete(sender, instance, **kwargs):
|
|
label = sender._meta.label
|
|
if label in _get_excluded_models():
|
|
return
|
|
config = getattr(settings, "INFRASYNTH_AUDIT", {})
|
|
if not config.get("ENABLE_MODEL_CHANGE_TRACKING", True):
|
|
return
|
|
|
|
ModelChangeLog.objects.create(
|
|
model_label=label,
|
|
object_id=str(instance.pk),
|
|
action="delete",
|
|
changes={},
|
|
actor=_get_actor_from_instance(instance),
|
|
request_id=_get_request_id(),
|
|
)
|
|
|
|
|
|
def _get_actor_from_instance(instance):
|
|
for field in ["actor", "user", "usuario_creacion", "created_by", "uploaded_by"]:
|
|
val = getattr(instance, field, None)
|
|
if val is not None:
|
|
return val
|
|
return None
|
|
|
|
|
|
def _get_created_changes(instance):
|
|
excluded = _get_excluded_fields()
|
|
changes = {}
|
|
for field in instance._meta.get_fields():
|
|
if field.name in excluded:
|
|
continue
|
|
if hasattr(field, "serialize") and field.serialize:
|
|
val = getattr(instance, field.name, None)
|
|
if val is not None:
|
|
changes[field.name] = [None, str(val)]
|
|
return changes
|
|
|
|
|
|
def _compute_changes(old, new):
|
|
excluded = _get_excluded_fields()
|
|
changes = {}
|
|
for field in new._meta.get_fields():
|
|
if field.name in excluded:
|
|
continue
|
|
if not hasattr(field, "column") or field.column is None:
|
|
continue
|
|
old_val = getattr(old, field.name, None)
|
|
new_val = getattr(new, field.name, None)
|
|
if old_val != new_val:
|
|
changes[field.name] = [
|
|
str(old_val) if old_val is not None else None,
|
|
str(new_val) if new_val is not None else None,
|
|
]
|
|
return changes
|
|
|
|
|
|
@receiver(security_event_occurred)
|
|
def log_security_event(sender, **kwargs):
|
|
config = getattr(settings, "INFRASYNTH_AUDIT", {})
|
|
if not config.get("ENABLE_SECURITY_EVENTS", True):
|
|
return
|
|
SecurityEvent.objects.create(
|
|
event_type=kwargs.get("event_type", "unknown"),
|
|
actor=kwargs.get("actor"),
|
|
ip_address=kwargs.get("ip_address"),
|
|
metadata=kwargs.get("metadata", {}),
|
|
request_id=_get_request_id(),
|
|
)
|