Skip to content

Commit

Permalink
Read only events admin (#127)
Browse files Browse the repository at this point in the history
* Added readonly stuff

* Missing colon

* Fix reverse URL bug

* Deny deletion

* Added documentation

* Make models readonly through inherited EasyAuditModelAdmin
  • Loading branch information
Etenil authored May 15, 2020
1 parent 00f5591 commit 3424469
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 16 deletions.
31 changes: 19 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,12 +89,12 @@ Below are some of the settings you may want to use. These should be defined in y
- ['event_type', 'content_type', 'user', 'datetime', ] for CRUDEventAdmin
- ['login_type', 'user', 'datetime', ] for LoginEventAdmin
- ['method', 'user', 'datetime', ] for RequestEventAdmin

* `DJANGO_EASY_AUDIT_DATABASE_ALIAS`

By default it is the django `default` database alias. But for projects that have split databases,
this is necessary in order to keep database atomicity concerns in check during signal handlers.

To clarify, this is only _truly_ necessary for the model signals.

* `DJANGO_EASY_AUDIT_CRUD_EVENT_NO_CHANGED_FIELDS_SKIP`
Expand All @@ -105,35 +105,42 @@ Below are some of the settings you may want to use. These should be defined in y
or those that do not closely follow the release notes of this project will have one less worry when upgrading.


* `DJANGO_EASY_AUDIT_READONLY_EVENTS`

Default is `False`. The events visible through the admin interface are editable by default by a
superuser. Set this to `True` if you wish to make the recorded events read-only through the admin
UI.

* `DJANGO_EASY_AUDIT_LOGGING_BACKEND`

A pluggable backend option for logging. Defaults to `easyaudit.backends.ModelBackend`.
This class expects to have 3 methods:
* `login(self, login_info_dict):`
* `crud(self, crud_info_dict):`
This class expects to have 3 methods:
* `login(self, login_info_dict):`
* `crud(self, crud_info_dict):`
* `request(self, request_info_dict):`

each of these methods accept a dictionary containing the info regarding the event.
each of these methods accept a dictionary containing the info regarding the event.
example overriding:
```python
import logging

class PythonLoggerBackend:
logging.basicConfig()
logger = logging.getLogger('your-kibana-logger')
logger.setLevel(logging.DEBUG)

def request(self, request_info):
return request_info # if you don't need it

def login(self, login_info):
self.logger.info(msg='your message', extra=login_info)
return login_info
self.logger.info(msg='your message', extra=login_info)
return login_info

def crud(self, crud_info):
self.logger.info(msg='your message', extra=crud_info)
return crud_info
```

## What does it do

Django Easy Audit uses [Django signals](https://docs.djangoproject.com/en/dev/topics/signals/)
Expand Down
4 changes: 2 additions & 2 deletions easyaudit/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
from .models import CRUDEvent, LoginEvent, RequestEvent
from .admin_helpers import prettify_json, EasyAuditModelAdmin
from .settings import (CRUD_EVENT_LIST_FILTER, LOGIN_EVENT_LIST_FILTER, REQUEST_EVENT_LIST_FILTER,
CRUD_EVENT_SEARCH_FIELDS, LOGIN_EVENT_SEARCH_FIELDS, REQUEST_EVENT_SEARCH_FIELDS)

CRUD_EVENT_SEARCH_FIELDS, LOGIN_EVENT_SEARCH_FIELDS, REQUEST_EVENT_SEARCH_FIELDS,
READONLY_EVENTS)

# CRUD events
class CRUDEventAdmin(EasyAuditModelAdmin):
Expand Down
17 changes: 16 additions & 1 deletion easyaudit/admin_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
except: # Django < 2.0
from django.core.urlresolvers import reverse

from django.http import HttpResponseRedirect
from django.http import HttpResponseRedirect, HttpResponse
from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from django.conf.urls import url
Expand Down Expand Up @@ -36,6 +36,13 @@ def get_changelist_instance(self, *args, **kwargs):
self.users_by_id = {user.id: user for user in get_user_model().objects.filter(id__in=user_ids)}
return changelist_instance

def get_readonly_fields(self, request, obj=None):
"Mark all fields of model as readonly if configured to do so."
if settings.READONLY_EVENTS:
return [f.name for f in self.model._meta.get_fields()]
else:
return self.readonly_fields

def user_link(self, obj):
user = self.users_by_id.get(obj.user_id)
#return mark_safe(get_user_link(user))
Expand All @@ -56,6 +63,11 @@ def user_link(self, obj):
def has_add_permission(self, request, obj=None):
return False

def has_delete_permission(self, request, obj=None):
if settings.READONLY_EVENTS:
return False
return True

def get_urls(self):
info = self.model._meta.app_label, self.model._meta.model_name
urls = super(EasyAuditModelAdmin, self).get_urls()
Expand All @@ -75,6 +87,9 @@ def purge_objects(self, request):
next, it deletes all objects and redirects back to the change list.
"""

if settings.READONLY_EVENTS:
raise PermissionDenied

def truncate_table(model):
if settings.TRUNCATE_TABLE_SQL_STATEMENT:
from django.db import connection
Expand Down
2 changes: 2 additions & 0 deletions easyaudit/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,5 @@ def get_model_list(class_list):
CRUD_EVENT_SEARCH_FIELDS = getattr(settings, 'DJANGO_EASY_AUDIT_CRUD_EVENT_SEARCH_FIELDS', ['=object_id', 'object_json_repr', ])
LOGIN_EVENT_SEARCH_FIELDS = getattr(settings, 'DJANGO_EASY_AUDIT_LOGIN_EVENT_SEARCH_FIELDS', ['=remote_ip', 'username', ])
REQUEST_EVENT_SEARCH_FIELDS = getattr(settings, 'DJANGO_EASY_AUDIT_REQUEST_EVENT_SEARCH_FIELDS', ['=remote_ip', 'user__username', 'url', 'query_string', ])

READONLY_EVENTS = getattr(settings, 'DJANGO_EASY_AUDIT_READONLY_EVENTS', False)
1 change: 0 additions & 1 deletion easyaudit/tests/test_app/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,3 @@ def test_request_event_admin_no_users(self):
response = self.client.get(reverse('admin:easyaudit_requestevent_changelist'))
self.assertEqual(200, response.status_code)
filters = self._list_filters(response.content)
print(filters)

0 comments on commit 3424469

Please sign in to comment.