From 75a1aaa1f9341e558b6efe9227cf663d55704469 Mon Sep 17 00:00:00 2001 From: Russell Keith-Magee Date: Sat, 19 Feb 2011 14:05:07 +0000 Subject: [PATCH] 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. git-svn-id: http://code.djangoproject.com/svn/django/trunk@15584 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/contrib/admin/options.py | 8 ++++- tests/regressiontests/admin_views/models.py | 8 +++++ tests/regressiontests/admin_views/tests.py | 37 ++++++++++++++++++++- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index f7b979bc3d..42c1516691 100644 --- a/django/contrib/admin/options.py +++ b/django/contrib/admin/options.py @@ -766,7 +766,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 58a545a64a..7d6cab3cbf 100644 --- a/tests/regressiontests/admin_views/models.py +++ b/tests/regressiontests/admin_views/models.py @@ -133,6 +133,13 @@ class ArticleAdmin(admin.ModelAdmin): ).send() return super(ArticleAdmin, self).save_model(request, obj, form, change) +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 CustomArticle(models.Model): content = models.TextField() @@ -735,6 +742,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 f77c64bd21..642186eee2 100644 --- a/tests/regressiontests/admin_views/tests.py +++ b/tests/regressiontests/admin_views/tests.py @@ -35,7 +35,8 @@ 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, Chapter, Book, Promo, WorkHour, Employee, - Question, Answer, Inquisition, Actor, FoodDelivery) + Question, Answer, Inquisition, Actor, FoodDelivery, + RowLevelChangePermissionModel) class AdminViewBasicTest(TestCase): @@ -792,6 +793,40 @@ 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.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.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 testConditionallyShowAddSectionLink(self): """ The foreign key widget should only show the "add related" button if the