Skip to content

Commit

Permalink
Fixed #23869 -- Made ModelAdmin.get_deleted_objects() use has_delete_…
Browse files Browse the repository at this point in the history
…permission() for permissions checking.
  • Loading branch information
milkomeda authored and timgraham committed Jun 15, 2018
1 parent ec2c9c3 commit 3eb9127
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 9 deletions.
1 change: 1 addition & 0 deletions AUTHORS
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@ answer newbie questions, and generally made Django that much better:
Mikhail Korobov <[email protected]>
Mikko Hellsing <[email protected]>
Mikołaj Siedlarek <[email protected]>
milkomeda
Milton Waddams
[email protected]
mmarshall
Expand Down
2 changes: 1 addition & 1 deletion django/contrib/admin/options.py
Original file line number Diff line number Diff line change
Expand Up @@ -1812,7 +1812,7 @@ def get_deleted_objects(self, objs, request):
Hook for customizing the delete process for the delete view and the
"delete selected" action.
"""
return get_deleted_objects(objs, request.user, self.admin_site)
return get_deleted_objects(objs, request, self.admin_site)

@csrf_protect_m
def delete_view(self, request, object_id, extra_context=None):
Expand Down
12 changes: 5 additions & 7 deletions django/contrib/admin/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
import decimal
from collections import defaultdict

from django.contrib.auth import get_permission_codename
from django.core.exceptions import FieldDoesNotExist
from django.db import models, router
from django.db.models.constants import LOOKUP_SEP
Expand Down Expand Up @@ -117,7 +116,7 @@ def flatten_fieldsets(fieldsets):
return field_names


def get_deleted_objects(objs, user, admin_site):
def get_deleted_objects(objs, request, admin_site):
"""
Find all objects related to ``objs`` that should also be deleted. ``objs``
must be a homogeneous iterable of objects (e.g. a QuerySet).
Expand All @@ -136,12 +135,15 @@ def get_deleted_objects(objs, user, admin_site):
perms_needed = set()

def format_callback(obj):
has_admin = obj.__class__ in admin_site._registry
model = obj.__class__
has_admin = model in admin_site._registry
opts = obj._meta

no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), obj)

if has_admin:
if not admin_site._registry[model].has_delete_permission(request, obj):
perms_needed.add(opts.verbose_name)
try:
admin_url = reverse('%s:%s_%s_change'
% (admin_site.name,
Expand All @@ -152,10 +154,6 @@ def format_callback(obj):
# Change url doesn't exist -- don't display link to edit
return no_edit_link

if 'delete' in opts.default_permissions:
p = '%s.%s' % (opts.app_label, get_permission_codename('delete', opts))
if not user.has_perm(p):
perms_needed.add(opts.verbose_name)
# Display a link to the admin page.
return format_html('{}: <a href="{}">{}</a>',
capfirst(opts.verbose_name),
Expand Down
23 changes: 22 additions & 1 deletion tests/modeladmin/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,13 +669,34 @@ def get_autocomplete_fields(self, request):
def test_get_deleted_objects(self):
mock_request = MockRequest()
mock_request.user = User.objects.create_superuser(username='bob', email='[email protected]', password='test')
ma = ModelAdmin(Band, self.site)
self.site.register(Band, ModelAdmin)
ma = self.site._registry[Band]
deletable_objects, model_count, perms_needed, protected = ma.get_deleted_objects([self.band], request)
self.assertEqual(deletable_objects, ['Band: The Doors'])
self.assertEqual(model_count, {'bands': 1})
self.assertEqual(perms_needed, set())
self.assertEqual(protected, [])

def test_get_deleted_objects_with_custom_has_delete_permission(self):
"""
ModelAdmin.get_deleted_objects() uses ModelAdmin.has_delete_permission()
for permissions checking.
"""
mock_request = MockRequest()
mock_request.user = User.objects.create_superuser(username='bob', email='[email protected]', password='test')

class TestModelAdmin(ModelAdmin):
def has_delete_permission(self, request, obj=None):
return False

self.site.register(Band, TestModelAdmin)
ma = self.site._registry[Band]
deletable_objects, model_count, perms_needed, protected = ma.get_deleted_objects([self.band], request)
self.assertEqual(deletable_objects, ['Band: The Doors'])
self.assertEqual(model_count, {'bands': 1})
self.assertEqual(perms_needed, {'band'})
self.assertEqual(protected, [])


class ModelAdminPermissionTests(SimpleTestCase):

Expand Down

0 comments on commit 3eb9127

Please sign in to comment.