Fixed #23869 -- Made ModelAdmin.get_deleted_objects() use has_delete_permission() for permissions checking.

This commit is contained in:
Henk Kahlfuß 2018-05-26 15:00:16 +02:00 committed by Tim Graham
parent ec2c9c3531
commit 3eb9127678
4 changed files with 29 additions and 9 deletions

View File

@ -590,6 +590,7 @@ answer newbie questions, and generally made Django that much better:
Mikhail Korobov <kmike84@googlemail.com> Mikhail Korobov <kmike84@googlemail.com>
Mikko Hellsing <mikko@sorl.net> Mikko Hellsing <mikko@sorl.net>
Mikołaj Siedlarek <mikolaj.siedlarek@gmail.com> Mikołaj Siedlarek <mikolaj.siedlarek@gmail.com>
milkomeda
Milton Waddams Milton Waddams
mitakummaa@gmail.com mitakummaa@gmail.com
mmarshall mmarshall

View File

@ -1812,7 +1812,7 @@ class ModelAdmin(BaseModelAdmin):
Hook for customizing the delete process for the delete view and the Hook for customizing the delete process for the delete view and the
"delete selected" action. "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 @csrf_protect_m
def delete_view(self, request, object_id, extra_context=None): def delete_view(self, request, object_id, extra_context=None):

View File

@ -2,7 +2,6 @@ import datetime
import decimal import decimal
from collections import defaultdict from collections import defaultdict
from django.contrib.auth import get_permission_codename
from django.core.exceptions import FieldDoesNotExist from django.core.exceptions import FieldDoesNotExist
from django.db import models, router from django.db import models, router
from django.db.models.constants import LOOKUP_SEP from django.db.models.constants import LOOKUP_SEP
@ -117,7 +116,7 @@ def flatten_fieldsets(fieldsets):
return field_names 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`` Find all objects related to ``objs`` that should also be deleted. ``objs``
must be a homogeneous iterable of objects (e.g. a QuerySet). must be a homogeneous iterable of objects (e.g. a QuerySet).
@ -136,12 +135,15 @@ def get_deleted_objects(objs, user, admin_site):
perms_needed = set() perms_needed = set()
def format_callback(obj): 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 opts = obj._meta
no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), obj) no_edit_link = '%s: %s' % (capfirst(opts.verbose_name), obj)
if has_admin: if has_admin:
if not admin_site._registry[model].has_delete_permission(request, obj):
perms_needed.add(opts.verbose_name)
try: try:
admin_url = reverse('%s:%s_%s_change' admin_url = reverse('%s:%s_%s_change'
% (admin_site.name, % (admin_site.name,
@ -152,10 +154,6 @@ def get_deleted_objects(objs, user, admin_site):
# Change url doesn't exist -- don't display link to edit # Change url doesn't exist -- don't display link to edit
return no_edit_link 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. # Display a link to the admin page.
return format_html('{}: <a href="{}">{}</a>', return format_html('{}: <a href="{}">{}</a>',
capfirst(opts.verbose_name), capfirst(opts.verbose_name),

View File

@ -669,13 +669,34 @@ class ModelAdminTests(TestCase):
def test_get_deleted_objects(self): def test_get_deleted_objects(self):
mock_request = MockRequest() mock_request = MockRequest()
mock_request.user = User.objects.create_superuser(username='bob', email='bob@test.com', password='test') mock_request.user = User.objects.create_superuser(username='bob', email='bob@test.com', 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) deletable_objects, model_count, perms_needed, protected = ma.get_deleted_objects([self.band], request)
self.assertEqual(deletable_objects, ['Band: The Doors']) self.assertEqual(deletable_objects, ['Band: The Doors'])
self.assertEqual(model_count, {'bands': 1}) self.assertEqual(model_count, {'bands': 1})
self.assertEqual(perms_needed, set()) self.assertEqual(perms_needed, set())
self.assertEqual(protected, []) 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='bob@test.com', 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): class ModelAdminPermissionTests(SimpleTestCase):