Michael Merickel
2017-04-13 8597552cbf9b49366c7d2a27c123d140877a1bab
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