Fixed #6327 -- Added has_module_permission method to BaseModelAdmin
Thanks chrj for the suggestion.
This commit is contained in:
parent
bf743a4d57
commit
504c89e800
|
@ -473,6 +473,19 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
|
||||||
codename = get_permission_codename('delete', opts)
|
codename = get_permission_codename('delete', opts)
|
||||||
return request.user.has_perm("%s.%s" % (opts.app_label, codename))
|
return request.user.has_perm("%s.%s" % (opts.app_label, codename))
|
||||||
|
|
||||||
|
def has_module_permission(self, request):
|
||||||
|
"""
|
||||||
|
Returns True if the given request has any permission in the given
|
||||||
|
app label.
|
||||||
|
|
||||||
|
Can be overridden by the user in subclasses. In such case it should
|
||||||
|
return True if the given request has permission to view the module on
|
||||||
|
the admin index page and access the module's index page. Overriding it
|
||||||
|
does not restrict access to the add, change or delete views. Use
|
||||||
|
`ModelAdmin.has_(add|change|delete)_permission` for that.
|
||||||
|
"""
|
||||||
|
return request.user.has_module_perms(self.opts.app_label)
|
||||||
|
|
||||||
|
|
||||||
@python_2_unicode_compatible
|
@python_2_unicode_compatible
|
||||||
class ModelAdmin(BaseModelAdmin):
|
class ModelAdmin(BaseModelAdmin):
|
||||||
|
|
|
@ -367,10 +367,9 @@ class AdminSite(object):
|
||||||
apps that have been registered in this site.
|
apps that have been registered in this site.
|
||||||
"""
|
"""
|
||||||
app_dict = {}
|
app_dict = {}
|
||||||
user = request.user
|
|
||||||
for model, model_admin in self._registry.items():
|
for model, model_admin in self._registry.items():
|
||||||
app_label = model._meta.app_label
|
app_label = model._meta.app_label
|
||||||
has_module_perms = user.has_module_perms(app_label)
|
has_module_perms = model_admin.has_module_permission(request)
|
||||||
|
|
||||||
if has_module_perms:
|
if has_module_perms:
|
||||||
perms = model_admin.get_model_perms(request)
|
perms = model_admin.get_model_perms(request)
|
||||||
|
@ -424,14 +423,14 @@ class AdminSite(object):
|
||||||
current_app=self.name)
|
current_app=self.name)
|
||||||
|
|
||||||
def app_index(self, request, app_label, extra_context=None):
|
def app_index(self, request, app_label, extra_context=None):
|
||||||
user = request.user
|
|
||||||
app_name = apps.get_app_config(app_label).verbose_name
|
app_name = apps.get_app_config(app_label).verbose_name
|
||||||
has_module_perms = user.has_module_perms(app_label)
|
|
||||||
if not has_module_perms:
|
|
||||||
raise PermissionDenied
|
|
||||||
app_dict = {}
|
app_dict = {}
|
||||||
for model, model_admin in self._registry.items():
|
for model, model_admin in self._registry.items():
|
||||||
if app_label == model._meta.app_label:
|
if app_label == model._meta.app_label:
|
||||||
|
has_module_perms = model_admin.has_module_permission(request)
|
||||||
|
if not has_module_perms:
|
||||||
|
raise PermissionDenied
|
||||||
|
|
||||||
perms = model_admin.get_model_perms(request)
|
perms = model_admin.get_model_perms(request)
|
||||||
|
|
||||||
# Check whether user has any perm for this module.
|
# Check whether user has any perm for this module.
|
||||||
|
|
|
@ -1631,6 +1631,19 @@ templates used by the :class:`ModelAdmin` views:
|
||||||
be interpreted as meaning that the current user is not permitted to delete
|
be interpreted as meaning that the current user is not permitted to delete
|
||||||
any object of this type).
|
any object of this type).
|
||||||
|
|
||||||
|
.. method:: ModelAdmin.has_module_permission(request)
|
||||||
|
|
||||||
|
.. versionadded:: 1.8
|
||||||
|
|
||||||
|
Should return ``True`` if displaying the module on the admin index page and
|
||||||
|
accessing the module's index page is permitted, ``False`` otherwise.
|
||||||
|
Uses :meth:`User.has_module_perms()
|
||||||
|
<django.contrib.auth.models.User.has_module_perms>` by default. Overriding
|
||||||
|
it does not restrict access to the add, change or delete views,
|
||||||
|
:meth:`~ModelAdmin.has_add_permission`,
|
||||||
|
:meth:`~ModelAdmin.has_change_permission`, and
|
||||||
|
:meth:`~ModelAdmin.has_delete_permission` should be used for that.
|
||||||
|
|
||||||
.. method:: ModelAdmin.get_queryset(request)
|
.. method:: ModelAdmin.get_queryset(request)
|
||||||
|
|
||||||
The ``get_queryset`` method on a ``ModelAdmin`` returns a
|
The ``get_queryset`` method on a ``ModelAdmin`` returns a
|
||||||
|
@ -1909,6 +1922,7 @@ adds some of its own (the shared features are actually defined in the
|
||||||
- :meth:`~ModelAdmin.has_add_permission`
|
- :meth:`~ModelAdmin.has_add_permission`
|
||||||
- :meth:`~ModelAdmin.has_change_permission`
|
- :meth:`~ModelAdmin.has_change_permission`
|
||||||
- :meth:`~ModelAdmin.has_delete_permission`
|
- :meth:`~ModelAdmin.has_delete_permission`
|
||||||
|
- :meth:`~ModelAdmin.has_module_permission`
|
||||||
|
|
||||||
The ``InlineModelAdmin`` class adds:
|
The ``InlineModelAdmin`` class adds:
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,9 @@ Minor features
|
||||||
:mod:`django.contrib.admin`
|
:mod:`django.contrib.admin`
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
* ...
|
* :class:`~django.contrib.admin.ModelAdmin` now has a
|
||||||
|
:meth:`~django.contrib.admin.ModelAdmin.has_module_permission`
|
||||||
|
method to allow limiting access to the module on the admin index page.
|
||||||
|
|
||||||
:mod:`django.contrib.auth`
|
:mod:`django.contrib.auth`
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
@ -17,6 +17,9 @@ class MockSuperUser(object):
|
||||||
def has_perm(self, perm):
|
def has_perm(self, perm):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def has_module_perms(self, module):
|
||||||
|
return True
|
||||||
|
|
||||||
request = MockRequest()
|
request = MockRequest()
|
||||||
request.user = MockSuperUser()
|
request.user = MockSuperUser()
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,12 @@ class ArticleAdmin(admin.ModelAdmin):
|
||||||
return super(ArticleAdmin, self).save_model(request, obj, form, change)
|
return super(ArticleAdmin, self).save_model(request, obj, form, change)
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleAdmin2(admin.ModelAdmin):
|
||||||
|
|
||||||
|
def has_module_permission(self, request):
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class RowLevelChangePermissionModelAdmin(admin.ModelAdmin):
|
class RowLevelChangePermissionModelAdmin(admin.ModelAdmin):
|
||||||
def has_change_permission(self, request, obj=None):
|
def has_change_permission(self, request, obj=None):
|
||||||
""" Only allow changing objects with even id number """
|
""" Only allow changing objects with even id number """
|
||||||
|
@ -923,3 +929,5 @@ site.register(Group, GroupAdmin)
|
||||||
site2 = admin.AdminSite(name="namespaced_admin")
|
site2 = admin.AdminSite(name="namespaced_admin")
|
||||||
site2.register(User, UserAdmin)
|
site2.register(User, UserAdmin)
|
||||||
site2.register(Group, GroupAdmin)
|
site2.register(Group, GroupAdmin)
|
||||||
|
site7 = admin.AdminSite(name="admin7")
|
||||||
|
site7.register(Article, ArticleAdmin2)
|
||||||
|
|
|
@ -1493,6 +1493,70 @@ class AdminViewPermissionsTest(TestCase):
|
||||||
self.assertEqual(response.status_code, 302)
|
self.assertEqual(response.status_code, 302)
|
||||||
self.assertEqual(response.url, 'http://example.com/dummy/foo/')
|
self.assertEqual(response.url, 'http://example.com/dummy/foo/')
|
||||||
|
|
||||||
|
def test_has_module_permission(self):
|
||||||
|
"""
|
||||||
|
Ensure that has_module_permission() returns True for all users who
|
||||||
|
have any permission for that module (add, change, or delete), so that
|
||||||
|
the module is displayed on the admin index page.
|
||||||
|
"""
|
||||||
|
login_url = reverse('admin:login') + '?next=/test_admin/admin/'
|
||||||
|
|
||||||
|
self.client.post(login_url, self.super_login)
|
||||||
|
response = self.client.get('/test_admin/admin/')
|
||||||
|
self.assertContains(response, 'admin_views')
|
||||||
|
self.assertContains(response, 'Articles')
|
||||||
|
self.client.get('/test_admin/admin/logout/')
|
||||||
|
|
||||||
|
self.client.post(login_url, self.adduser_login)
|
||||||
|
response = self.client.get('/test_admin/admin/')
|
||||||
|
self.assertContains(response, 'admin_views')
|
||||||
|
self.assertContains(response, 'Articles')
|
||||||
|
self.client.get('/test_admin/admin/logout/')
|
||||||
|
|
||||||
|
self.client.post(login_url, self.changeuser_login)
|
||||||
|
response = self.client.get('/test_admin/admin/')
|
||||||
|
self.assertContains(response, 'admin_views')
|
||||||
|
self.assertContains(response, 'Articles')
|
||||||
|
self.client.get('/test_admin/admin/logout/')
|
||||||
|
|
||||||
|
self.client.post(login_url, self.deleteuser_login)
|
||||||
|
response = self.client.get('/test_admin/admin/')
|
||||||
|
self.assertContains(response, 'admin_views')
|
||||||
|
self.assertContains(response, 'Articles')
|
||||||
|
self.client.get('/test_admin/admin/logout/')
|
||||||
|
|
||||||
|
def test_overriding_has_module_permission(self):
|
||||||
|
"""
|
||||||
|
Ensure that overriding has_module_permission() has the desired effect.
|
||||||
|
In this case, it always returns False, so the module should not be
|
||||||
|
displayed on the admin index page for any users.
|
||||||
|
"""
|
||||||
|
login_url = reverse('admin:login') + '?next=/test_admin/admin7/'
|
||||||
|
|
||||||
|
self.client.post(login_url, self.super_login)
|
||||||
|
response = self.client.get('/test_admin/admin7/')
|
||||||
|
self.assertNotContains(response, 'admin_views')
|
||||||
|
self.assertNotContains(response, 'Articles')
|
||||||
|
self.client.get('/test_admin/admin7/logout/')
|
||||||
|
|
||||||
|
self.client.post(login_url, self.adduser_login)
|
||||||
|
response = self.client.get('/test_admin/admin7/')
|
||||||
|
self.assertNotContains(response, 'admin_views')
|
||||||
|
self.assertNotContains(response, 'Articles')
|
||||||
|
self.client.get('/test_admin/admin7/logout/')
|
||||||
|
|
||||||
|
self.client.post(login_url, self.changeuser_login)
|
||||||
|
response = self.client.get('/test_admin/admin7/')
|
||||||
|
self.assertNotContains(response, 'admin_views')
|
||||||
|
self.assertNotContains(response, 'Articles')
|
||||||
|
self.client.get('/test_admin/admin7/logout/')
|
||||||
|
|
||||||
|
self.client.post(login_url, self.deleteuser_login)
|
||||||
|
response = self.client.get('/test_admin/admin7/')
|
||||||
|
self.assertNotContains(response, 'admin_views')
|
||||||
|
self.assertNotContains(response, 'Articles')
|
||||||
|
self.client.get('/test_admin/admin7/logout/')
|
||||||
|
|
||||||
|
|
||||||
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
|
@override_settings(PASSWORD_HASHERS=('django.contrib.auth.hashers.SHA1PasswordHasher',),
|
||||||
ROOT_URLCONF="admin_views.urls")
|
ROOT_URLCONF="admin_views.urls")
|
||||||
|
|
|
@ -11,4 +11,5 @@ urlpatterns = [
|
||||||
url(r'^test_admin/admin3/', include(admin.site.urls), dict(form_url='pony')),
|
url(r'^test_admin/admin3/', include(admin.site.urls), dict(form_url='pony')),
|
||||||
url(r'^test_admin/admin4/', include(customadmin.simple_site.urls)),
|
url(r'^test_admin/admin4/', include(customadmin.simple_site.urls)),
|
||||||
url(r'^test_admin/admin5/', include(admin.site2.urls)),
|
url(r'^test_admin/admin5/', include(admin.site2.urls)),
|
||||||
|
url(r'^test_admin/admin7/', include(admin.site7.urls)),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1542,3 +1542,93 @@ class ListDisplayEditableTests(CheckTestCase):
|
||||||
list_editable = ['name', 'slug']
|
list_editable = ['name', 'slug']
|
||||||
list_display_links = ['pub_date']
|
list_display_links = ['pub_date']
|
||||||
self.assertIsValid(ProductAdmin, ValidationTestModel)
|
self.assertIsValid(ProductAdmin, ValidationTestModel)
|
||||||
|
|
||||||
|
|
||||||
|
class ModelAdminPermissionTests(TestCase):
|
||||||
|
|
||||||
|
class MockUser(object):
|
||||||
|
def has_module_perms(self, app_label):
|
||||||
|
if app_label == "modeladmin":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
class MockAddUser(MockUser):
|
||||||
|
def has_perm(self, perm):
|
||||||
|
if perm == "modeladmin.add_band":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
class MockChangeUser(MockUser):
|
||||||
|
def has_perm(self, perm):
|
||||||
|
if perm == "modeladmin.change_band":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
class MockDeleteUser(MockUser):
|
||||||
|
def has_perm(self, perm):
|
||||||
|
if perm == "modeladmin.delete_band":
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
def test_has_add_permission(self):
|
||||||
|
"""
|
||||||
|
Ensure that has_add_permission returns True for users who can add
|
||||||
|
objects and False for users who can't.
|
||||||
|
"""
|
||||||
|
ma = ModelAdmin(Band, AdminSite())
|
||||||
|
request = MockRequest()
|
||||||
|
request.user = self.MockAddUser()
|
||||||
|
self.assertTrue(ma.has_add_permission(request))
|
||||||
|
request.user = self.MockChangeUser()
|
||||||
|
self.assertFalse(ma.has_add_permission(request))
|
||||||
|
request.user = self.MockDeleteUser()
|
||||||
|
self.assertFalse(ma.has_add_permission(request))
|
||||||
|
|
||||||
|
def test_has_change_permission(self):
|
||||||
|
"""
|
||||||
|
Ensure that has_change_permission returns True for users who can edit
|
||||||
|
objects and False for users who can't.
|
||||||
|
"""
|
||||||
|
ma = ModelAdmin(Band, AdminSite())
|
||||||
|
request = MockRequest()
|
||||||
|
request.user = self.MockAddUser()
|
||||||
|
self.assertFalse(ma.has_change_permission(request))
|
||||||
|
request.user = self.MockChangeUser()
|
||||||
|
self.assertTrue(ma.has_change_permission(request))
|
||||||
|
request.user = self.MockDeleteUser()
|
||||||
|
self.assertFalse(ma.has_change_permission(request))
|
||||||
|
|
||||||
|
def test_has_delete_permission(self):
|
||||||
|
"""
|
||||||
|
Ensure that has_delete_permission returns True for users who can delete
|
||||||
|
objects and False for users who can't.
|
||||||
|
"""
|
||||||
|
ma = ModelAdmin(Band, AdminSite())
|
||||||
|
request = MockRequest()
|
||||||
|
request.user = self.MockAddUser()
|
||||||
|
self.assertFalse(ma.has_delete_permission(request))
|
||||||
|
request.user = self.MockChangeUser()
|
||||||
|
self.assertFalse(ma.has_delete_permission(request))
|
||||||
|
request.user = self.MockDeleteUser()
|
||||||
|
self.assertTrue(ma.has_delete_permission(request))
|
||||||
|
|
||||||
|
def test_has_module_permission(self):
|
||||||
|
"""
|
||||||
|
Ensure that has_module_permission returns True for users who have any
|
||||||
|
permission for the module and False for users who don't.
|
||||||
|
"""
|
||||||
|
ma = ModelAdmin(Band, AdminSite())
|
||||||
|
request = MockRequest()
|
||||||
|
request.user = self.MockAddUser()
|
||||||
|
self.assertTrue(ma.has_module_permission(request))
|
||||||
|
request.user = self.MockChangeUser()
|
||||||
|
self.assertTrue(ma.has_module_permission(request))
|
||||||
|
request.user = self.MockDeleteUser()
|
||||||
|
self.assertTrue(ma.has_module_permission(request))
|
||||||
|
ma.opts.app_label = "anotherapp"
|
||||||
|
request.user = self.MockAddUser()
|
||||||
|
self.assertFalse(ma.has_module_permission(request))
|
||||||
|
request.user = self.MockChangeUser()
|
||||||
|
self.assertFalse(ma.has_module_permission(request))
|
||||||
|
request.user = self.MockDeleteUser()
|
||||||
|
self.assertFalse(ma.has_module_permission(request))
|
||||||
|
|
Loading…
Reference in New Issue