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