PageRenderTime 46ms CodeModel.GetById 18ms app.highlight 24ms RepoModel.GetById 1ms app.codeStats 0ms

/app/api/packages/flaskext/wtf/__init__.py

https://gitlab.com/gregtyka/server
Python | 226 lines | 121 code | 50 blank | 55 comment | 14 complexity | 4b622cc509b0247b81942bcb38b2cb51 MD5 | raw file
  1# -*- coding: utf-8 -*-
  2"""
  3    flaskext.wtf
  4    ~~~~~~~~~~~~
  5
  6    Flask-WTF extension
  7
  8    :copyright: (c) 2010 by Dan Jacob.
  9    :license: BSD, see LICENSE for more details.
 10"""
 11
 12import warnings
 13import uuid
 14
 15from wtforms.fields import BooleanField, DecimalField, DateField, \
 16    DateTimeField, FieldList, FloatField, FormField, \
 17    HiddenField, IntegerField, PasswordField, RadioField, SelectField, \
 18    SelectMultipleField, SubmitField, TextField, TextAreaField
 19
 20from wtforms.validators import Email, email, EqualTo, equal_to, \
 21    IPAddress, ip_address, Length, length, NumberRange, number_range, \
 22    Optional, optional, Required, required, Regexp, regexp, \
 23    URL, url, AnyOf, any_of, NoneOf, none_of
 24
 25from wtforms.widgets import CheckboxInput, FileInput, HiddenInput, \
 26    ListWidget, PasswordInput, RadioInput, Select, SubmitInput, \
 27    TableWidget, TextArea, TextInput
 28
 29from wtforms.fields import FileField as _FileField
 30
 31try:
 32    import sqlalchemy
 33    _is_sqlalchemy = True
 34except ImportError:
 35    _is_sqlalchemy = False
 36
 37
 38from wtforms import Form as BaseForm
 39from wtforms import fields, widgets, validators, ValidationError
 40
 41from flask import request, session, current_app
 42
 43from jinja2 import Markup
 44
 45from flaskext.wtf import html5
 46
 47from flaskext.wtf import recaptcha
 48
 49from flaskext.wtf.recaptcha.fields import RecaptchaField
 50from flaskext.wtf.recaptcha.widgets import RecaptchaWidget
 51from flaskext.wtf.recaptcha.validators import Recaptcha
 52
 53fields.RecaptchaField = RecaptchaField
 54widgets.RecaptchaWidget = RecaptchaWidget
 55validators.Recaptcha = Recaptcha
 56
 57from flaskext.wtf.file import FileField
 58from flaskext.wtf.file import FileAllowed, FileRequired, file_allowed, \
 59        file_required
 60
 61fields.FileField = FileField
 62
 63validators.file_allowed = file_allowed
 64validators.file_required = file_required
 65validators.FileAllowed = FileAllowed
 66validators.FileRequired = FileRequired
 67
 68
 69__all__  = ['Form', 'ValidationError',
 70            'fields', 'validators', 'widgets', 'html5']
 71
 72__all__ += fields.__all__
 73__all__ += validators.__all__
 74__all__ += widgets.__all__
 75__all__ += recaptcha.__all__
 76
 77if _is_sqlalchemy:
 78    from wtforms.ext.sqlalchemy.fields import QuerySelectField, \
 79        QuerySelectMultipleField
 80
 81    __all__ += ['QuerySelectField', 
 82                'QuerySelectMultipleField']
 83
 84    for field in (QuerySelectField, 
 85                  QuerySelectMultipleField):
 86
 87        setattr(fields, field.__name__, field)
 88
 89
 90def _generate_csrf_token():
 91    return str(uuid.uuid4())
 92
 93
 94class Form(BaseForm):
 95
 96    """
 97    Subclass of WTForms **Form** class. The main difference is that
 98    **request.form** is passed as `formdata` argument to constructor
 99    so can handle request data implicitly. 
100
101    In addition this **Form** implementation has automatic CSRF handling.
102    """
103
104    csrf = fields.HiddenField()
105
106    def __init__(self, formdata=None, *args, **kwargs):
107
108        csrf_enabled = kwargs.pop('csrf_enabled', None)
109
110        if csrf_enabled is None:
111            csrf_enabled = current_app.config.get('CSRF_ENABLED', True)
112        
113        self.csrf_enabled = csrf_enabled
114
115        self.csrf_session_key = kwargs.pop('csrf_session_key', None)
116
117        if self.csrf_session_key is None:
118            self.csrf_session_key = \
119                current_app.config.get('CSRF_SESSION_KEY', '_csrf_token')
120
121        csrf_token = session.get(self.csrf_session_key, None)
122
123        if csrf_token is None:
124            csrf_token = self.reset_csrf()
125
126        super(Form, self).__init__(formdata, csrf=csrf_token, *args, **kwargs)
127
128    def is_submitted(self):
129        """
130        Checks if form has been submitted. The default case is if the HTTP 
131        method is **PUT** or **POST**.
132        """
133
134        return request and request.method in ("PUT", "POST")
135
136    def process(self, formdata=None, obj=None, **kwargs):
137
138        if self.is_submitted():
139        
140            if formdata is None:
141                formdata = request.form
142
143            # ensure csrf validation occurs ONLY when formdata is passed
144            # in case "csrf" is the only field in the form
145
146            if not formdata and not request.files:
147                self.csrf_is_valid = False
148            else:
149                self.csrf_is_valid = None
150
151        super(Form, self).process(formdata, obj, **kwargs)
152
153    @property
154    def csrf_token(self):
155        """
156        Renders CSRF field inside a hidden DIV.
157
158        :deprecated: Use **hidden_tag** instead.
159        """
160        warnings.warn("csrf_token is deprecated. Use hidden_tag instead", 
161                      DeprecationWarning)
162
163        return self.hidden_tag('csrf')
164
165    def reset_csrf(self):
166        """
167        Resets the CSRF token in the session. If you are reusing the form
168        in the same view (i.e. you are not redirecting somewhere else)
169        it's recommended you call this before rendering the form.
170        """
171        
172        csrf_token = _generate_csrf_token()
173        session[self.csrf_session_key] = csrf_token
174        return csrf_token
175
176    def validate_csrf(self, field):
177        if not self.csrf_enabled:
178            return
179
180        csrf_token = session.pop(self.csrf_session_key, None)
181        is_valid = field.data and \
182                   field.data == csrf_token and \
183                   self.csrf_is_valid is not False
184
185        # reset this field, otherwise stale token is displayed
186        field.data = self.reset_csrf()
187
188        # we set this flag to ensure consistent behaviour when
189        # calling validate() more than once
190
191        self.csrf_is_valid = bool(is_valid)
192
193        if not is_valid:
194            raise ValidationError, "Missing or invalid CSRF token"
195
196    def hidden_tag(self, *fields):
197        """
198        Wraps hidden fields in a hidden DIV tag, in order to keep XHTML 
199        compliance.
200
201        .. versionadded:: 0.3
202
203        :param fields: list of hidden field names. If not provided will render
204                       all hidden fields, including the CSRF field.
205        """
206
207        if not fields:
208            fields = [f for f in self if isinstance(f, HiddenField)]
209
210        rv = [u'<div style="display:none;">']
211        for field in fields:
212            if isinstance(field, basestring):
213                field = getattr(self, field)
214            rv.append(unicode(field))
215        rv.append(u"</div>")
216
217        return Markup(u"".join(rv))
218        
219    def validate_on_submit(self):
220        """
221        Checks if form has been submitted and if so runs validate. This is 
222        a shortcut, equivalent to ``form.is_submitted() and form.validate()``
223        """
224        return self.is_submitted() and self.validate()
225    
226