From 626ad2c9ab9e02db8e3f19bd51654d749e84af02 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Sat, 19 Feb 2011 14:30:23 +0000 Subject: [PATCH] [1.2.X] Fixed #11513 -- Ensure that the redirect at the end of an object change won't redirect to a page for which the user doesn't have permission. Thanks to rlaager for the report and draft patch, and to Julien Phalip for the final patch. Backport of r15584 from trunk. git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@15586 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/admin/options.py | 8 ++++- tests/regressiontests/admin_views/models.py | 8 +++++ tests/regressiontests/admin_views/tests.py | 38 ++++++++++++++++++++- 3 files changed, 52 insertions(+), 2 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index a80509f801..6120182c87 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -749,7 +749,13 @@ class ModelAdmin(BaseModelAdmin): return HttpResponseRedirect("../add/") else: self.message_user(request, msg) - return HttpResponseRedirect("../") + # Figure out where to redirect. If the user has change permission, + # redirect to the change-list page for this object. Otherwise, + # redirect to the admin index. + if self.has_change_permission(request, None): + return HttpResponseRedirect('../') + else: + return HttpResponseRedirect('../../../') def response_action(self, request, queryset): """ diff --git a/tests/regressiontests/admin_views/models.py b/tests/regressiontests/admin_views/models.py index 6e0f9997fb..17d3dffe4d 100644 --- a/tests/regressiontests/admin_views/models.py +++ b/tests/regressiontests/admin_views/models.py @@ -232,6 +232,13 @@ class PersonAdmin(admin.ModelAdmin): return super(PersonAdmin, self).get_changelist_formset(request, formset=BasePersonModelFormSet, **kwargs) +class RowLevelChangePermissionModel(models.Model): + name = models.CharField(max_length=100, blank=True) + +class RowLevelChangePermissionModelAdmin(admin.ModelAdmin): + def has_change_permission(self, request, obj=None): + """ Only allow changing objects with even id number """ + return request.user.is_staff and (obj is not None) and (obj.id % 2 == 0) class Persona(models.Model): """ @@ -698,6 +705,7 @@ admin.site.register(CyclicTwo) admin.site.register(WorkHour, WorkHourAdmin) admin.site.register(Reservation) admin.site.register(FoodDelivery, FoodDeliveryAdmin) +admin.site.register(RowLevelChangePermissionModel, RowLevelChangePermissionModelAdmin) # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. # That way we cover all four cases: diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index e93ec66e4d..be6c6bb05e 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -31,7 +31,7 @@ from models import (Article, BarAccount, CustomArticle, EmptyModel, Person, Persona, Picture, Podcast, Section, Subscriber, Vodcast, Language, Collector, Widget, Grommet, DooHickey, FancyDoodad, Whatsit, Category, Post, Plot, FunkyTag, WorkHour, Employee, Inquisition, - Actor, FoodDelivery) + Actor, FoodDelivery, RowLevelChangePermissionModel) class AdminViewBasicTest(TestCase): @@ -707,6 +707,42 @@ class AdminViewPermissionsTest(TestCase): 'Plural error message not found in response to post with multiple errors.') self.client.get('/test_admin/admin/logout/') + # Test redirection when using row-level change permissions. Refs #11513. + RowLevelChangePermissionModel.objects.create(name="odd id") + RowLevelChangePermissionModel.objects.create(name="even id") + for login_dict in [self.super_login, self.changeuser_login, self.adduser_login, self.deleteuser_login]: + self.client.get('/test_admin/admin/') + self.client.post('/test_admin/admin/', login_dict) + request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/') + self.assertEqual(request.status_code, 403) + request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'}) + self.assertEquals(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id') + self.assertEqual(request.status_code, 403) + request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/') + self.assertEqual(request.status_code, 200) + request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed'}) + self.assertEquals(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed') + self.assertRedirects(request, '/test_admin/admin/') + self.client.get('/test_admin/admin/logout/') + for login_dict in [self.joepublic_login, self.no_username_login]: + self.client.get('/test_admin/admin/') + self.client.post('/test_admin/admin/', login_dict) + request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/') + self.assertEqual(request.status_code, 200) + self.assertContains(request, 'login-form') + request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/1/', {'name': 'changed'}) + self.assertEquals(RowLevelChangePermissionModel.objects.get(id=1).name, 'odd id') + self.assertEqual(request.status_code, 200) + self.assertContains(request, 'login-form') + request = self.client.get('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/') + self.assertEqual(request.status_code, 200) + self.assertContains(request, 'login-form') + request = self.client.post('/test_admin/admin/admin_views/rowlevelchangepermissionmodel/2/', {'name': 'changed again'}) + self.assertEquals(RowLevelChangePermissionModel.objects.get(id=2).name, 'changed') + self.assertEqual(request.status_code, 200) + self.assertContains(request, 'login-form') + self.client.get('/test_admin/admin/logout/') + def testCustomModelAdminTemplates(self): self.client.get('/test_admin/admin/') self.client.post('/test_admin/admin/', self.super_login)