summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Meyer <carl@oddbird.net>2013-02-04 16:57:59 -0700
committerCarl Meyer <carl@oddbird.net>2013-02-19 10:37:54 -0700
commit0e7861aec73702f7933ce2a93056f7983939f0d6 (patch)
tree40c442ca4e278a75200ce4c2702e049edb45a57a
parent1c60d07ba23e0350351c278ad28d0bd5aa410b40 (diff)
[1.4.x] Checked object permissions on admin history view.
This is a security fix. Disclosure and advisory coming shortly. Patch by Russell Keith-Magee.
-rw-r--r--django/contrib/admin/options.py10
-rw-r--r--tests/regressiontests/admin_views/tests.py40
2 files changed, 48 insertions, 2 deletions
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py
index 2071792bdb..78a08cd120 100644
--- a/django/contrib/admin/options.py
+++ b/django/contrib/admin/options.py
@@ -1317,15 +1317,21 @@ class ModelAdmin(BaseModelAdmin):
def history_view(self, request, object_id, extra_context=None):
"The 'history' admin view for this model."
from django.contrib.admin.models import LogEntry
+ # First check if the user can see this history.
model = self.model
+ obj = get_object_or_404(model, pk=unquote(object_id))
+
+ if not self.has_change_permission(request, obj):
+ raise PermissionDenied
+
+ # Then get the history for this object.
opts = model._meta
app_label = opts.app_label
action_list = LogEntry.objects.filter(
object_id = object_id,
content_type__id__exact = ContentType.objects.get_for_model(model).id
).select_related().order_by('action_time')
- # If no history was found, see whether this object even exists.
- obj = get_object_or_404(model, pk=unquote(object_id))
+
context = {
'title': _('Change history: %s') % force_unicode(obj),
'action_list': action_list,
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py
index 8835e816dc..5531cb4e96 100644
--- a/tests/regressiontests/admin_views/tests.py
+++ b/tests/regressiontests/admin_views/tests.py
@@ -1064,6 +1064,46 @@ class AdminViewPermissionsTest(TestCase):
self.assertContains(request, 'login-form')
self.client.get('/test_admin/admin/logout/')
+ def testHistoryView(self):
+ """History view should restrict access."""
+
+ # add user shoud not be able to view the list of article or change any of them
+ self.client.get('/test_admin/admin/')
+ self.client.post('/test_admin/admin/', self.adduser_login)
+ response = self.client.get('/test_admin/admin/admin_views/article/1/history/')
+ self.assertEqual(response.status_code, 403)
+ self.client.get('/test_admin/admin/logout/')
+
+ # change user can view all items and edit them
+ self.client.get('/test_admin/admin/')
+ self.client.post('/test_admin/admin/', self.changeuser_login)
+ response = self.client.get('/test_admin/admin/admin_views/article/1/history/')
+ self.assertEqual(response.status_code, 200)
+
+ # Test redirection when using row-level change permissions. Refs #11513.
+ RowLevelChangePermissionModel.objects.create(id=1, name="odd id")
+ RowLevelChangePermissionModel.objects.create(id=2, name="even id")
+ for login_dict in [self.super_login, self.changeuser_login, self.adduser_login, self.deleteuser_login]:
+ self.client.post('/test_admin/admin/', login_dict)
+ response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/history/')
+ self.assertEqual(response.status_code, 403)
+
+ response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/history/')
+ self.assertEqual(response.status_code, 200)
+
+ self.client.get('/test_admin/admin/logout/')
+
+ for login_dict in [self.joepublic_login, self.no_username_login]:
+ self.client.post('/test_admin/admin/', login_dict)
+ response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/history/')
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, 'login-form')
+ response = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/history/')
+ self.assertEqual(response.status_code, 200)
+ self.assertContains(response, 'login-form')
+
+ self.client.get('/test_admin/admin/logout/')
+
def testConditionallyShowAddSectionLink(self):
"""
The foreign key widget should only show the "add related" button if the