commit | author | age
|
c5f24b
|
1 |
.. index:: |
CM |
2 |
single: security |
|
3 |
|
07a69e
|
4 |
.. _security_chapter: |
CM |
5 |
|
1462bc
|
6 |
Security |
CM |
7 |
======== |
|
8 |
|
cf9bdf
|
9 |
:app:`Pyramid` provides an optional, declarative, security system. Security in |
SP |
10 |
:app:`Pyramid` is separated into authentication and authorization. The two |
|
11 |
systems communicate via :term:`principal` identifiers. Authentication is merely |
|
12 |
the mechanism by which credentials provided in the :term:`request` are resolved |
|
13 |
to one or more :term:`principal` identifiers. These identifiers represent the |
|
14 |
users and groups that are in effect during the request. Authorization then |
|
15 |
determines access based on the :term:`principal` identifiers, the requested |
fe83c6
|
16 |
:term:`permission`, and a :term:`context`. |
03e959
|
17 |
|
cf9bdf
|
18 |
The :app:`Pyramid` authorization system can prevent a :term:`view` from being |
SP |
19 |
invoked based on an :term:`authorization policy`. Before a view is invoked, the |
|
20 |
authorization system can use the credentials in the :term:`request` along with |
|
21 |
the :term:`context` resource to determine if access will be allowed. Here's |
|
22 |
how it works at a high level: |
9ec2d6
|
23 |
|
cf9bdf
|
24 |
- A user may or may not have previously visited the application and supplied |
SP |
25 |
authentication credentials, including a :term:`userid`. If so, the |
|
26 |
application may have called :func:`pyramid.security.remember` to remember |
|
27 |
these. |
a0cba7
|
28 |
|
82e045
|
29 |
- A :term:`request` is generated when a user visits the application. |
9ec2d6
|
30 |
|
780999
|
31 |
- Based on the request, a :term:`context` resource is located through |
CM |
32 |
:term:`resource location`. A context is located differently depending on |
cf9bdf
|
33 |
whether the application uses :term:`traversal` or :term:`URL dispatch`, but a |
SP |
34 |
context is ultimately found in either case. See the |
|
35 |
:ref:`urldispatch_chapter` chapter for more information. |
9ec2d6
|
36 |
|
cf9bdf
|
37 |
- A :term:`view callable` is located by :term:`view lookup` using the context |
SP |
38 |
as well as other attributes of the request. |
590fe7
|
39 |
|
cf9bdf
|
40 |
- If an :term:`authentication policy` is in effect, it is passed the request. |
SP |
41 |
It will return some number of :term:`principal` identifiers. To do this, the |
|
42 |
policy would need to determine the authenticated :term:`userid` present in |
|
43 |
the request. |
9ec2d6
|
44 |
|
CM |
45 |
- If an :term:`authorization policy` is in effect and the :term:`view |
cf9bdf
|
46 |
configuration` associated with the view callable that was found has a |
SP |
47 |
:term:`permission` associated with it, the authorization policy is passed the |
|
48 |
:term:`context`, some number of :term:`principal` identifiers returned by the |
|
49 |
authentication policy, and the :term:`permission` associated with the view; |
|
50 |
it will allow or deny access. |
9ec2d6
|
51 |
|
cf9bdf
|
52 |
- If the authorization policy allows access, the view callable is invoked. |
9ec2d6
|
53 |
|
cf9bdf
|
54 |
- If the authorization policy denies access, the view callable is not invoked. |
SP |
55 |
Instead the :term:`forbidden view` is invoked. |
3dc911
|
56 |
|
5f44b2
|
57 |
Authorization is enabled by modifying your application to include an |
cf9bdf
|
58 |
:term:`authentication policy` and :term:`authorization policy`. :app:`Pyramid` |
SP |
59 |
comes with a variety of implementations of these policies. To provide maximal |
|
60 |
flexibility, :app:`Pyramid` also allows you to create custom authentication |
|
61 |
policies and authorization policies. |
3a63dc
|
62 |
|
8c56ae
|
63 |
.. index:: |
c5f24b
|
64 |
single: authorization policy |
8c56ae
|
65 |
|
bb93cb
|
66 |
.. _enabling_authorization_policy: |
CM |
67 |
|
9ec2d6
|
68 |
Enabling an Authorization Policy |
CM |
69 |
-------------------------------- |
1462bc
|
70 |
|
cf9bdf
|
71 |
:app:`Pyramid` does not enable any authorization policy by default. All views |
SP |
72 |
are accessible by completely anonymous users. In order to begin protecting |
|
73 |
views from execution based on security settings, you need to enable an |
|
74 |
authorization policy. |
1462bc
|
75 |
|
9ec2d6
|
76 |
Enabling an Authorization Policy Imperatively |
CM |
77 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
78 |
|
cf9bdf
|
79 |
Use the :meth:`~pyramid.config.Configurator.set_authorization_policy` method of |
SP |
80 |
the :class:`~pyramid.config.Configurator` to enable an authorization policy. |
1462bc
|
81 |
|
3bee9e
|
82 |
You must also enable an :term:`authentication policy` in order to enable the |
cf9bdf
|
83 |
authorization policy. This is because authorization, in general, depends upon |
SP |
84 |
authentication. Use the |
|
85 |
:meth:`~pyramid.config.Configurator.set_authentication_policy` method during |
|
86 |
application setup to specify the authentication policy. |
7c411c
|
87 |
|
CM |
88 |
For example: |
|
89 |
|
|
90 |
.. code-block:: python |
1479bd
|
91 |
:linenos: |
7c411c
|
92 |
|
1479bd
|
93 |
from pyramid.config import Configurator |
SP |
94 |
from pyramid.authentication import AuthTktAuthenticationPolicy |
|
95 |
from pyramid.authorization import ACLAuthorizationPolicy |
|
96 |
authn_policy = AuthTktAuthenticationPolicy('seekrit', hashalg='sha512') |
|
97 |
authz_policy = ACLAuthorizationPolicy() |
|
98 |
config = Configurator() |
|
99 |
config.set_authentication_policy(authn_policy) |
|
100 |
config.set_authorization_policy(authz_policy) |
7c411c
|
101 |
|
cf9bdf
|
102 |
.. note:: The ``authentication_policy`` and ``authorization_policy`` arguments |
SP |
103 |
may also be passed to their respective methods mentioned above as |
|
104 |
:term:`dotted Python name` values, each representing the dotted name path to |
|
105 |
a suitable implementation global defined at Python module scope. |
d89aee
|
106 |
|
fb6a5c
|
107 |
The above configuration enables a policy which compares the value of an "auth |
CM |
108 |
ticket" cookie passed in the request's environment which contains a reference |
cf9bdf
|
109 |
to a single :term:`userid`, and matches that userid's :term:`principals |
SP |
110 |
<principal>` against the principals present in any :term:`ACL` found in the |
|
111 |
resource tree when attempting to call some :term:`view`. |
7c411c
|
112 |
|
CM |
113 |
While it is possible to mix and match different authentication and |
cf9bdf
|
114 |
authorization policies, it is an error to configure a Pyramid application with |
SP |
115 |
an authentication policy but without the authorization policy or vice versa. If |
|
116 |
you do this, you'll receive an error at application startup time. |
7c411c
|
117 |
|
2033ee
|
118 |
.. seealso:: |
SP |
119 |
|
cf9bdf
|
120 |
See also the :mod:`pyramid.authorization` and :mod:`pyramid.authentication` |
SP |
121 |
modules for alternative implementations of authorization and authentication |
|
122 |
policies. |
8c56ae
|
123 |
|
CM |
124 |
.. index:: |
|
125 |
single: permissions |
|
126 |
single: protecting views |
1462bc
|
127 |
|
410457
|
128 |
.. _protecting_views: |
CM |
129 |
|
8b8e10
|
130 |
Protecting Views with Permissions |
CM |
131 |
--------------------------------- |
1462bc
|
132 |
|
780999
|
133 |
To protect a :term:`view callable` from invocation based on a user's security |
CM |
134 |
settings when a particular type of resource becomes the :term:`context`, you |
cf9bdf
|
135 |
must pass a :term:`permission` to :term:`view configuration`. Permissions are |
SP |
136 |
usually just strings, and they have no required composition: you can name |
780999
|
137 |
permissions whatever you like. |
9ec2d6
|
138 |
|
c7c40b
|
139 |
For example, the following view declaration protects the view named |
780999
|
140 |
``add_entry.html`` when the context resource is of type ``Blog`` with the |
cf9bdf
|
141 |
``add`` permission using the :meth:`pyramid.config.Configurator.add_view` API: |
1462bc
|
142 |
|
c7c40b
|
143 |
.. code-block:: python |
1479bd
|
144 |
:linenos: |
d9c735
|
145 |
|
1479bd
|
146 |
# config is an instance of pyramid.config.Configurator |
c7c40b
|
147 |
|
1479bd
|
148 |
config.add_view('mypackage.views.blog_entry_add_view', |
SP |
149 |
name='add_entry.html', |
|
150 |
context='mypackage.resources.Blog', |
|
151 |
permission='add') |
1b2957
|
152 |
|
cf9bdf
|
153 |
The equivalent view registration including the ``add`` permission name may be |
SP |
154 |
performed via the ``@view_config`` decorator: |
1b2957
|
155 |
|
CM |
156 |
.. code-block:: python |
1479bd
|
157 |
:linenos: |
1b2957
|
158 |
|
1479bd
|
159 |
from pyramid.view import view_config |
SP |
160 |
from resources import Blog |
1b2957
|
161 |
|
1479bd
|
162 |
@view_config(context=Blog, name='add_entry.html', permission='add') |
SP |
163 |
def blog_entry_add_view(request): |
|
164 |
""" Add blog entry code goes here """ |
|
165 |
pass |
8b8e10
|
166 |
|
c7c40b
|
167 |
As a result of any of these various view configuration statements, if an |
cf9bdf
|
168 |
authorization policy is in place when the view callable is found during normal |
SP |
169 |
application operations, the requesting user will need to possess the ``add`` |
|
170 |
permission against the :term:`context` resource in order to be able to invoke |
|
171 |
the ``blog_entry_add_view`` view. If they do not, the :term:`Forbidden view` |
|
172 |
will be invoked. |
8c56ae
|
173 |
|
6ce1e0
|
174 |
.. index:: |
CM |
175 |
pair: permission; default |
|
176 |
|
e25a70
|
177 |
.. _setting_a_default_permission: |
CM |
178 |
|
|
179 |
Setting a Default Permission |
|
180 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
181 |
|
cf9bdf
|
182 |
If a permission is not supplied to a view configuration, the registered view |
SP |
183 |
will always be executable by entirely anonymous users: any authorization policy |
|
184 |
in effect is ignored. |
e25a70
|
185 |
|
cf9bdf
|
186 |
In support of making it easier to configure applications which are "secure by |
SP |
187 |
default", :app:`Pyramid` allows you to configure a *default* permission. If |
|
188 |
supplied, the default permission is used as the permission string to all view |
|
189 |
registrations which don't otherwise name a ``permission`` argument. |
e25a70
|
190 |
|
cf9bdf
|
191 |
The :meth:`pyramid.config.Configurator.set_default_permission` method supports |
SP |
192 |
configuring a default permission for an application. |
e25a70
|
193 |
|
1ad1db
|
194 |
When a default permission is registered: |
e25a70
|
195 |
|
5a3520
|
196 |
- If a view configuration names an explicit ``permission``, the default |
1ad1db
|
197 |
permission is ignored for that view registration, and the |
CM |
198 |
view-configuration-named permission is used. |
|
199 |
|
feceff
|
200 |
- If a view configuration names the permission |
cf9bdf
|
201 |
:data:`pyramid.security.NO_PERMISSION_REQUIRED`, the default permission is |
SP |
202 |
ignored, and the view is registered *without* a permission (making it |
1ad1db
|
203 |
available to all callers regardless of their credentials). |
e25a70
|
204 |
|
2021a0
|
205 |
.. warning:: |
CM |
206 |
|
|
207 |
When you register a default permission, *all* views (even :term:`exception |
|
208 |
view` views) are protected by a permission. For all views which are truly |
|
209 |
meant to be anonymously accessible, you will need to associate the view's |
feceff
|
210 |
configuration with the :data:`pyramid.security.NO_PERMISSION_REQUIRED` |
MM |
211 |
permission. |
2021a0
|
212 |
|
8c56ae
|
213 |
.. index:: |
9ec2d6
|
214 |
single: ACL |
CM |
215 |
single: access control list |
6ce1e0
|
216 |
pair: resource; ACL |
8b8e10
|
217 |
|
c1278c
|
218 |
.. _assigning_acls: |
CM |
219 |
|
cf9bdf
|
220 |
Assigning ACLs to Your Resource Objects |
fb6a5c
|
221 |
--------------------------------------- |
8b8e10
|
222 |
|
cf9bdf
|
223 |
When the default :app:`Pyramid` :term:`authorization policy` determines whether |
SP |
224 |
a user possesses a particular permission with respect to a resource, it |
|
225 |
examines the :term:`ACL` associated with the resource. An ACL is associated |
|
226 |
with a resource by adding an ``__acl__`` attribute to the resource object. |
|
227 |
This attribute can be defined on the resource *instance* if you need |
780999
|
228 |
instance-level security, or it can be defined on the resource *class* if you |
CM |
229 |
just need type-level security. |
8b8e10
|
230 |
|
cf9bdf
|
231 |
For example, an ACL might be attached to the resource for a blog via its class: |
8b8e10
|
232 |
|
d9c735
|
233 |
.. code-block:: python |
1479bd
|
234 |
:linenos: |
8b8e10
|
235 |
|
1479bd
|
236 |
from pyramid.security import Allow |
SP |
237 |
from pyramid.security import Everyone |
8b8e10
|
238 |
|
1479bd
|
239 |
class Blog(object): |
SP |
240 |
__acl__ = [ |
|
241 |
(Allow, Everyone, 'view'), |
|
242 |
(Allow, 'group:editors', 'add'), |
|
243 |
(Allow, 'group:editors', 'edit'), |
|
244 |
] |
8b8e10
|
245 |
|
fb6a5c
|
246 |
Or, if your resources are persistent, an ACL might be specified via the |
CM |
247 |
``__acl__`` attribute of an *instance* of a resource: |
1b2957
|
248 |
|
CM |
249 |
.. code-block:: python |
1479bd
|
250 |
:linenos: |
1b2957
|
251 |
|
1479bd
|
252 |
from pyramid.security import Allow |
SP |
253 |
from pyramid.security import Everyone |
1b2957
|
254 |
|
1479bd
|
255 |
class Blog(object): |
SP |
256 |
pass |
1b2957
|
257 |
|
1479bd
|
258 |
blog = Blog() |
1b2957
|
259 |
|
1479bd
|
260 |
blog.__acl__ = [ |
SP |
261 |
(Allow, Everyone, 'view'), |
|
262 |
(Allow, 'group:editors', 'add'), |
|
263 |
(Allow, 'group:editors', 'edit'), |
|
264 |
] |
1b2957
|
265 |
|
cf9bdf
|
266 |
Whether an ACL is attached to a resource's class or an instance of the resource |
SP |
267 |
itself, the effect is the same. It is useful to decorate individual resource |
|
268 |
instances with an ACL (as opposed to just decorating their class) in |
|
269 |
applications such as content management systems where fine-grained access is |
|
270 |
required on an object-by-object basis. |
8c56ae
|
271 |
|
2d9314
|
272 |
Dynamic ACLs are also possible by turning the ACL into a callable on the |
MM |
273 |
resource. This may allow the ACL to dynamically generate rules based on |
|
274 |
properties of the instance. |
|
275 |
|
|
276 |
.. code-block:: python |
1479bd
|
277 |
:linenos: |
2d9314
|
278 |
|
1479bd
|
279 |
from pyramid.security import Allow |
SP |
280 |
from pyramid.security import Everyone |
2d9314
|
281 |
|
1479bd
|
282 |
class Blog(object): |
SP |
283 |
def __acl__(self): |
|
284 |
return [ |
|
285 |
(Allow, Everyone, 'view'), |
|
286 |
(Allow, self.owner, 'edit'), |
|
287 |
(Allow, 'group:editors', 'edit'), |
|
288 |
] |
2d9314
|
289 |
|
1479bd
|
290 |
def __init__(self, owner): |
SP |
291 |
self.owner = owner |
2d9314
|
292 |
|
bb800f
|
293 |
.. warning:: |
JM |
294 |
|
dd0925
|
295 |
Writing ``__acl__`` as properties is discouraged because an |
SP |
296 |
``AttributeError`` occurring in ``fget`` or ``fset`` will be silently |
|
297 |
dismissed (this is consistent with Python ``getattr`` and ``hasattr`` |
|
298 |
behaviors). For dynamic ACLs, simply use callables, as documented above. |
|
299 |
|
bb800f
|
300 |
|
8c56ae
|
301 |
.. index:: |
CM |
302 |
single: ACE |
9ec2d6
|
303 |
single: access control entry |
1b2957
|
304 |
|
CM |
305 |
Elements of an ACL |
|
306 |
------------------ |
|
307 |
|
|
308 |
Here's an example ACL: |
|
309 |
|
|
310 |
.. code-block:: python |
1479bd
|
311 |
:linenos: |
1b2957
|
312 |
|
1479bd
|
313 |
from pyramid.security import Allow |
SP |
314 |
from pyramid.security import Everyone |
1b2957
|
315 |
|
1479bd
|
316 |
__acl__ = [ |
SP |
317 |
(Allow, Everyone, 'view'), |
|
318 |
(Allow, 'group:editors', 'add'), |
|
319 |
(Allow, 'group:editors', 'edit'), |
|
320 |
] |
1b2957
|
321 |
|
cf9bdf
|
322 |
The example ACL indicates that the :data:`pyramid.security.Everyone` |
SP |
323 |
principal—a special system-defined principal indicating, literally, everyone—is |
|
324 |
allowed to view the blog, and the ``group:editors`` principal is allowed to add |
|
325 |
to and edit the blog. |
9ec2d6
|
326 |
|
cf9bdf
|
327 |
Each element of an ACL is an :term:`ACE`, or access control entry. For example, |
SP |
328 |
in the above code block, there are three ACEs: ``(Allow, Everyone, 'view')``, |
|
329 |
``(Allow, 'group:editors', 'add')``, and ``(Allow, 'group:editors', 'edit')``. |
9ec2d6
|
330 |
|
cf9bdf
|
331 |
The first element of any ACE is either :data:`pyramid.security.Allow`, or |
SP |
332 |
:data:`pyramid.security.Deny`, representing the action to take when the ACE |
|
333 |
matches. The second element is a :term:`principal`. The third argument is a |
|
334 |
permission or sequence of permission names. |
9ec2d6
|
335 |
|
c7c40b
|
336 |
A principal is usually a user id, however it also may be a group id if your |
CM |
337 |
authentication system provides group information and the effective |
|
338 |
:term:`authentication policy` policy is written to respect group information. |
df966a
|
339 |
See :ref:`extending_default_authentication_policies`. |
9ec2d6
|
340 |
|
cf9bdf
|
341 |
Each ACE in an ACL is processed by an authorization policy *in the order |
SP |
342 |
dictated by the ACL*. So if you have an ACL like this: |
9ec2d6
|
343 |
|
CM |
344 |
.. code-block:: python |
1479bd
|
345 |
:linenos: |
9ec2d6
|
346 |
|
1479bd
|
347 |
from pyramid.security import Allow |
SP |
348 |
from pyramid.security import Deny |
|
349 |
from pyramid.security import Everyone |
9ec2d6
|
350 |
|
1479bd
|
351 |
__acl__ = [ |
SP |
352 |
(Allow, Everyone, 'view'), |
|
353 |
(Deny, Everyone, 'view'), |
|
354 |
] |
9ec2d6
|
355 |
|
cf9bdf
|
356 |
The default authorization policy will *allow* everyone the view permission, |
SP |
357 |
even though later in the ACL you have an ACE that denies everyone the view |
|
358 |
permission. On the other hand, if you have an ACL like this: |
9ec2d6
|
359 |
|
CM |
360 |
.. code-block:: python |
1479bd
|
361 |
:linenos: |
9ec2d6
|
362 |
|
1479bd
|
363 |
from pyramid.security import Everyone |
SP |
364 |
from pyramid.security import Allow |
|
365 |
from pyramid.security import Deny |
9ec2d6
|
366 |
|
1479bd
|
367 |
__acl__ = [ |
SP |
368 |
(Deny, Everyone, 'view'), |
|
369 |
(Allow, Everyone, 'view'), |
|
370 |
] |
9ec2d6
|
371 |
|
cf9bdf
|
372 |
The authorization policy will deny everyone the view permission, even though |
SP |
373 |
later in the ACL, there is an ACE that allows everyone. |
8b8e10
|
374 |
|
cf9bdf
|
375 |
The third argument in an ACE can also be a sequence of permission names instead |
SP |
376 |
of a single permission name. So instead of creating multiple ACEs representing |
|
377 |
a number of different permission grants to a single ``group:editors`` group, we |
|
378 |
can collapse this into a single ACE, as below. |
9c82bc
|
379 |
|
CM |
380 |
.. code-block:: python |
1479bd
|
381 |
:linenos: |
1b2957
|
382 |
|
1479bd
|
383 |
from pyramid.security import Allow |
SP |
384 |
from pyramid.security import Everyone |
9c82bc
|
385 |
|
1479bd
|
386 |
__acl__ = [ |
SP |
387 |
(Allow, Everyone, 'view'), |
|
388 |
(Allow, 'group:editors', ('add', 'edit')), |
|
389 |
] |
9c82bc
|
390 |
|
1b2957
|
391 |
|
8c56ae
|
392 |
.. index:: |
1dff93
|
393 |
single: principal |
c5f24b
|
394 |
single: principal names |
8c56ae
|
395 |
|
1b2957
|
396 |
Special Principal Names |
CM |
397 |
----------------------- |
|
398 |
|
cf9bdf
|
399 |
Special principal names exist in the :mod:`pyramid.security` module. They can |
SP |
400 |
be imported for use in your own code to populate ACLs, e.g., |
|
401 |
:data:`pyramid.security.Everyone`. |
1b2957
|
402 |
|
fec0f0
|
403 |
:data:`pyramid.security.Everyone` |
1b2957
|
404 |
|
cf9bdf
|
405 |
Literally, everyone, no matter what. This object is actually a string under |
SP |
406 |
the hood (``system.Everyone``). Every user *is* the principal named |
|
407 |
"Everyone" during every request, even if a security policy is not in use. |
1b2957
|
408 |
|
fec0f0
|
409 |
:data:`pyramid.security.Authenticated` |
1b2957
|
410 |
|
cf9bdf
|
411 |
Any user with credentials as determined by the current security policy. You |
SP |
412 |
might think of it as any user that is "logged in". This object is actually a |
|
413 |
string under the hood (``system.Authenticated``). |
1b2957
|
414 |
|
8c56ae
|
415 |
.. index:: |
c5f24b
|
416 |
single: permission names |
CM |
417 |
single: special permission names |
8c56ae
|
418 |
|
1b2957
|
419 |
Special Permissions |
CM |
420 |
------------------- |
|
421 |
|
cf9bdf
|
422 |
Special permission names exist in the :mod:`pyramid.security` module. These |
SP |
423 |
can be imported for use in ACLs. |
1b2957
|
424 |
|
CM |
425 |
.. _all_permissions: |
|
426 |
|
fec0f0
|
427 |
:data:`pyramid.security.ALL_PERMISSIONS` |
1b2957
|
428 |
|
cf9bdf
|
429 |
An object representing, literally, *all* permissions. Useful in an ACL like |
SP |
430 |
so: ``(Allow, 'fred', ALL_PERMISSIONS)``. The ``ALL_PERMISSIONS`` object is |
|
431 |
actually a stand-in object that has a ``__contains__`` method that always |
|
432 |
returns ``True``, which, for all known authorization policies, has the effect |
|
433 |
of indicating that a given principal has any permission asked for by the |
|
434 |
system. |
8c56ae
|
435 |
|
CM |
436 |
.. index:: |
c5f24b
|
437 |
single: special ACE |
CM |
438 |
single: ACE (special) |
1b2957
|
439 |
|
CM |
440 |
Special ACEs |
|
441 |
------------ |
|
442 |
|
780999
|
443 |
A convenience :term:`ACE` is defined representing a deny to everyone of all |
CM |
444 |
permissions in :data:`pyramid.security.DENY_ALL`. This ACE is often used as |
cf9bdf
|
445 |
the *last* ACE of an ACL to explicitly cause inheriting authorization policies |
SP |
446 |
to "stop looking up the traversal tree" (effectively breaking any inheritance). |
|
447 |
For example, an ACL which allows *only* ``fred`` the view permission for a |
|
448 |
particular resource, despite what inherited ACLs may say when the default |
|
449 |
authorization policy is in effect, might look like so: |
1b2957
|
450 |
|
CM |
451 |
.. code-block:: python |
1479bd
|
452 |
:linenos: |
1b2957
|
453 |
|
1479bd
|
454 |
from pyramid.security import Allow |
SP |
455 |
from pyramid.security import DENY_ALL |
1b2957
|
456 |
|
1479bd
|
457 |
__acl__ = [ (Allow, 'fred', 'view'), DENY_ALL ] |
649923
|
458 |
|
cf9bdf
|
459 |
Under the hood, the :data:`pyramid.security.DENY_ALL` ACE equals the |
SP |
460 |
following: |
9ec2d6
|
461 |
|
CM |
462 |
.. code-block:: python |
1479bd
|
463 |
:linenos: |
9ec2d6
|
464 |
|
1479bd
|
465 |
from pyramid.security import ALL_PERMISSIONS |
SP |
466 |
__acl__ = [ (Deny, Everyone, ALL_PERMISSIONS) ] |
9ec2d6
|
467 |
|
8c56ae
|
468 |
.. index:: |
CM |
469 |
single: ACL inheritance |
9ec2d6
|
470 |
pair: location-aware; security |
8c56ae
|
471 |
|
9ec2d6
|
472 |
ACL Inheritance and Location-Awareness |
CM |
473 |
-------------------------------------- |
8b8e10
|
474 |
|
fb6a5c
|
475 |
While the default :term:`authorization policy` is in place, if a resource |
CM |
476 |
object does not have an ACL when it is the context, its *parent* is consulted |
cf9bdf
|
477 |
for an ACL. If that object does not have an ACL, *its* parent is consulted for |
SP |
478 |
an ACL, ad infinitum, until we've reached the root and there are no more |
fb6a5c
|
479 |
parents left. |
8b8e10
|
480 |
|
fb6a5c
|
481 |
In order to allow the security machinery to perform ACL inheritance, resource |
CM |
482 |
objects must provide *location-awareness*. Providing *location-awareness* |
cf9bdf
|
483 |
means two things: the root object in the resource tree must have a ``__name__`` |
SP |
484 |
attribute and a ``__parent__`` attribute. |
8b8e10
|
485 |
|
85fd25
|
486 |
.. code-block:: python |
1479bd
|
487 |
:linenos: |
cbdc36
|
488 |
|
1479bd
|
489 |
class Blog(object): |
SP |
490 |
__name__ = '' |
|
491 |
__parent__ = None |
cbdc36
|
492 |
|
cf9bdf
|
493 |
An object with a ``__parent__`` attribute and a ``__name__`` attribute is said |
SP |
494 |
to be *location-aware*. Location-aware objects define a ``__parent__`` |
|
495 |
attribute which points at their parent object. The root object's |
|
496 |
``__parent__`` is ``None``. |
8b8e10
|
497 |
|
2033ee
|
498 |
.. seealso:: |
SP |
499 |
|
|
500 |
See also :ref:`location_module` for documentations of functions which use |
|
501 |
location-awareness. |
|
502 |
|
|
503 |
.. seealso:: |
|
504 |
|
|
505 |
See also :ref:`location_aware`. |
7bc20e
|
506 |
|
8c56ae
|
507 |
.. index:: |
c5f24b
|
508 |
single: forbidden view |
8c56ae
|
509 |
|
7bc20e
|
510 |
Changing the Forbidden View |
CM |
511 |
--------------------------- |
|
512 |
|
cf9bdf
|
513 |
When :app:`Pyramid` denies a view invocation due to an authorization denial, |
SP |
514 |
the special ``forbidden`` view is invoked. Out of the box, this forbidden view |
|
515 |
is very plain. See :ref:`changing_the_forbidden_view` within |
|
516 |
:ref:`hooks_chapter` for instructions on how to create a custom forbidden view |
|
517 |
and arrange for it to be called when view authorization is denied. |
8c56ae
|
518 |
|
CM |
519 |
.. index:: |
c5f24b
|
520 |
single: debugging authorization failures |
cbdc36
|
521 |
|
47b4d3
|
522 |
.. _debug_authorization_section: |
2f1209
|
523 |
|
17ce57
|
524 |
Debugging View Authorization Failures |
CM |
525 |
------------------------------------- |
2f1209
|
526 |
|
cf9bdf
|
527 |
If your application in your judgment is allowing or denying view access |
SP |
528 |
inappropriately, start your application under a shell using the |
7b9066
|
529 |
``PYRAMID_DEBUG_AUTHORIZATION`` environment variable set to ``1``. For |
23bfce
|
530 |
example: |
BL |
531 |
|
|
532 |
.. code-block:: text |
47b4d3
|
533 |
|
1479bd
|
534 |
PYRAMID_DEBUG_AUTHORIZATION=1 $VENV/bin/pserve myproject.ini |
2f1209
|
535 |
|
cf9bdf
|
536 |
When any authorization takes place during a top-level view rendering, a message |
SP |
537 |
will be logged to the console (to stderr) about what ACE in which ACL permitted |
|
538 |
or denied the authorization based on authentication information. |
47b4d3
|
539 |
|
cf9bdf
|
540 |
This behavior can also be turned on in the application ``.ini`` file by setting |
SP |
541 |
the ``pyramid.debug_authorization`` key to ``true`` within the application's |
|
542 |
configuration section, e.g.: |
23bfce
|
543 |
|
16cd50
|
544 |
.. code-block:: ini |
1479bd
|
545 |
:linenos: |
47b4d3
|
546 |
|
1479bd
|
547 |
[app:main] |
SP |
548 |
use = egg:MyProject |
|
549 |
pyramid.debug_authorization = true |
2f1209
|
550 |
|
cf9bdf
|
551 |
With this debug flag turned on, the response sent to the browser will also |
SP |
552 |
contain security debugging information in its body. |
17ce57
|
553 |
|
CM |
554 |
Debugging Imperative Authorization Failures |
|
555 |
------------------------------------------- |
|
556 |
|
3c2f95
|
557 |
The :meth:`pyramid.request.Request.has_permission` API is used to check |
cf9bdf
|
558 |
security within view functions imperatively. It returns instances of objects |
SP |
559 |
that are effectively booleans. But these objects are not raw ``True`` or |
|
560 |
``False`` objects, and have information attached to them about why the |
|
561 |
permission was allowed or denied. The object will be one of |
|
562 |
:data:`pyramid.security.ACLAllowed`, :data:`pyramid.security.ACLDenied`, |
|
563 |
:data:`pyramid.security.Allowed`, or :data:`pyramid.security.Denied`, as |
|
564 |
documented in :ref:`security_module`. At the very minimum, these objects will |
|
565 |
have a ``msg`` attribute, which is a string indicating why the permission was |
|
566 |
denied or allowed. Introspecting this information in the debugger or via print |
|
567 |
statements when a call to :meth:`~pyramid.request.Request.has_permission` fails |
|
568 |
is often useful. |
8c56ae
|
569 |
|
CM |
570 |
.. index:: |
df966a
|
571 |
single: authentication policy (extending) |
MM |
572 |
|
|
573 |
.. _extending_default_authentication_policies: |
|
574 |
|
|
575 |
Extending Default Authentication Policies |
|
576 |
----------------------------------------- |
|
577 |
|
cf9bdf
|
578 |
Pyramid ships with some built in authentication policies for use in your |
SP |
579 |
applications. See :mod:`pyramid.authentication` for the available policies. |
|
580 |
They differ on their mechanisms for tracking authentication credentials between |
|
581 |
requests, however they all interface with your application in mostly the same |
|
582 |
way. |
df966a
|
583 |
|
cf9bdf
|
584 |
Above you learned about :ref:`assigning_acls`. Each :term:`principal` used in |
SP |
585 |
the :term:`ACL` is matched against the list returned from |
df966a
|
586 |
:meth:`pyramid.interfaces.IAuthenticationPolicy.effective_principals`. |
MM |
587 |
Similarly, :meth:`pyramid.request.Request.authenticated_userid` maps to |
|
588 |
:meth:`pyramid.interfaces.IAuthenticationPolicy.authenticated_userid`. |
|
589 |
|
|
590 |
You may control these values by subclassing the default authentication |
|
591 |
policies. For example, below we subclass the |
cf9bdf
|
592 |
:class:`pyramid.authentication.AuthTktAuthenticationPolicy` and define extra |
SP |
593 |
functionality to query our database before confirming that the :term:`userid` |
|
594 |
is valid in order to avoid blindly trusting the value in the cookie (what if |
|
595 |
the cookie is still valid, but the user has deleted their account?). We then |
|
596 |
use that :term:`userid` to augment the ``effective_principals`` with |
|
597 |
information about groups and other state for that user. |
df966a
|
598 |
|
MM |
599 |
.. code-block:: python |
1479bd
|
600 |
:linenos: |
df966a
|
601 |
|
1479bd
|
602 |
from pyramid.authentication import AuthTktAuthenticationPolicy |
df966a
|
603 |
|
1479bd
|
604 |
class MyAuthenticationPolicy(AuthTktAuthenticationPolicy): |
SP |
605 |
def authenticated_userid(self, request): |
|
606 |
userid = self.unauthenticated_userid(request) |
|
607 |
if userid: |
|
608 |
if request.verify_userid_is_still_valid(userid): |
|
609 |
return userid |
df966a
|
610 |
|
1479bd
|
611 |
def effective_principals(self, request): |
SP |
612 |
principals = [Everyone] |
|
613 |
userid = self.authenticated_userid(request) |
|
614 |
if userid: |
|
615 |
principals += [Authenticated, str(userid)] |
|
616 |
return principals |
df966a
|
617 |
|
MM |
618 |
In most instances ``authenticated_userid`` and ``effective_principals`` are |
cf9bdf
|
619 |
application-specific, whereas ``unauthenticated_userid``, ``remember``, and |
SP |
620 |
``forget`` are generic and focused on transport and serialization of data |
df966a
|
621 |
between consecutive requests. |
MM |
622 |
|
|
623 |
.. index:: |
c5f24b
|
624 |
single: authentication policy (creating) |
831da8
|
625 |
|
cf58ab
|
626 |
.. _creating_an_authentication_policy: |
CM |
627 |
|
643bd0
|
628 |
Creating Your Own Authentication Policy |
CM |
629 |
--------------------------------------- |
|
630 |
|
cf9bdf
|
631 |
:app:`Pyramid` ships with a number of useful out-of-the-box security policies |
SP |
632 |
(see :mod:`pyramid.authentication`). However, creating your own authentication |
|
633 |
policy is often necessary when you want to control the "horizontal and |
|
634 |
vertical" of how your users authenticate. Doing so is a matter of creating an |
|
635 |
instance of something that implements the following interface: |
643bd0
|
636 |
|
CM |
637 |
.. code-block:: python |
1479bd
|
638 |
:linenos: |
643bd0
|
639 |
|
1479bd
|
640 |
class IAuthenticationPolicy(object): |
SP |
641 |
""" An object representing a Pyramid authentication policy. """ |
80b4af
|
642 |
|
1479bd
|
643 |
def authenticated_userid(self, request): |
SP |
644 |
""" Return the authenticated :term:`userid` or ``None`` if |
|
645 |
no authenticated userid can be found. This method of the |
|
646 |
policy should ensure that a record exists in whatever |
|
647 |
persistent store is used related to the user (the user |
|
648 |
should not have been deleted); if a record associated with |
|
649 |
the current id does not exist in a persistent store, it |
|
650 |
should return ``None``. |
|
651 |
""" |
fe83c6
|
652 |
|
1479bd
|
653 |
def unauthenticated_userid(self, request): |
SP |
654 |
""" Return the *unauthenticated* userid. This method |
|
655 |
performs the same duty as ``authenticated_userid`` but is |
|
656 |
permitted to return the userid based only on data present |
|
657 |
in the request; it needn't (and shouldn't) check any |
|
658 |
persistent store to ensure that the user record related to |
|
659 |
the request userid exists. |
80b4af
|
660 |
|
1479bd
|
661 |
This method is intended primarily a helper to assist the |
SP |
662 |
``authenticated_userid`` method in pulling credentials out |
|
663 |
of the request data, abstracting away the specific headers, |
|
664 |
query strings, etc that are used to authenticate the request. |
|
665 |
""" |
fe83c6
|
666 |
|
1479bd
|
667 |
def effective_principals(self, request): |
SP |
668 |
""" Return a sequence representing the effective principals |
|
669 |
typically including the :term:`userid` and any groups belonged |
|
670 |
to by the current user, always including 'system' groups such |
|
671 |
as ``pyramid.security.Everyone`` and |
|
672 |
``pyramid.security.Authenticated``. |
|
673 |
""" |
fe83c6
|
674 |
|
1479bd
|
675 |
def remember(self, request, userid, **kw): |
SP |
676 |
""" Return a set of headers suitable for 'remembering' the |
|
677 |
:term:`userid` named ``userid`` when set in a response. An |
|
678 |
individual authentication policy and its consumers can |
|
679 |
decide on the composition and meaning of **kw. |
|
680 |
""" |
643bd0
|
681 |
|
1479bd
|
682 |
def forget(self, request): |
SP |
683 |
""" Return a set of headers suitable for 'forgetting' the |
|
684 |
current user on subsequent requests. |
|
685 |
""" |
643bd0
|
686 |
|
7c411c
|
687 |
After you do so, you can pass an instance of such a class into the |
459493
|
688 |
:class:`~pyramid.config.Configurator.set_authentication_policy` method at |
3bee9e
|
689 |
configuration time to use it. |
643bd0
|
690 |
|
8c56ae
|
691 |
.. index:: |
c5f24b
|
692 |
single: authorization policy (creating) |
8c56ae
|
693 |
|
cf58ab
|
694 |
.. _creating_an_authorization_policy: |
CM |
695 |
|
643bd0
|
696 |
Creating Your Own Authorization Policy |
CM |
697 |
-------------------------------------- |
|
698 |
|
3bee9e
|
699 |
An authorization policy is a policy that allows or denies access after a user |
cf9bdf
|
700 |
has been authenticated. Most :app:`Pyramid` applications will use the default |
SP |
701 |
:class:`pyramid.authorization.ACLAuthorizationPolicy`. |
7c411c
|
702 |
|
cf9bdf
|
703 |
However, in some cases, it's useful to be able to use a different authorization |
SP |
704 |
policy than the default :class:`~pyramid.authorization.ACLAuthorizationPolicy`. |
|
705 |
For example, it might be desirable to construct an alternate authorization |
|
706 |
policy which allows the application to use an authorization mechanism that does |
|
707 |
not involve :term:`ACL` objects. |
cf58ab
|
708 |
|
cf9bdf
|
709 |
:app:`Pyramid` ships with only a single default authorization policy, so you'll |
SP |
710 |
need to create your own if you'd like to use a different one. Creating and |
|
711 |
using your own authorization policy is a matter of creating an instance of an |
|
712 |
object that implements the following interface: |
643bd0
|
713 |
|
CM |
714 |
.. code-block:: python |
16cd50
|
715 |
:linenos: |
643bd0
|
716 |
|
a7e16c
|
717 |
class IAuthorizationPolicy(Interface): |
fec0f0
|
718 |
""" An object representing a Pyramid authorization policy. """ |
a7e16c
|
719 |
def permits(context, principals, permission): |
BJR |
720 |
""" Return an instance of :class:`pyramid.security.Allowed` if any |
|
721 |
of the ``principals`` is allowed the ``permission`` in the current |
|
722 |
``context``, else return an instance of |
|
723 |
:class:`pyramid.security.Denied`. |
57cc86
|
724 |
""" |
313c25
|
725 |
|
a7e16c
|
726 |
def principals_allowed_by_permission(context, permission): |
57cc86
|
727 |
""" Return a set of principal identifiers allowed by the |
CM |
728 |
``permission`` in ``context``. This behavior is optional; if you |
|
729 |
choose to not implement it you should define this method as |
|
730 |
something which raises a ``NotImplementedError``. This method |
|
731 |
will only be called when the |
|
732 |
``pyramid.security.principals_allowed_by_permission`` API is |
|
733 |
used.""" |
643bd0
|
734 |
|
7c411c
|
735 |
After you do so, you can pass an instance of such a class into the |
3bee9e
|
736 |
:class:`~pyramid.config.Configurator.set_authorization_policy` method at |
CM |
737 |
configuration time to use it. |
e521f1
|
738 |
|
CM |
739 |
.. _admonishment_against_secret_sharing: |
|
740 |
|
94360d
|
741 |
Admonishment Against Secret-Sharing |
e521f1
|
742 |
----------------------------------- |
CM |
743 |
|
|
744 |
A "secret" is required by various components of Pyramid. For example, the |
|
745 |
:term:`authentication policy` below uses a secret value ``seekrit``:: |
|
746 |
|
|
747 |
authn_policy = AuthTktAuthenticationPolicy('seekrit', hashalg='sha512') |
|
748 |
|
|
749 |
A :term:`session factory` also requires a secret:: |
|
750 |
|
|
751 |
my_session_factory = SignedCookieSessionFactory('itsaseekreet') |
|
752 |
|
|
753 |
It is tempting to use the same secret for multiple Pyramid subsystems. For |
|
754 |
example, you might be tempted to use the value ``seekrit`` as the secret for |
|
755 |
both the authentication policy and the session factory defined above. This is |
|
756 |
a bad idea, because in both cases, these secrets are used to sign the payload |
|
757 |
of the data. |
|
758 |
|
|
759 |
If you use the same secret for two different parts of your application for |
|
760 |
signing purposes, it may allow an attacker to get his chosen plaintext signed, |
|
761 |
which would allow the attacker to control the content of the payload. Re-using |
|
762 |
a secret across two different subsystems might drop the security of signing to |
|
763 |
zero. Keys should not be re-used across different contexts where an attacker |
|
764 |
has the possibility of providing a chosen plaintext. |
a2c7c7
|
765 |
|
2ded2f
|
766 |
.. index:: |
MW |
767 |
single: preventing cross-site request forgery attacks |
|
768 |
single: cross-site request forgery attacks, prevention |
|
769 |
|
a2c7c7
|
770 |
Preventing Cross-Site Request Forgery Attacks |
MW |
771 |
--------------------------------------------- |
|
772 |
|
|
773 |
`Cross-site request forgery |
|
774 |
<https://en.wikipedia.org/wiki/Cross-site_request_forgery>`_ attacks are a |
|
775 |
phenomenon whereby a user who is logged in to your website might inadvertantly |
|
776 |
load a URL because it is linked from, or embedded in, an attacker's website. |
|
777 |
If the URL is one that may modify or delete data, the consequences can be dire. |
|
778 |
|
|
779 |
You can avoid most of these attacks by issuing a unique token to the browser |
|
780 |
and then requiring that it be present in all potentially unsafe requests. |
313c25
|
781 |
:app:`Pyramid` provides facilities to create and check CSRF tokens. |
a2c7c7
|
782 |
|
313c25
|
783 |
By default :app:`Pyramid` comes with a session-based CSRF implementation |
7c0f09
|
784 |
:class:`pyramid.csrf.SessionCSRFStoragePolicy`. To use it, you must first enable |
313c25
|
785 |
a :term:`session factory` as described in |
JC |
786 |
:ref:`using_the_default_session_factory` or |
|
787 |
:ref:`using_alternate_session_factories`. Alternatively, you can use |
7c0f09
|
788 |
a cookie-based implementation :class:`pyramid.csrf.CookieCSRFStoragePolicy` which gives |
313c25
|
789 |
some additional flexibility as it does not require a session for each user. |
JC |
790 |
You can also define your own implementation of |
fe0d22
|
791 |
:class:`pyramid.interfaces.ICSRFStoragePolicy` and register it with the |
7c0f09
|
792 |
:meth:`pyramid.config.Configurator.set_csrf_storage_policy` directive. |
313c25
|
793 |
|
JC |
794 |
For example: |
|
795 |
|
|
796 |
.. code-block:: python |
|
797 |
|
1479bd
|
798 |
from pyramid.config import Configurator |
313c25
|
799 |
|
1479bd
|
800 |
config = Configurator() |
SP |
801 |
config.set_csrf_storage_policy(MyCustomCSRFPolicy()) |
a2c7c7
|
802 |
|
MW |
803 |
.. index:: |
|
804 |
single: csrf.get_csrf_token |
|
805 |
|
|
806 |
Using the ``csrf.get_csrf_token`` Method |
|
807 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
808 |
|
|
809 |
To get the current CSRF token, use the |
|
810 |
:data:`pyramid.csrf.get_csrf_token` method. |
|
811 |
|
|
812 |
.. code-block:: python |
|
813 |
|
1479bd
|
814 |
from pyramid.csrf import get_csrf_token |
SP |
815 |
token = get_csrf_token(request) |
a2c7c7
|
816 |
|
MW |
817 |
The ``get_csrf_token()`` method accepts a single argument: the request. It |
|
818 |
returns a CSRF *token* string. If ``get_csrf_token()`` or ``new_csrf_token()`` |
|
819 |
was invoked previously for this user, then the existing token will be returned. |
|
820 |
If no CSRF token previously existed for this user, then a new token will be set |
|
821 |
into the session and returned. The newly created token will be opaque and |
|
822 |
randomized. |
|
823 |
|
682a9b
|
824 |
.. _get_csrf_token_in_templates: |
a2c7c7
|
825 |
|
MW |
826 |
Using the ``get_csrf_token`` global in templates |
|
827 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
828 |
|
|
829 |
Templates have a ``get_csrf_token()`` method inserted into their globals, which |
|
830 |
allows you to get the current token without modifying the view code. This |
|
831 |
method takes no arguments and returns a CSRF token string. You can use the |
|
832 |
returned token as the value of a hidden field in a form that posts to a method |
|
833 |
that requires elevated privileges, or supply it as a request header in AJAX |
|
834 |
requests. |
|
835 |
|
|
836 |
For example, include the CSRF token as a hidden field: |
|
837 |
|
|
838 |
.. code-block:: html |
|
839 |
|
|
840 |
<form method="post" action="/myview"> |
|
841 |
<input type="hidden" name="csrf_token" value="${get_csrf_token()}"> |
|
842 |
<input type="submit" value="Delete Everything"> |
|
843 |
</form> |
|
844 |
|
|
845 |
Or include it as a header in a jQuery AJAX request: |
|
846 |
|
|
847 |
.. code-block:: javascript |
|
848 |
|
|
849 |
var csrfToken = "${get_csrf_token()}"; |
|
850 |
$.ajax({ |
|
851 |
type: "POST", |
|
852 |
url: "/myview", |
|
853 |
headers: { 'X-CSRF-Token': csrfToken } |
|
854 |
}).done(function() { |
|
855 |
alert("Deleted"); |
|
856 |
}); |
|
857 |
|
|
858 |
The handler for the URL that receives the request should then require that the |
|
859 |
correct CSRF token is supplied. |
|
860 |
|
|
861 |
.. index:: |
|
862 |
single: csrf.new_csrf_token |
|
863 |
|
|
864 |
Using the ``csrf.new_csrf_token`` Method |
|
865 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
866 |
|
|
867 |
To explicitly create a new CSRF token, use the ``csrf.new_csrf_token()`` |
|
868 |
method. This differs only from ``csrf.get_csrf_token()`` inasmuch as it |
|
869 |
clears any existing CSRF token, creates a new CSRF token, sets the token into |
|
870 |
the user, and returns the token. |
|
871 |
|
|
872 |
.. code-block:: python |
|
873 |
|
1479bd
|
874 |
from pyramid.csrf import new_csrf_token |
SP |
875 |
token = new_csrf_token(request) |
a2c7c7
|
876 |
|
MW |
877 |
.. note:: |
|
878 |
|
|
879 |
It is not possible to force a new CSRF token from a template. If you |
|
880 |
want to regenerate your CSRF token then do it in the view code and return |
|
881 |
the new token as part of the context. |
|
882 |
|
|
883 |
Checking CSRF Tokens Manually |
|
884 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
885 |
|
|
886 |
In request handling code, you can check the presence and validity of a CSRF |
313c25
|
887 |
token with :func:`pyramid.csrf.check_csrf_token`. If the token is valid, it |
a2c7c7
|
888 |
will return ``True``, otherwise it will raise ``HTTPBadRequest``. Optionally, |
MW |
889 |
you can specify ``raises=False`` to have the check return ``False`` instead of |
|
890 |
raising an exception. |
|
891 |
|
|
892 |
By default, it checks for a POST parameter named ``csrf_token`` or a header |
|
893 |
named ``X-CSRF-Token``. |
|
894 |
|
|
895 |
.. code-block:: python |
|
896 |
|
1479bd
|
897 |
from pyramid.csrf import check_csrf_token |
a2c7c7
|
898 |
|
1479bd
|
899 |
def myview(request): |
SP |
900 |
# Require CSRF Token |
|
901 |
check_csrf_token(request) |
a2c7c7
|
902 |
|
1479bd
|
903 |
# ... |
a2c7c7
|
904 |
|
MW |
905 |
.. _auto_csrf_checking: |
|
906 |
|
|
907 |
Checking CSRF Tokens Automatically |
|
908 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
909 |
|
|
910 |
.. versionadded:: 1.7 |
|
911 |
|
|
912 |
:app:`Pyramid` supports automatically checking CSRF tokens on requests with an |
|
913 |
unsafe method as defined by RFC2616. Any other request may be checked manually. |
|
914 |
This feature can be turned on globally for an application using the |
|
915 |
:meth:`pyramid.config.Configurator.set_default_csrf_options` directive. |
|
916 |
For example: |
|
917 |
|
|
918 |
.. code-block:: python |
|
919 |
|
1479bd
|
920 |
from pyramid.config import Configurator |
a2c7c7
|
921 |
|
1479bd
|
922 |
config = Configurator() |
SP |
923 |
config.set_default_csrf_options(require_csrf=True) |
a2c7c7
|
924 |
|
MW |
925 |
CSRF checking may be explicitly enabled or disabled on a per-view basis using |
|
926 |
the ``require_csrf`` view option. A value of ``True`` or ``False`` will |
|
927 |
override the default set by ``set_default_csrf_options``. For example: |
|
928 |
|
|
929 |
.. code-block:: python |
|
930 |
|
1479bd
|
931 |
@view_config(route_name='hello', require_csrf=False) |
SP |
932 |
def myview(request): |
|
933 |
# ... |
a2c7c7
|
934 |
|
MW |
935 |
When CSRF checking is active, the token and header used to find the |
|
936 |
supplied CSRF token will be ``csrf_token`` and ``X-CSRF-Token``, respectively, |
|
937 |
unless otherwise overridden by ``set_default_csrf_options``. The token is |
|
938 |
checked against the value in ``request.POST`` which is the submitted form body. |
|
939 |
If this value is not present, then the header will be checked. |
|
940 |
|
|
941 |
In addition to token based CSRF checks, if the request is using HTTPS then the |
|
942 |
automatic CSRF checking will also check the referrer of the request to ensure |
|
943 |
that it matches one of the trusted origins. By default the only trusted origin |
|
944 |
is the current host, however additional origins may be configured by setting |
|
945 |
``pyramid.csrf_trusted_origins`` to a list of domain names (and ports if they |
69828b
|
946 |
are non-standard). If a host in the list of domains starts with a ``.`` then |
a2c7c7
|
947 |
that will allow all subdomains as well as the domain without the ``.``. |
MW |
948 |
|
|
949 |
If CSRF checks fail then a :class:`pyramid.exceptions.BadCSRFToken` or |
|
950 |
:class:`pyramid.exceptions.BadCSRFOrigin` exception will be raised. This |
|
951 |
exception may be caught and handled by an :term:`exception view` but, by |
|
952 |
default, will result in a ``400 Bad Request`` response being sent to the |
|
953 |
client. |
|
954 |
|
|
955 |
Checking CSRF Tokens with a View Predicate |
|
956 |
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
957 |
|
|
958 |
.. deprecated:: 1.7 |
|
959 |
Use the ``require_csrf`` option or read :ref:`auto_csrf_checking` instead |
|
960 |
to have :class:`pyramid.exceptions.BadCSRFToken` exceptions raised. |
|
961 |
|
|
962 |
A convenient way to require a valid CSRF token for a particular view is to |
|
963 |
include ``check_csrf=True`` as a view predicate. See |
|
964 |
:meth:`pyramid.config.Configurator.add_view`. |
|
965 |
|
|
966 |
.. code-block:: python |
|
967 |
|
1479bd
|
968 |
@view_config(request_method='POST', check_csrf=True, ...) |
SP |
969 |
def myview(request): |
|
970 |
# ... |
a2c7c7
|
971 |
|
MW |
972 |
.. note:: |
|
973 |
A mismatch of a CSRF token is treated like any other predicate miss, and the |
|
974 |
predicate system, when it doesn't find a view, raises ``HTTPNotFound`` |
|
975 |
instead of ``HTTPBadRequest``, so ``check_csrf=True`` behavior is different |
313c25
|
976 |
from calling :func:`pyramid.csrf.check_csrf_token`. |