django easy audit implemetation

This commit is contained in:
Faheedkhan 2025-06-04 20:41:58 +03:00
parent be037e7fa6
commit 90e6af08d8
7 changed files with 227 additions and 123 deletions

View File

@ -809,10 +809,8 @@ path(
path('management/user_management/', views.user_management, name='user_management'),
path('management/<str:content_type>/<slug:slug>/activate_account/', views.activate_account, name='activate_account'),
path('management/<str:content_type>/<slug:slug>/permenant_delete_account/', views.permenant_delete_account, name='permenant_delete_account'),
path('management/audit_log_management/', views.audit_log_management, name='audit_log_management'),
path('management/audit_log_management/modellogs', views.ModelLogListView.as_view(), name='modellogs_list'),
path('management/audit_log_management/authlogs', views.RequestLogListView.as_view(), name='authlogs_list'),
path('management/audit_log_management/requestlogs', views. AuthLogListView.as_view(), name='requestlogs_list'),
path('management/audit_log_dashboard/', views.AuditLogDashboardView.as_view(), name='audit_log_dashboard'),
]

View File

@ -150,10 +150,12 @@ from .utils import (
CarTransfer,
)
from .tasks import create_accounts_for_make, send_email
#djago easy audit log
from easyaudit.models import RequestEvent, CRUDEvent, LoginEvent
logger = logging.getLogger(__name__)
logging.basicConfig(level=logging.INFO)
@ -8253,44 +8255,79 @@ def user_management(request):
return render(request, "admin_management/user_management.html", context)
#audit log Management
def audit_log_management(request):
return render(request, "admin_management/audit_log_lists.html")
#audit logs views
# class AuditLogDashboardView(TemplateView):
# """
# Displays a dashboard with tabs for different audit log types.
# Fetches all necessary log data to pass to the template.
# """
# template_name = 'admin_management/audit_log_dashboard.html' # Name of your main template with tabs
class RequestLogListView(ListView):
"""Displays a list of RequestEvents."""
model = RequestEvent
template_name = 'admin_management/request_logs.html'
context_object_name = 'request_events'
paginate_by = 20
# def get_context_data(self, **kwargs):
# context = super().get_context_data(**kwargs)
# # Fetch data for each log type
# # Order by 'datetime' field as per easy_audit models
# context['model_events'] = CRUDEvent.objects.all().order_by('-datetime')
# context['auth_events'] = LoginEvent.objects.all().order_by('-datetime')
# context['request_events'] = RequestEvent.objects.all().order_by('-datetime')
# return context
def get_queryset(self):
# The field name in RequestEvent is 'datetime', not 'created'
return RequestEvent.objects.all().order_by('-datetime')
class AuditLogDashboardView(TemplateView):
template_name = 'admin_management/audit_log_dashboard.html'
class ModelLogListView(ListView):
"""Displays a list of CRUDEvents (model changes)."""
# Corrected: Use CRUDEvent model
model = CRUDEvent
template_name = 'admin_management/model_logs.html'
context_object_name = 'model_events'
paginate_by = 20
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
def get_queryset(self):
# The field name in CRUDEvent is 'datetime', not 'created'
return CRUDEvent.objects.all().order_by('-datetime')
# Process CRUD (Model Change) Events
model_events_raw = CRUDEvent.objects.all().order_by('-datetime')
processed_model_events = []
for event in model_events_raw:
# Create a base dictionary for each event's data
event_data = {
'datetime': event.datetime,
'user': event.user,
'event_type_display': event.get_event_type_display(),
'model_name': event.content_type.model,
'object_id': event.object_id,
'object_repr': event.object_repr,
'field_changes': [] # This will be a list of dicts: [{'field': 'name', 'old': 'A', 'new': 'B'}]
}
if event.changed_fields:
try:
changes = json.loads(event.changed_fields)
for field_name, values in changes.items():
# Ensure values are lists and have at least two elements
old_value = values[0] if isinstance(values, list) and len(values) > 0 else None
new_value = values[1] if isinstance(values, list) and len(values) > 1 else None
# Store each field change as a separate entry
event_data['field_changes'].append({
'field': field_name,
'old': old_value,
'new': new_value
})
except json.JSONDecodeError:
# Handle invalid JSON; you might log this error
event_data['field_changes'].append({
'field': 'Error',
'old': '',
'new': 'Invalid JSON in changed_fields'
})
processed_model_events.append(event_data)
context['model_events'] = processed_model_events
context['auth_events'] = LoginEvent.objects.all().order_by('-datetime')
context['request_events'] = RequestEvent.objects.all().order_by('-datetime')
return context
class AuthLogListView(ListView):
"""Displays a list of LoginEvents (authentication events)."""
model = LoginEvent
template_name = 'admin_management/auth_logs.html'
context_object_name = 'auth_events'
paginate_by = 20
def get_queryset(self):
# The field name in LoginEvent is 'datetime', not 'created'
return LoginEvent.objects.all().order_by('-datetime')

