commit | author | age
|
de3d0c
|
1 |
from zope.interface import implementer |
MM |
2 |
|
ee117e
|
3 |
from pyramid.interfaces import ( |
CM |
4 |
IAuthorizationPolicy, |
|
5 |
IAuthenticationPolicy, |
fe0d22
|
6 |
ICSRFStoragePolicy, |
de3d0c
|
7 |
IDefaultCSRFOptions, |
ee117e
|
8 |
IDefaultPermission, |
CM |
9 |
PHASE1_CONFIG, |
|
10 |
PHASE2_CONFIG, |
0c29cf
|
11 |
) |
5bf23f
|
12 |
|
682a9b
|
13 |
from pyramid.csrf import LegacySessionCSRFStoragePolicy |
5bf23f
|
14 |
from pyramid.exceptions import ConfigurationError |
c7974f
|
15 |
from pyramid.util import as_sorted_tuple |
a2c7c7
|
16 |
|
d579f2
|
17 |
from pyramid.config.actions import action_method |
5bf23f
|
18 |
|
7c0f09
|
19 |
|
0c29cf
|
20 |
class SecurityConfiguratorMixin(object): |
7c0f09
|
21 |
def add_default_security(self): |
682a9b
|
22 |
self.set_csrf_storage_policy(LegacySessionCSRFStoragePolicy()) |
7c0f09
|
23 |
|
5bf23f
|
24 |
@action_method |
CM |
25 |
def set_authentication_policy(self, policy): |
|
26 |
""" Override the :app:`Pyramid` :term:`authentication policy` in the |
|
27 |
current configuration. The ``policy`` argument must be an instance |
|
28 |
of an authentication policy or a :term:`dotted Python name` |
|
29 |
that points at an instance of an authentication policy. |
adfc23
|
30 |
|
012b97
|
31 |
.. note:: |
M |
32 |
|
|
33 |
Using the ``authentication_policy`` argument to the |
|
34 |
:class:`pyramid.config.Configurator` constructor can be used to |
|
35 |
achieve the same purpose. |
|
36 |
|
5bf23f
|
37 |
""" |
0c29cf
|
38 |
|
eb2fee
|
39 |
def register(): |
CM |
40 |
self._set_authentication_policy(policy) |
5bf23f
|
41 |
if self.registry.queryUtility(IAuthorizationPolicy) is None: |
CM |
42 |
raise ConfigurationError( |
|
43 |
'Cannot configure an authentication policy without ' |
|
44 |
'also configuring an authorization policy ' |
0c29cf
|
45 |
'(use the set_authorization_policy method)' |
MM |
46 |
) |
|
47 |
|
|
48 |
intr = self.introspectable( |
|
49 |
'authentication policy', |
|
50 |
None, |
|
51 |
self.object_description(policy), |
|
52 |
'authentication policy', |
|
53 |
) |
7f72f8
|
54 |
intr['policy'] = policy |
eb2fee
|
55 |
# authentication policy used by view config (phase 3) |
0c29cf
|
56 |
self.action( |
MM |
57 |
IAuthenticationPolicy, |
|
58 |
register, |
|
59 |
order=PHASE2_CONFIG, |
|
60 |
introspectables=(intr,), |
|
61 |
) |
5bf23f
|
62 |
|
CM |
63 |
def _set_authentication_policy(self, policy): |
|
64 |
policy = self.maybe_dotted(policy) |
|
65 |
self.registry.registerUtility(policy, IAuthenticationPolicy) |
|
66 |
|
|
67 |
@action_method |
|
68 |
def set_authorization_policy(self, policy): |
|
69 |
""" Override the :app:`Pyramid` :term:`authorization policy` in the |
|
70 |
current configuration. The ``policy`` argument must be an instance |
|
71 |
of an authorization policy or a :term:`dotted Python name` that points |
|
72 |
at an instance of an authorization policy. |
adfc23
|
73 |
|
012b97
|
74 |
.. note:: |
M |
75 |
|
|
76 |
Using the ``authorization_policy`` argument to the |
|
77 |
:class:`pyramid.config.Configurator` constructor can be used to |
|
78 |
achieve the same purpose. |
5bf23f
|
79 |
""" |
0c29cf
|
80 |
|
eb2fee
|
81 |
def register(): |
CM |
82 |
self._set_authorization_policy(policy) |
0c29cf
|
83 |
|
f67ba4
|
84 |
def ensure(): |
CM |
85 |
if self.autocommit: |
|
86 |
return |
|
87 |
if self.registry.queryUtility(IAuthenticationPolicy) is None: |
|
88 |
raise ConfigurationError( |
|
89 |
'Cannot configure an authorization policy without ' |
|
90 |
'also configuring an authentication policy ' |
0c29cf
|
91 |
'(use the set_authorization_policy method)' |
MM |
92 |
) |
012b97
|
93 |
|
0c29cf
|
94 |
intr = self.introspectable( |
MM |
95 |
'authorization policy', |
|
96 |
None, |
|
97 |
self.object_description(policy), |
|
98 |
'authorization policy', |
|
99 |
) |
7f72f8
|
100 |
intr['policy'] = policy |
eb2fee
|
101 |
# authorization policy used by view config (phase 3) and |
CM |
102 |
# authentication policy (phase 2) |
0c29cf
|
103 |
self.action( |
MM |
104 |
IAuthorizationPolicy, |
|
105 |
register, |
|
106 |
order=PHASE1_CONFIG, |
|
107 |
introspectables=(intr,), |
|
108 |
) |
3171fb
|
109 |
self.action(None, ensure) |
5bf23f
|
110 |
|
CM |
111 |
def _set_authorization_policy(self, policy): |
|
112 |
policy = self.maybe_dotted(policy) |
|
113 |
self.registry.registerUtility(policy, IAuthorizationPolicy) |
|
114 |
|
|
115 |
@action_method |
|
116 |
def set_default_permission(self, permission): |
|
117 |
""" |
|
118 |
Set the default permission to be used by all subsequent |
|
119 |
:term:`view configuration` registrations. ``permission`` |
|
120 |
should be a :term:`permission` string to be used as the |
|
121 |
default permission. An example of a permission |
|
122 |
string:``'view'``. Adding a default permission makes it |
|
123 |
unnecessary to protect each view configuration with an |
|
124 |
explicit permission, unless your application policy requires |
|
125 |
some exception for a particular view. |
|
126 |
|
|
127 |
If a default permission is *not* set, views represented by |
|
128 |
view configuration registrations which do not explicitly |
|
129 |
declare a permission will be executable by entirely anonymous |
|
130 |
users (any authorization policy is ignored). |
|
131 |
|
|
132 |
Later calls to this method override will conflict with earlier calls; |
|
133 |
there can be only one default permission active at a time within an |
|
134 |
application. |
|
135 |
|
|
136 |
.. warning:: |
|
137 |
|
|
138 |
If a default permission is in effect, view configurations meant to |
|
139 |
create a truly anonymously accessible view (even :term:`exception |
adfc23
|
140 |
view` views) *must* use the value of the permission importable as |
CM |
141 |
:data:`pyramid.security.NO_PERMISSION_REQUIRED`. When this string |
|
142 |
is used as the ``permission`` for a view configuration, the default |
|
143 |
permission is ignored, and the view is registered, making it |
|
144 |
available to all callers regardless of their credentials. |
5bf23f
|
145 |
|
2033ee
|
146 |
.. seealso:: |
SP |
147 |
|
|
148 |
See also :ref:`setting_a_default_permission`. |
5bf23f
|
149 |
|
012b97
|
150 |
.. note:: |
M |
151 |
|
|
152 |
Using the ``default_permission`` argument to the |
|
153 |
:class:`pyramid.config.Configurator` constructor can be used to |
|
154 |
achieve the same purpose. |
5bf23f
|
155 |
""" |
0c29cf
|
156 |
|
eb2fee
|
157 |
def register(): |
CM |
158 |
self.registry.registerUtility(permission, IDefaultPermission) |
0c29cf
|
159 |
|
MM |
160 |
intr = self.introspectable( |
|
161 |
'default permission', None, permission, 'default permission' |
|
162 |
) |
7f72f8
|
163 |
intr['value'] = permission |
0c29cf
|
164 |
perm_intr = self.introspectable( |
MM |
165 |
'permissions', permission, permission, 'permission' |
|
166 |
) |
7f72f8
|
167 |
perm_intr['value'] = permission |
CM |
168 |
# default permission used during view registration (phase 3) |
0c29cf
|
169 |
self.action( |
MM |
170 |
IDefaultPermission, |
|
171 |
register, |
|
172 |
order=PHASE1_CONFIG, |
|
173 |
introspectables=(intr, perm_intr), |
|
174 |
) |
5bf23f
|
175 |
|
6b180c
|
176 |
def add_permission(self, permission_name): |
CM |
177 |
""" |
|
178 |
A configurator directive which registers a free-standing |
|
179 |
permission without associating it with a view callable. This can be |
|
180 |
used so that the permission shows up in the introspectable data under |
|
181 |
the ``permissions`` category (permissions mentioned via ``add_view`` |
|
182 |
already end up in there). For example:: |
|
183 |
|
|
184 |
config = Configurator() |
|
185 |
config.add_permission('view') |
|
186 |
""" |
|
187 |
intr = self.introspectable( |
0c29cf
|
188 |
'permissions', permission_name, permission_name, 'permission' |
MM |
189 |
) |
6b180c
|
190 |
intr['value'] = permission_name |
CM |
191 |
self.action(None, introspectables=(intr,)) |
|
192 |
|
de3d0c
|
193 |
@action_method |
MM |
194 |
def set_default_csrf_options( |
|
195 |
self, |
|
196 |
require_csrf=True, |
|
197 |
token='csrf_token', |
|
198 |
header='X-CSRF-Token', |
|
199 |
safe_methods=('GET', 'HEAD', 'OPTIONS', 'TRACE'), |
17fa5e
|
200 |
callback=None, |
de3d0c
|
201 |
): |
MM |
202 |
""" |
|
203 |
Set the default CSRF options used by subsequent view registrations. |
|
204 |
|
|
205 |
``require_csrf`` controls whether CSRF checks will be automatically |
|
206 |
enabled on each view in the application. This value is used as the |
|
207 |
fallback when ``require_csrf`` is left at the default of ``None`` on |
|
208 |
:meth:`pyramid.config.Configurator.add_view`. |
|
209 |
|
|
210 |
``token`` is the name of the CSRF token used in the body of the |
|
211 |
request, accessed via ``request.POST[token]``. Default: ``csrf_token``. |
|
212 |
|
|
213 |
``header`` is the name of the header containing the CSRF token, |
|
214 |
accessed via ``request.headers[header]``. Default: ``X-CSRF-Token``. |
|
215 |
|
|
216 |
If ``token`` or ``header`` are set to ``None`` they will not be used |
|
217 |
for checking CSRF tokens. |
|
218 |
|
|
219 |
``safe_methods`` is an iterable of HTTP methods which are expected to |
|
220 |
not contain side-effects as defined by RFC2616. Safe methods will |
|
221 |
never be automatically checked for CSRF tokens. |
|
222 |
Default: ``('GET', 'HEAD', 'OPTIONS', TRACE')``. |
|
223 |
|
17fa5e
|
224 |
If ``callback`` is set, it must be a callable accepting ``(request)`` |
MM |
225 |
and returning ``True`` if the request should be checked for a valid |
|
226 |
CSRF token. This callback allows an application to support |
|
227 |
alternate authentication methods that do not rely on cookies which |
|
228 |
are not subject to CSRF attacks. For example, if a request is |
|
229 |
authenticated using the ``Authorization`` header instead of a cookie, |
|
230 |
this may return ``False`` for that request so that clients do not |
859755
|
231 |
need to send the ``X-CSRF-Token`` header. The callback is only tested |
17fa5e
|
232 |
for non-safe methods as defined by ``safe_methods``. |
MM |
233 |
|
2078f2
|
234 |
.. versionadded:: 1.7 |
MM |
235 |
|
|
236 |
.. versionchanged:: 1.8 |
|
237 |
Added the ``callback`` option. |
|
238 |
|
de3d0c
|
239 |
""" |
17fa5e
|
240 |
options = DefaultCSRFOptions( |
0c29cf
|
241 |
require_csrf, token, header, safe_methods, callback |
17fa5e
|
242 |
) |
0c29cf
|
243 |
|
de3d0c
|
244 |
def register(): |
MM |
245 |
self.registry.registerUtility(options, IDefaultCSRFOptions) |
0c29cf
|
246 |
|
MM |
247 |
intr = self.introspectable( |
|
248 |
'default csrf view options', |
|
249 |
None, |
|
250 |
options, |
|
251 |
'default csrf view options', |
|
252 |
) |
de3d0c
|
253 |
intr['require_csrf'] = require_csrf |
MM |
254 |
intr['token'] = token |
|
255 |
intr['header'] = header |
|
256 |
intr['safe_methods'] = as_sorted_tuple(safe_methods) |
17fa5e
|
257 |
intr['callback'] = callback |
a2c7c7
|
258 |
|
0c29cf
|
259 |
self.action( |
MM |
260 |
IDefaultCSRFOptions, |
|
261 |
register, |
|
262 |
order=PHASE1_CONFIG, |
|
263 |
introspectables=(intr,), |
|
264 |
) |
de3d0c
|
265 |
|
7c0f09
|
266 |
@action_method |
MW |
267 |
def set_csrf_storage_policy(self, policy): |
|
268 |
""" |
682a9b
|
269 |
Set the :term:`CSRF storage policy` used by subsequent view |
MM |
270 |
registrations. |
7c0f09
|
271 |
|
MW |
272 |
``policy`` is a class that implements the |
682a9b
|
273 |
:meth:`pyramid.interfaces.ICSRFStoragePolicy` interface and defines |
MM |
274 |
how to generate and persist CSRF tokens. |
|
275 |
|
7c0f09
|
276 |
""" |
0c29cf
|
277 |
|
7c0f09
|
278 |
def register(): |
MW |
279 |
self.registry.registerUtility(policy, ICSRFStoragePolicy) |
0c29cf
|
280 |
|
MM |
281 |
intr = self.introspectable( |
|
282 |
'csrf storage policy', None, policy, 'csrf storage policy' |
|
283 |
) |
682a9b
|
284 |
intr['policy'] = policy |
MM |
285 |
self.action(ICSRFStoragePolicy, register, introspectables=(intr,)) |
7c0f09
|
286 |
|
a2c7c7
|
287 |
|
de3d0c
|
288 |
@implementer(IDefaultCSRFOptions) |
MM |
289 |
class DefaultCSRFOptions(object): |
17fa5e
|
290 |
def __init__(self, require_csrf, token, header, safe_methods, callback): |
de3d0c
|
291 |
self.require_csrf = require_csrf |
MM |
292 |
self.token = token |
|
293 |
self.header = header |
|
294 |
self.safe_methods = frozenset(safe_methods) |
17fa5e
|
295 |
self.callback = callback |