Ticket #7167: 7167-safeform1.diff

File 7167-safeform1.diff, 5.7 KB (added by ElliottM, 16 years ago)

revised docstring

  • django/contrib/csrf/tests.py

     
     1"""
     2
     3>>> from django.conf import settings
     4>>> from django.http import HttpRequest
     5>>> from django.contrib.csrf.forms import SafeForm
     6
     7>>> settings.SECRET_KEY='secret'
     8>>> settings.SESSION_COOKIE_NAME='session_id'
     9
     10#If SafeForm is not passed a HttpRequest object, a ValueError is raised
     11>>> form=SafeForm()
     12Traceback (most recent call last):
     13  ...
     14ValueError: SafeForm must be given a HttpRequest object
     15
     16#if the user who made the request does not have the session cookie set,
     17#a csrf token cannot be generated, so the value for the token should be empty
     18>>> request=HttpRequest()
     19>>> form=SafeForm(request=request)
     20>>> form.csrf_token_field()
     21u'<input type=\"hidden\" name=\"_csrf_token\" id=\"id__csrf_token\" />'
     22
     23#if a user that does not send any cookies tries to submit a form,
     24#SafeForm should automatically invalidate it
     25>>> request=HttpRequest()
     26>>> request.POST['_csrf_token']='2376e3ffd767c170fab368189b7e4799'
     27>>> form=SafeForm(request.POST, request=request)
     28>>> form.is_valid()
     29False
     30>>> form.non_field_errors()
     31[u'Your session has expired. Please refresh the page and submit the form again.']
     32
     33#if the request has a valid session ID cookie, generate a token for it
     34>>> request=HttpRequest()
     35>>> request.COOKIES['session_id']='abcde'
     36>>> form=SafeForm(request=request)
     37>>> form.csrf_token_field()
     38u'<input type=\"hidden\" name=\"_csrf_token\" value=\"2376e3ffd767c170fab368189b7e4799\" id=\"id__csrf_token\" />'
     39
     40#if a user submits a form that doesn't have a csrf token at all, the form is not valid
     41>>> form=SafeForm(request.POST,request=request)
     42>>> form.is_valid()
     43False
     44>>> form.non_field_errors()
     45[u'Your session has expired. Please refresh the page and submit the form again.']
     46
     47#if a user submits a form with an incorrect csrf token, the form is not valid.
     48>>> request=HttpRequest()
     49>>> request.POST['_csrf_token']='hello'
     50>>> request.COOKIES['session_id']='abcde'
     51>>> form=SafeForm(request.POST,request=request)
     52>>> form.is_valid()
     53False
     54>>> form.non_field_errors()
     55[u'Your session has expired. Please refresh the page and submit the form again.']
     56
     57#if a user submits a form with the correct token, only then should is_valid() be True
     58>>> request=HttpRequest()
     59>>> request.POST['_csrf_token']='2376e3ffd767c170fab368189b7e4799'
     60>>> request.COOKIES['session_id']='abcde'
     61>>> form=SafeForm(request.POST,request=request)
     62>>> form.is_valid()
     63True
     64
     65"""
     66
     67if __name__ == '__main__':
     68    import doctest
     69    doctest.testmod()
  • django/contrib/csrf/forms.py

     
     1from django import forms
     2from django.conf import settings
     3from django.utils.hashcompat import md5_constructor
     4from django.utils.safestring import mark_safe
     5
     6class SafeForm(forms.Form):
     7    """
     8    Form that adds protection against Cross Site Request Forgeries by adding
     9    a hidden field and checking for the correct value during validation.
     10   
     11    It works virtually the same as a regular form, with the exception of an
     12    additional parameter passed to the constructor - an HttpRequest object.
     13    It uses the request object to get the user's session ID.
     14   
     15    If the developer is manually specifying each field in their forms rather
     16    than calling form.as_table or some other helper, they will also need to
     17    add {{form.csrf_token_field}} somewhere inside the form in their template
     18   
     19    This form does not rely on django.contrib.sessions to work as long as
     20    whatever backend is ultimately used honors the settings.SESSION_COOKIE_NAME
     21    setting. If that cookie is unset, safeform.is_valid() will always be false
     22    """
     23   
     24    _csrf_token = forms.CharField(widget=forms.HiddenInput, required=False)
     25    INVALID_TOKEN_MSG = "Your session has expired. Please refresh the "\
     26                    + "page and submit the form again."
     27   
     28    def __init__(self, *args, **kwargs):
     29        try:
     30            self._request=kwargs.pop('request')
     31        except KeyError:
     32            raise ValueError("SafeForm must be given a HttpRequest object")
     33       
     34        super(SafeForm,self).__init__(*args, **kwargs)
     35       
     36        #We cannot set the initial in the field declaration because the
     37        #_make_token call needs the reference to "self"
     38        self.fields['_csrf_token'].initial=self._make_token
     39   
     40    def clean(self):
     41        session_token = self._make_token()
     42       
     43        if '_csrf_token' not in self.cleaned_data or self.cleaned_data['_csrf_token'] != session_token or session_token is None:
     44            raise forms.ValidationError(self.INVALID_TOKEN_MSG)
     45       
     46        return self.cleaned_data
     47       
     48       
     49    def _make_token(self):
     50        try:
     51            session_id = self._request.COOKIES[settings.SESSION_COOKIE_NAME]
     52            return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
     53        except KeyError:
     54            #if the user does not have any cookies set,
     55            #a token cannot be generated
     56            return None
     57   
     58    def csrf_token_field(self):
     59        return mark_safe(unicode(self['_csrf_token']))
     60 No newline at end of file
  • AUTHORS

     
    425425    ymasuda@ethercube.com
    426426    Jarek Zgoda <jarek.zgoda@gmail.com>
    427427    Cheng Zhang
     428    Elliott Mahler <join.together@gmail.com>
    428429
    429430A big THANK YOU goes to:
    430431
Back to Top