Fixed #23431 -- Allowed inline and hidden references to admin fields.

This fixes a regression introduced by the 53ff096982 security fix.

Thanks to @a1tus for the report and Tim for the review.

refs #23329.
This commit is contained in:
Simon Charette 2014-09-04 17:04:53 -04:00
parent cbbc7131c1
commit 342ccbddc1
9 changed files with 85 additions and 4 deletions

View File

@ -439,6 +439,10 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
return clean_lookup in valid_lookups return clean_lookup in valid_lookups
def to_field_allowed(self, request, to_field): def to_field_allowed(self, request, to_field):
"""
Returns True if the model associated with this admin should be
allowed to be referenced by the specified field.
"""
opts = self.model._meta opts = self.model._meta
try: try:
@ -448,8 +452,13 @@ class BaseModelAdmin(six.with_metaclass(forms.MediaDefiningClass)):
# Make sure at least one of the models registered for this site # Make sure at least one of the models registered for this site
# references this field through a FK or a M2M relationship. # references this field through a FK or a M2M relationship.
registered_models = self.admin_site._registry registered_models = set()
for related_object in (opts.get_all_related_objects() + for model, admin in self.admin_site._registry.items():
registered_models.add(model)
for inline in admin.inlines:
registered_models.add(inline.model)
for related_object in (opts.get_all_related_objects(include_hidden=True) +
opts.get_all_related_many_to_many_objects()): opts.get_all_related_many_to_many_objects()):
related_model = related_object.model related_model = related_object.model
if (any(issubclass(model, related_model) for model in registered_models) and if (any(issubclass(model, related_model) for model in registered_models) and

13
docs/releases/1.4.16.txt Normal file
View File

@ -0,0 +1,13 @@
===========================
Django 1.4.16 release notes
===========================
*Under development*
Django 1.4.16 fixes a regression in the 1.4.14 security release.
Bugfixes
========
* Allowed inline and hidden references to admin fields
(`#23431 <http://code.djangoproject.com/ticket/23431>`_).

13
docs/releases/1.5.11.txt Normal file
View File

@ -0,0 +1,13 @@
===========================
Django 1.5.11 release notes
===========================
*Under development*
Django 1.5.11 fixes a regression in the 1.5.9 security release.
Bugfixes
========
* Allowed inline and hidden references to admin fields
(`#23431 <http://code.djangoproject.com/ticket/23431>`_).

12
docs/releases/1.6.8.txt Normal file
View File

@ -0,0 +1,12 @@
==========================
Django 1.6.8 release notes
==========================
*Under development*
Django 1.6.8 fixes a regression in the 1.6.6 security release.
Bugfixes
========
* Allowed inline and hidden references to admin fields (:ticket:`23431`).

View File

@ -18,3 +18,5 @@ Bugfixes
when not using migrations (:ticket:`23416`). when not using migrations (:ticket:`23416`).
* Fixed serialization of ``type`` objects in migrations (:ticket:`22951`). * Fixed serialization of ``type`` objects in migrations (:ticket:`22951`).
* Allowed inline and hidden references to admin fields (:ticket:`23431`).

View File

@ -40,6 +40,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
1.6.8
1.6.7 1.6.7
1.6.6 1.6.6
1.6.5 1.6.5
@ -54,6 +55,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
1.5.11
1.5.10 1.5.10
1.5.9 1.5.9
1.5.8 1.5.8
@ -71,6 +73,7 @@ versions of the documentation contain the release notes for any later releases.
.. toctree:: .. toctree::
:maxdepth: 1 :maxdepth: 1
1.4.16
1.4.15 1.4.15
1.4.14 1.4.14
1.4.13 1.4.13

View File

@ -36,7 +36,8 @@ from .models import (Article, Chapter, Child, Parent, Picture, Widget,
FilteredManager, EmptyModelHidden, EmptyModelVisible, EmptyModelMixin, FilteredManager, EmptyModelHidden, EmptyModelVisible, EmptyModelMixin,
State, City, Restaurant, Worker, ParentWithDependentChildren, State, City, Restaurant, Worker, ParentWithDependentChildren,
DependentChild, StumpJoke, FieldOverridePost, FunkyTag, DependentChild, StumpJoke, FieldOverridePost, FunkyTag,
ReferencedByParent, ChildOfReferer, M2MReference) ReferencedByParent, ChildOfReferer, M2MReference, ReferencedByInline,
InlineReference, InlineReferer)
def callable_year(dt_value): def callable_year(dt_value):
@ -832,6 +833,14 @@ class FunkyTagAdmin(admin.ModelAdmin):
list_display = ('name', 'content_object') list_display = ('name', 'content_object')
class InlineReferenceInline(admin.TabularInline):
model = InlineReference
class InlineRefererAdmin(admin.ModelAdmin):
inlines = [InlineReferenceInline]
site = admin.AdminSite(name="admin") site = admin.AdminSite(name="admin")
site.site_url = '/my-site-url/' site.site_url = '/my-site-url/'
site.register(Article, ArticleAdmin) site.register(Article, ArticleAdmin)
@ -892,6 +901,8 @@ site.register(FunkyTag, FunkyTagAdmin)
site.register(ReferencedByParent) site.register(ReferencedByParent)
site.register(ChildOfReferer) site.register(ChildOfReferer)
site.register(M2MReference) site.register(M2MReference)
site.register(ReferencedByInline)
site.register(InlineReferer, InlineRefererAdmin)
# We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2. # We intentionally register Promo and ChapterXtra1 but not Chapter nor ChapterXtra2.
# That way we cover all four cases: # That way we cover all four cases:

View File

@ -839,3 +839,16 @@ class ChildOfReferer(ParentWithFK):
class M2MReference(models.Model): class M2MReference(models.Model):
ref = models.ManyToManyField('self') ref = models.ManyToManyField('self')
# Models for #23431
class ReferencedByInline(models.Model):
pass
class InlineReference(models.Model):
fk = models.ForeignKey(ReferencedByInline, related_name='hidden+')
class InlineReferer(models.Model):
refs = models.ManyToManyField(InlineReference)

View File

@ -620,11 +620,16 @@ class AdminViewBasicTest(AdminViewBasicTestCase):
response = self.client.get("/test_admin/admin/admin_views/m2mreference/", {TO_FIELD_VAR: 'id'}) response = self.client.get("/test_admin/admin/admin_views/m2mreference/", {TO_FIELD_VAR: 'id'})
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
# Specifying a field that is not refered by any other model directly registered # #23329 - Specifying a field that is not refered by any other model directly registered
# to this admin site but registered through inheritance should be allowed. # to this admin site but registered through inheritance should be allowed.
response = self.client.get("/test_admin/admin/admin_views/referencedbyparent/", {TO_FIELD_VAR: 'id'}) response = self.client.get("/test_admin/admin/admin_views/referencedbyparent/", {TO_FIELD_VAR: 'id'})
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
# #23431 - Specifying a field that is only refered to by a inline of a registered
# model should be allowed.
response = self.client.get("/test_admin/admin/admin_views/referencedbyinline/", {TO_FIELD_VAR: 'id'})
self.assertEqual(response.status_code, 200)
# We also want to prevent the add and change view from leaking a # We also want to prevent the add and change view from leaking a
# disallowed field value. # disallowed field value.
with patch_logger('django.security.DisallowedModelAdminToField', 'error') as calls: with patch_logger('django.security.DisallowedModelAdminToField', 'error') as calls: