Code

Ticket #3987: diff-dynamic.3.diff

File diff-dynamic.3.diff, 8.6 KB (added by Baptiste <baptiste.goupil@…>, 7 years ago)

security fix : the choice of an item not in the list of choices must fail

Line 
1Index: db/models/fields/related.py
2===================================================================
3--- db/models/fields/related.py (revision 5636)
4+++ db/models/fields/related.py (working copy)
5@@ -544,7 +544,10 @@
6         setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
7 
8     def formfield(self, **kwargs):
9-        defaults = {'form_class': forms.ModelChoiceField, 'queryset': self.rel.to._default_manager.all()}
10+        defaults = {'form_class': forms.ModelChoiceField}
11+        #To prevent an useless query
12+        if not 'queryset' in kwargs:
13+            defaults['queryset'] = self.rel.to._default_manager.all()
14         defaults.update(kwargs)
15         return super(ForeignKey, self).formfield(**defaults)
16 
17@@ -605,7 +608,9 @@
18             cls._meta.one_to_one_field = self
19 
20     def formfield(self, **kwargs):
21-        defaults = {'form_class': forms.ModelChoiceField, 'queryset': self.rel.to._default_manager.all()}
22+        defaults = {'form_class': forms.ModelChoiceField}
23+        if not 'queryset' in kwargs:
24+            defaults['queryset'] = self.rel.to._default_manager.all()
25         defaults.update(kwargs)
26         return super(OneToOneField, self).formfield(**defaults)
27 
28@@ -711,7 +716,9 @@
29         return getattr(obj, self.attname).all()
30 
31     def formfield(self, **kwargs):
32-        defaults = {'form_class': forms.ModelMultipleChoiceField, 'queryset': self.rel.to._default_manager.all()}
33+        defaults = {'form_class': forms.ModelMultipleChoiceField}
34+        if not 'queryset' in kwargs:
35+            defaults['queryset'] = self.rel.to._default_manager.all()
36         defaults.update(kwargs)
37         # If initial is passed in, it's a list of related objects, but the
38         # MultipleChoiceField takes a list of IDs.
39Index: newforms/models.py
40===================================================================
41--- newforms/models.py  (revision 5636)
42+++ newforms/models.py  (working copy)
43@@ -170,7 +170,7 @@
44         if value in ('', None):
45             return None
46         try:
47-            value = self.queryset.model._default_manager.get(pk=value)
48+            value = self.queryset.get(pk=value)
49         except self.queryset.model.DoesNotExist:
50             raise ValidationError(ugettext(u'Select a valid choice. That choice is not one of the available choices.'))
51         return value
52@@ -193,7 +193,7 @@
53         final_values = []
54         for val in value:
55             try:
56-                obj = self.queryset.model._default_manager.get(pk=val)
57+                obj = self.queryset.get(pk=val)
58             except self.queryset.model.DoesNotExist:
59                 raise ValidationError(ugettext(u'Select a valid choice. %s is not one of the available choices.') % val)
60             else:
61Index: contrib/admin/options.py
62===================================================================
63--- contrib/admin/options.py    (revision 5636)
64+++ contrib/admin/options.py    (working copy)
65@@ -103,7 +103,7 @@
66     raw_id_fields = ()
67     fields = None
68 
69-    def formfield_for_dbfield(self, db_field, **kwargs):
70+    def formfield_for_dbfield(self, db_field, request, **kwargs):
71         """
72         Hook for specifying the form Field instance for a given database Field
73         instance.
74@@ -113,7 +113,8 @@
75         # For ManyToManyFields with a filter interface, use a special widget.
76         if isinstance(db_field, models.ManyToManyField) and db_field.name in (self.filter_vertical + self.filter_horizontal):
77             kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical))
78-            return db_field.formfield(**kwargs)
79+            if hasattr(self, 'dynamic_%s_choices' % db_field.name):
80+                formfield = db_field.formfield(queryset = getattr(self, 'dynamic_%s_choices' % db_field.name)(request, db_field.rel.to), **kwargs)
81 
82         # For DateTimeFields, use a special field and widget.
83         if isinstance(db_field, models.DateTimeField):
84@@ -133,15 +134,20 @@
85 
86         # For ForeignKey or ManyToManyFields, use a special widget.
87         if isinstance(db_field, (models.ForeignKey, models.ManyToManyField)):
88+            if hasattr(self, 'dynamic_%s_choices' % db_field.name):
89+                formfield = db_field.formfield(queryset = getattr(self, 'dynamic_%s_choices' % db_field.name)(request, db_field.rel.to), **kwargs)
90+            else:
91+                formfield = db_field.formfield(**kwargs)
92             if isinstance(db_field, models.ForeignKey) and db_field.name in self.raw_id_fields:
93-                kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel)
94-                return db_field.formfield(**kwargs)
95+                formfield.widget = widgets.ForeignKeyRawIdWidget(db_field.rel)
96             else:
97                 # Wrap the widget's render() method with a method that adds
98                 # extra HTML to the end of the rendered output.
99-                formfield = db_field.formfield(**kwargs)
100                 formfield.widget.render = widgets.RelatedFieldWidgetWrapper(formfield.widget.render, db_field.rel)
101-                return formfield
102+            return formfield
103+       
104+        if isinstance(db_field, (models.OneToOneField)) and hasattr(self, 'dynamic_%s_choices' % db_field.name):
105+            return db_field.formfield(queryset = getattr(self, 'dynamic_%s_choices' % db_field.name)(request, db_field.rel.to), **kwargs)
106 
107         # For any other type of field, just call its formfield() method.
108         return db_field.formfield(**kwargs)
109@@ -414,7 +420,7 @@
110             # Object list will give 'Permission Denied', so go back to admin home
111             post_url = '../../../'
112 
113-        ModelForm = forms.form_for_model(model, formfield_callback=self.formfield_for_dbfield)
114+        ModelForm = forms.form_for_model(model, formfield_callback=lambda f, **kwargs: self.formfield_for_dbfield(f, request, **kwargs))
115 
116         inline_formsets = []
117         if request.POST:
118@@ -422,14 +428,14 @@
119             if opts.has_field_type(models.FileField):
120                 new_data.update(request.FILES)
121             form = ModelForm(new_data)
122-            for FormSet in self.get_inline_formsets():
123+            for FormSet in self.get_inline_formsets(request):
124                 inline_formset = FormSet(data=new_data)
125                 inline_formsets.append(inline_formset)
126             if all_valid(inline_formsets) and form.is_valid():
127                 return self.save_add(request, model, form, inline_formsets, '../%s/')
128         else:
129             form = ModelForm(initial=request.GET)
130-            for FormSet in self.get_inline_formsets():
131+            for FormSet in self.get_inline_formsets(request):
132                 inline_formset = FormSet()
133                 inline_formsets.append(inline_formset)
134 
135@@ -467,7 +473,7 @@
136         if request.POST and request.POST.has_key("_saveasnew"):
137             return self.add_view(request, form_url='../../add/')
138 
139-        ModelForm = forms.form_for_instance(obj, formfield_callback=self.formfield_for_dbfield)
140+        ModelForm = forms.form_for_instance(obj, formfield_callback=lambda f, **kwargs: self.formfield_for_dbfield(f, request, **kwargs))
141 
142         inline_formsets = []
143         if request.POST:
144@@ -475,7 +481,7 @@
145             if opts.has_field_type(models.FileField):
146                 new_data.update(request.FILES)
147             form = ModelForm(new_data)
148-            for FormSet in self.get_inline_formsets():
149+            for FormSet in self.get_inline_formsets(request):
150                 inline_formset = FormSet(obj, new_data)
151                 inline_formsets.append(inline_formset)
152 
153@@ -483,7 +489,7 @@
154                 return self.save_change(request, model, form, inline_formsets)
155         else:
156             form = ModelForm()
157-            for FormSet in self.get_inline_formsets():
158+            for FormSet in self.get_inline_formsets(request):
159                 inline_formset = FormSet(obj)
160                 inline_formsets.append(inline_formset)
161 
162@@ -610,10 +616,10 @@
163         ]
164         return render_to_response(template_list, extra_context, context_instance=template.RequestContext(request))
165 
166-    def get_inline_formsets(self):
167+    def get_inline_formsets(self, request):
168         inline_formset_classes = []
169         for opts in self.inlines:
170-            inline = inline_formset(self.model, opts.model, formfield_callback=opts.formfield_for_dbfield, fields=opts.fields, extra=opts.extra)
171+            inline = inline_formset(self.model, opts.model, formfield_callback=lambda f, **kwargs: opts.formfield_for_dbfield(f, request, **kwargs), fields=opts.fields, extra=opts.extra)
172             inline_formset_classes.append(inline)
173         return inline_formset_classes
174