1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
|
from wtforms.fields import HiddenField
from wtforms.validators import ValidationError
__all__ = ("CSRFTokenField", "CSRF")
class CSRFTokenField(HiddenField):
"""
A subclass of HiddenField designed for sending the CSRF token that is used
for most CSRF protection schemes.
Notably different from a normal field, this field always renders the
current token regardless of the submitted value, and also will not be
populated over to object data via populate_obj
"""
current_token = None
def __init__(self, *args, **kw):
self.csrf_impl = kw.pop("csrf_impl")
super().__init__(*args, **kw)
def _value(self):
"""
We want to always return the current token on render, regardless of
whether a good or bad token was passed.
"""
return self.current_token
def populate_obj(self, *args):
"""
Don't populate objects with the CSRF token
"""
pass
def pre_validate(self, form):
"""
Handle validation of this token field.
"""
self.csrf_impl.validate_csrf_token(form, self)
def process(self, *args, **kwargs):
super().process(*args, **kwargs)
self.current_token = self.csrf_impl.generate_csrf_token(self)
class CSRF:
field_class = CSRFTokenField
def setup_form(self, form):
"""
Receive the form we're attached to and set up fields.
The default implementation creates a single field of
type :attr:`field_class` with name taken from the
``csrf_field_name`` of the class meta.
:param form:
The form instance we're attaching to.
:return:
A sequence of `(field_name, unbound_field)` 2-tuples which
are unbound fields to be added to the form.
"""
meta = form.meta
field_name = meta.csrf_field_name
unbound_field = self.field_class(label="CSRF Token", csrf_impl=self)
return [(field_name, unbound_field)]
def generate_csrf_token(self, csrf_token_field):
"""
Implementations must override this to provide a method with which one
can get a CSRF token for this form.
A CSRF token is usually a string that is generated deterministically
based on some sort of user data, though it can be anything which you
can validate on a subsequent request.
:param csrf_token_field:
The field which is being used for CSRF.
:return:
A generated CSRF string.
"""
raise NotImplementedError()
def validate_csrf_token(self, form, field):
"""
Override this method to provide custom CSRF validation logic.
The default CSRF validation logic simply checks if the recently
generated token equals the one we received as formdata.
:param form: The form which has this CSRF token.
:param field: The CSRF token field.
"""
if field.current_token != field.data:
raise ValidationError(field.gettext("Invalid CSRF Token."))
|