Ticket #6209: patch-#6209-preview_wizard.diff
File patch-#6209-preview_wizard.diff, 7.8 KB (added by , 16 years ago) |
---|
-
django/contrib/formtools/wizard.py
12 12 from django.shortcuts import render_to_response 13 13 from django.template.context import RequestContext 14 14 from django.utils.hashcompat import md5_constructor 15 from django.contrib.formtools.utils import security_hash 15 16 16 17 class FormWizard(object): 17 18 # Dictionary of extra template context variables. … … 140 141 """ 141 142 Calculates the security hash for the given HttpRequest and Form instances. 142 143 143 This creates a list of the form field names/values in a deterministic144 order, pickles the result with the SECRET_KEY setting and takes an md5145 hash of that.146 147 144 Subclasses may want to take into account request-specific information, 148 145 such as the IP address. 149 146 """ 150 data = [(bf.name, bf.data or '') for bf in form] + [settings.SECRET_KEY] 151 # Use HIGHEST_PROTOCOL because it's the most efficient. It requires 152 # Python 2.3, but Django requires 2.3 anyway, so that's OK. 153 pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) 154 return md5_constructor(pickled).hexdigest() 147 return security_hash(request, form) 155 148 156 149 def determine_step(self, request, *args, **kwargs): 157 150 """ -
django/contrib/formtools/utils.py
1 import cPickle as pickle 2 3 from django.conf import settings 4 from django.utils.hashcompat import md5_constructor 5 from django.forms import BooleanField 6 7 def security_hash(request, form): 8 """ 9 Calculates the security hash for the given Form instance. 10 11 This creates a list of the form field names/values in a deterministic 12 order, pickles the result with the SECRET_KEY setting and takes an md5 13 hash of that. 14 """ 15 # Ensure that the hash does not change when a BooleanField's bound 16 # data is a string `False' or a boolean False. 17 # DRY: Rather than re-coding this special behaviour here, we 18 # create a dummy BooleanField and call its clean method to get a 19 # boolean True or False verdict that is consistent with 20 # BooleanField.clean() 21 dummy_bool = BooleanField(required=False) 22 def _cleaned_data(bf): 23 if isinstance(bf.field, BooleanField): 24 return dummy_bool.clean(bf.data) 25 return bf.data 26 data = [(bf.name, _cleaned_data(bf) or '') for bf in form] + [settings.SECRET_KEY] 27 # Use HIGHEST_PROTOCOL because it's the most efficient. It requires 28 # Python 2.3, but Django requires 2.3 anyway, so that's OK. 29 pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) 30 return md5_constructor(pickled).hexdigest() 31 -
django/contrib/formtools/preview.py
9 9 from django.shortcuts import render_to_response 10 10 from django.template.context import RequestContext 11 11 from django.utils.hashcompat import md5_constructor 12 from django.contrib.formtools.utils import security_hash 12 13 13 14 AUTO_ID = 'formtools_%s' # Each form here uses this as its auto_id parameter. 14 15 … … 97 98 98 99 def security_hash(self, request, form): 99 100 """ 100 Calculates the security hash for the given Form instance.101 Calculates the security hash for the given HttpRequest and Form instances. 101 102 102 This creates a list of the form field names/values in a deterministic 103 order, pickles the result with the SECRET_KEY setting and takes an md5 104 hash of that. 105 106 Subclasses may want to take into account request-specific information 103 Subclasses may want to take into account request-specific information, 107 104 such as the IP address. 108 105 """ 109 data = [(bf.name, bf.data or '') for bf in form] + [settings.SECRET_KEY] 110 # Use HIGHEST_PROTOCOL because it's the most efficient. It requires 111 # Python 2.3, but Django requires 2.3 anyway, so that's OK. 112 pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) 113 return md5_constructor(pickled).hexdigest() 106 return security_hash(request, form) 114 107 115 108 def failed_hash(self, request): 116 109 "Returns an HttpResponse in the case of an invalid security hash." -
django/contrib/formtools/tests.py
4 4 from django.test import TestCase 5 5 6 6 success_string = "Done was called!" 7 test_data = {'field1': u'foo',8 'field1_': u'asdf'}9 7 10 8 11 9 class TestFormPreview(preview.FormPreview): … … 17 15 class TestForm(forms.Form): 18 16 field1 = forms.CharField() 19 17 field1_ = forms.CharField() 18 bool1 = forms.BooleanField(required=False) 20 19 21 20 22 21 class PreviewTests(TestCase): … … 27 26 self.preview = preview.FormPreview(TestForm) 28 27 input_template = '<input type="hidden" name="%s" value="%s" />' 29 28 self.input = input_template % (self.preview.unused_name('stage'), "%d") 29 self.test_data = {'field1':u'foo', 'field1_':u'asdf'} 30 30 31 31 def test_unused_name(self): 32 32 """ … … 59 59 """ 60 60 # Pass strings for form submittal and add stage variable to 61 61 # show we previously saw first stage of the form. 62 test_data.update({'stage': 1})63 response = self.client.post('/test1/', test_data)62 self.test_data.update({'stage': 1}) 63 response = self.client.post('/test1/', self.test_data) 64 64 # Check to confirm stage is set to 2 in output form. 65 65 stage = self.input % 2 66 66 self.assertContains(response, stage, 1) … … 77 77 """ 78 78 # Pass strings for form submittal and add stage variable to 79 79 # show we previously saw first stage of the form. 80 test_data.update({'stage':2})81 response = self.client.post('/test1/', test_data)80 self.test_data.update({'stage':2}) 81 response = self.client.post('/test1/', self.test_data) 82 82 self.failIfEqual(response.content, success_string) 83 hash = self.preview.security_hash(None, TestForm( test_data))84 test_data.update({'hash': hash})85 response = self.client.post('/test1/', test_data)83 hash = self.preview.security_hash(None, TestForm(self.test_data)) 84 self.test_data.update({'hash': hash}) 85 response = self.client.post('/test1/', self.test_data) 86 86 self.assertEqual(response.content, success_string) 87 87 88 def test_bool_submit(self): 89 """ 90 Test contrib.formtools.preview form submittal when form contains: 91 BooleanField(required=False) 92 93 Ticket: #6209 - When an unchecked BooleanField is previewed, the preview 94 form's hash would be computed with no value for ``bool1``. However, when 95 the preview form is rendered, the unchecked hidden BooleanField would be 96 rendered with the string value 'False'. So when the preview form is 97 resubmitted, the hash would be computed with the value 'False' for 98 ``bool1``. We need to make sure the hashes are the same in both cases. 99 100 """ 101 self.test_data.update({'stage':2}) 102 hash = self.preview.security_hash(None, TestForm(self.test_data)) 103 self.test_data.update({'hash':hash, 'bool1':u'False'}) 104 response = self.client.post('/test1/', self.test_data) 105 self.assertEqual(response.content, success_string) 106