View File

@ -9,6 +9,7 @@
{% endblock %}
{% block content %}
<div class="row mt-4">
<div class="d-flex justify-content-between mb-2">
<h3 class=""><i class="fa-solid fa-book"></i> {% trans "Audit Log Management" %}</h3>
</div>
@ -50,12 +51,14 @@
</div>
<!-- Capital Tab -->
<div class="tab-pane fade" id="requestslog" role="tabpanel" aria-labelledby="requestslog-tab">
{% comment %} <div class="tab-pane fade" id="requestslog" role="tabpanel" aria-labelledby="requestslog-tab">
{% include "partials/search_box.html" %}
{% include "admin_management/request_logs.html" %}
</div>
</div> {% endcomment %}
<div class="tab-pane fade" id="requestslog" role="tabpanel" aria-labelledby="requestslog-tab">
<p>Hello from Request Logs tab!</p>
</div>
</div>
</div>
</div>

View File

@ -1,29 +1,33 @@
{% if auth_events %}
<table>
<thead>
<tr>
<th>Timestamp</th>
<th>User</th>
<th>Event Type</th>
<th>Username</th> {# Added username field #}
<th>IP Address</th>
</tr>
</thead>
<tbody>
{% for event in auth_events %}
<tr>
<td>{{ event.datetime }}</td> {# Corrected field name #}
<td>{{ event.user.username|default:"N/A" }}</td>
<td>{{ event.get_login_type_display }}</td> {# Corrected: get_login_type_display #}
<td>{{ event.username }}</td> {# Added username field #}
<td>{{ event.remote_ip }}</td> {# Corrected field name #}
</tr>
{% endfor %}
</tbody>
</table>
{% load i18n custom_filters %}
{% if auth_events %}
<div class="table-responsive px-1 scrollbar mt-3">
<table class= "table align-items-center table-flush table-hover">
<thead>
<tr class="bg-body-highlight">
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Timestamp") |capfirst }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("User") |capfirst }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Event Type") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("username") |capfirst }}</th>
<th class="sort white-space-nowrap align-middle"scope="col">{{ _("IP Address") |capfirst }}</th>
</tr>
</thead>
<tbody class="list">
{% for event in auth_events %}
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="align-middle product white-space-nowrap">{{event.datetime}}</td>
<td class="align-middle product white-space-nowrap">{{ event.user.username|default:"N/A" }}</td>
<td class="align-middle product white-space-nowrap">{{ event.get_login_type_display}}</td>
<td class="align-middle product white-space-nowrap">{{ event.username}}</td>
<td class="align-middle product white-space-nowrap">{{ event.remote_ip}}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>No authentication audit events found.</p>
{% endif %}

View File

@ -14,10 +14,10 @@
</a>
</div>
<div class="col">
<a href="{% url 'audit_log_management' %}">
<a href="{% url 'audit_log_dashboard' %}">
<div class="card h-100">
<div class="card-header text-center">
<h5 class="card-title">{{ _("Audit Log Management")}}</h5>
<h5 class="card-title">{{ _("Audit Log Dashboard")}}</h5>
<span class="me-2"><i class="fas fa-user fa-2x"></i></span>
</div>
</div>

View File

@ -1,37 +1,94 @@
{% load i18n custom_filters %}
{% if model_events %}
<table>
<thead>
<tr>
<th>Timestamp</th>
<th>User</th>
<th>Action</th>
<th>Model</th>
<th>Object ID</th>
<th>Changes</th>
<th>Object Repr</th> {# Added for object representation #}
</tr>
</thead>
<tbody>
{% for event in model_events %}
{% if model_events %}
<div class="table-responsive px-1 scrollbar mt-3">
<table class="table align-items-center table-flush table-hover mt-3">
<thead>
<tr class="bg-body-highlight">
<th>{% trans "Timestamp" %}</th>
<th>{% trans "User" %}</th>
<th>{% trans "Action" %}</th>
<th>{% trans "Model" %}</th>
<th>{% trans "Object ID" %}</th>
<th>{% trans "Object Representation" %}</th>
<th>{% trans "Field" %}</th> {# Dedicated column for field name #}
<th>{% trans "Old Value" %}</th> {# Dedicated column for old value #}
<th>{% trans "New Value" %}</th> {# Dedicated column for new value #}
</tr>
</thead>
<tbody>
{% for event in model_events %}
{% if event.field_changes %}
{# Loop through each individual field change for this event #}
{% for change in event.field_changes %}
<tr>
{# Display common event details using rowspan for the first change #}
{% if forloop.first %}
<td rowspan="{{ event.field_changes|length }}">
{{ event.datetime|date:"Y-m-d H:i:s" }}
</td>
<td rowspan="{{ event.field_changes|length }}">
{{ event.user.username|default:"Anonymous" }}
</td>
<td rowspan="{{ event.field_changes|length }}">
{{ event.event_type_display }}
</td>
<td rowspan="{{ event.field_changes|length }}">
{{ event.model_name|title }}
</td>
<td rowspan="{{ event.field_changes|length }}">
{{ event.object_id }}
</td>
<td rowspan="{{ event.field_changes|length }}">
{{ event.object_repr }}
</td>
{% endif %}
{# Display the specific field change details in their own columns #}
<td><strong>{{ change.field }}</strong></td>
<td>
{% if change.old is not None %}
<pre style="white-space: pre-wrap; word-break: break-all; font-size: 0.85em; background-color: #f8f9fa; padding: 5px; border-radius: 3px;">{{ change.old }}</pre>
{% else %}
(None)
{% endif %}
</td>
<td>
{% if change.new is not None %}
<pre style="white-space: pre-wrap; word-break: break-all; font-size: 0.85em; background-color: #f8f9fa; padding: 5px; border-radius: 3px;">{{ change.new }}</pre>
{% else %}
(None)
{% endif %}
</td>
</tr>
{% endfor %}
{% else %}
{# Fallback for events with no specific field changes (e.g., CREATE, DELETE) #}
<tr>
<td>{{ event.datetime }}</td> {# Corrected field name #}
<td>{{ event.datetime|date:"Y-m-d H:i:s" }}</td>
<td>{{ event.user.username|default:"Anonymous" }}</td>
<td>{{ event.get_event_type_display }}</td>
<td>{{ event.content_type.model|title }}</td> {# Model name #}
<td>{{ event.object_id }}</td> {# Object ID #}
<td>
{% if event.changed_fields %} {# Corrected field name #}
<pre style="white-space: pre-wrap; word-break: break-all;">{{ event.changed_fields|safe }}</pre>
<td>{{ event.event_type_display }}</td>
<td>{{ event.model_name|title }}</td>
<td>{{ event.object_id }}</td>
<td>{{ event.object_repr }}</td>
{# Span the 'Field', 'Old Value', 'New Value' columns #}
<td colspan="3">
{% if event.event_type_display == "Create" %}
{% trans "Object created." %}
{% elif event.event_type_display == "Delete" %}
{% trans "Object deleted." %}
{% else %}
N/A
{% trans "No specific field changes recorded." %}
{% endif %}
</td>
<td>{{ event.object_repr }}</td> {# Object representation #}
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>No model change audit events found.</p>
{% endif %}
{% endif %}
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p>{% trans "No model change audit events found." %}</p>
{% endif %}

View File

@ -1,29 +1,34 @@
{% if request_events %}
<table>
<thead>
<tr>
<th>Timestamp</th>
<th>User</th>
<th>URL</th> {# Changed from Path to URL based on model #}
<th>Method</th>
<th>IP Address</th>
{# No status_code in RequestEvent model #}
</tr>
</thead>
<tbody>
{% for event in request_events %}
<tr>
<td>{{ event.datetime }}</td> {# Corrected field name #}
<td>{{ event.user.username|default:"Anonymous" }}</td>
<td>{{ event.url }}</td> {# Corrected field name #}
<td>{{ event.method }}</td>
<td>{{ event.remote_ip }}</td> {# Corrected field name #}
{# Removed status_code as it's not in the model #}
</tr>
{% endfor %}
</tbody>
</table>
{% load i18n custom_filters %}
{% if request_events %}
<div class="table-responsive px-1 scrollbar mt-3">
<table class= "table align-items-center table-flush table-hover">
<thead>
<tr class="bg-body-highlight">
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Timestamp") |capfirst }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("User") |capfirst }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("URL") }}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{{ _("Method") |capfirst }}</th>
<th class="sort white-space-nowrap align-middle"scope="col">{{ _("IP Address") |capfirst }}</th>
</tr>
</thead>
<tbody class="list">
{% for event in request_events %}
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="align-middle product white-space-nowrap">{{event.datetime}}</td>
<td class="align-middle product white-space-nowrap">{{ event.user.username|default:"Anonymous" }}</td>
<td class="align-middle product white-space-nowrap">{{ event.url }}</td>
<td class="align-middle product white-space-nowrap">{{ event.method}}</td>
<td class="align-middle product white-space-nowrap">{{ event.remote_ip}}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<p>No request audit events found.</p>
<p>No request audit events found.</p>
{% endif %}