Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow cascade deletion of auditlog entries #556

Merged
merged 2 commits into from
Aug 27, 2023

Conversation

aleh-rymasheuski
Copy link
Contributor

@aleh-rymasheuski aleh-rymasheuski commented Aug 24, 2023

Resolves #493.

This pull request allows cascade deletion of log entries, but still disallows direct deletion through the admin.

@aleh-rymasheuski aleh-rymasheuski added this to the 3.0 milestone Aug 24, 2023
@codecov
Copy link

codecov bot commented Aug 24, 2023

Codecov Report

Merging #556 (12d113a) into master (699d838) will increase coverage by 0.03%.
Report is 1 commits behind head on master.
The diff coverage is 100.00%.

@@            Coverage Diff             @@
##           master     #556      +/-   ##
==========================================
+ Coverage   94.93%   94.96%   +0.03%     
==========================================
  Files          31       31              
  Lines         988      994       +6     
==========================================
+ Hits          938      944       +6     
  Misses         50       50              
Files Changed Coverage Δ
auditlog/admin.py 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

📣 We’re building smart automated test selection to slash your CI/CD build times. Learn more

@aleh-rymasheuski
Copy link
Contributor Author

@hramezani, we don't have a structure for testing this case with automated tests, and I don't have an elegant way to test this. If you can think of a good way of testing this, please add a test. Otherwise I'd suggest merging this change without a test.

Comment on lines 43 to 45
if request.resolver_match.url_name not in [
pattern.name for pattern in self.urls if pattern.name
]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aleh-rymasheuski Could you please explain more about what is happening here?

Also, I think you can move the [pattern.name for pattern in self.urls if pattern.name] out of method and define a class attr to don't repeat the loop in each call.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

request.resolver_match is the information about the matched URL handler:

(Pdb) request.resolver_match
ResolverMatch(func=django.contrib.admin.options.changelist_view, args=(), kwargs={}, url_name=auditlog_logentry_changelist, app_names=['admin'], namespaces=['admin'], route=admin/auditlog/logentry/)

Its url_name is the symbolic name of the route, e.g. 'auditlog_logentry_changelist'.

self.urls is the relative routes defined in the current admin class:

(Pdb) pp self.urls
[<URLPattern '' [name='auditlog_logentry_changelist']>,
 <URLPattern 'add/' [name='auditlog_logentry_add']>,
 <URLPattern '<path:object_id>/history/' [name='auditlog_logentry_history']>,
 <URLPattern '<path:object_id>/delete/' [name='auditlog_logentry_delete']>,
 <URLPattern '<path:object_id>/change/' [name='auditlog_logentry_change']>,
 <URLPattern '<path:object_id>/'>]

This check basically denies the delete permission if the request is handled by LogEntryAdmin, and allows it otherwise.

@hramezani
Copy link
Member

@hramezani, we don't have a structure for testing this case with automated tests, and I don't have an elegant way to test this. If you can think of a good way of testing this, please add a test. Otherwise I'd suggest merging this change without a test.

Thanks @aleh-rymasheuski for this patch 🙏

I think we can test it like:

diff --git a/auditlog_tests/tests.py b/auditlog_tests/tests.py
index 12a4725..8824780 100644
--- a/auditlog_tests/tests.py
+++ b/auditlog_tests/tests.py
@@ -19,7 +19,7 @@ from django.core import management
 from django.db import models
 from django.db.models.signals import pre_save
 from django.test import RequestFactory, TestCase, override_settings
-from django.urls import reverse
+from django.urls import resolve, reverse
 from django.utils import dateformat, formats
 from django.utils import timezone as django_timezone
 from django.utils.encoding import smart_str
@@ -1555,6 +1555,9 @@ class DiffMsgTest(TestCase):
         super().setUp()
         self.site = AdminSite()
         self.admin = LogEntryAdmin(LogEntry, self.site)
+        self.user = User.objects.create_user(
+            username="test", email="test@example.com", password="top_secret"
+        )
 
     def _create_log_entry(self, action, changes):
         return LogEntry.objects.log_create(
@@ -1711,6 +1714,13 @@ class DiffMsgTest(TestCase):
         ):
             self.assertEqual(self.admin.field_verbose_name(log_entry, "actor"), "actor")
 
+    def test_has_delete_permission(self):
+        request = RequestFactory().get("/simplemodel/1/")
+        request.resolver_match = resolve("/simplemodel/1/")
+        request.user = self.user
+
+        self.assertFalse(self.admin.has_delete_permission(request))
+
 
 class NoDeleteHistoryTest(TestCase):
     def test_delete_related(self):

@aleh-rymasheuski
Copy link
Contributor Author

@hramezani, I added a test based on your suggestion.

@@ -40,6 +40,11 @@ def has_change_permission(self, request, obj=None):
return False

def has_delete_permission(self, request, obj=None):
if request.resolver_match and request.resolver_match.url_name not in [
pattern.name for pattern in self.urls if pattern.name
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't we move the admin urls patterns list creation out of this method to not calculate it every time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure when in the lifecycle of the admin self.urls is available, so I'll just move this expression into a cached property.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@hramezani hramezani merged commit c527868 into jazzband:master Aug 27, 2023
6 checks passed
aleh-rymasheuski added a commit to MacmillanPlatform/django-auditlog that referenced this pull request Sep 1, 2023
* Allow cascade deletion of auditlog entries

* Cache iteration over self.urls
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Can't remove any auditlogged objects through the admin
2 participants