Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/pyramid/security.py : 40%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1from zope.deprecation import deprecated
2from zope.interface import providedBy
4from pyramid.interfaces import (
5 IAuthenticationPolicy,
6 IAuthorizationPolicy,
7 ISecuredView,
8 IView,
9 IViewClassifier,
10)
12from pyramid.compat import map_
13from pyramid.threadlocal import get_current_registry
15Everyone = 'system.Everyone'
16Authenticated = 'system.Authenticated'
17Allow = 'Allow'
18Deny = 'Deny'
21class AllPermissionsList(object):
22 """ Stand in 'permission list' to represent all permissions """
24 def __iter__(self):
25 return iter(())
27 def __contains__(self, other):
28 return True
30 def __eq__(self, other):
31 return isinstance(other, self.__class__)
34ALL_PERMISSIONS = AllPermissionsList()
35DENY_ALL = (Deny, Everyone, ALL_PERMISSIONS)
37NO_PERMISSION_REQUIRED = '__no_permission_required__'
40def _get_registry(request):
41 try:
42 reg = request.registry
43 except AttributeError:
44 reg = get_current_registry() # b/c
45 return reg
48def _get_authentication_policy(request):
49 registry = _get_registry(request)
50 return registry.queryUtility(IAuthenticationPolicy)
53def has_permission(permission, context, request):
54 """
55 A function that calls :meth:`pyramid.request.Request.has_permission`
56 and returns its result.
58 .. deprecated:: 1.5
59 Use :meth:`pyramid.request.Request.has_permission` instead.
61 .. versionchanged:: 1.5a3
62 If context is None, then attempt to use the context attribute of self;
63 if not set, then the AttributeError is propagated.
64 """
65 return request.has_permission(permission, context)
68deprecated(
69 'has_permission',
70 'As of Pyramid 1.5 the "pyramid.security.has_permission" API is now '
71 'deprecated. It will be removed in Pyramid 1.8. Use the '
72 '"has_permission" method of the Pyramid request instead.',
73)
76def authenticated_userid(request):
77 """
78 A function that returns the value of the property
79 :attr:`pyramid.request.Request.authenticated_userid`.
81 .. deprecated:: 1.5
82 Use :attr:`pyramid.request.Request.authenticated_userid` instead.
83 """
84 return request.authenticated_userid
87deprecated(
88 'authenticated_userid',
89 'As of Pyramid 1.5 the "pyramid.security.authenticated_userid" API is now '
90 'deprecated. It will be removed in Pyramid 1.8. Use the '
91 '"authenticated_userid" attribute of the Pyramid request instead.',
92)
95def unauthenticated_userid(request):
96 """
97 A function that returns the value of the property
98 :attr:`pyramid.request.Request.unauthenticated_userid`.
100 .. deprecated:: 1.5
101 Use :attr:`pyramid.request.Request.unauthenticated_userid` instead.
102 """
103 return request.unauthenticated_userid
106deprecated(
107 'unauthenticated_userid',
108 'As of Pyramid 1.5 the "pyramid.security.unauthenticated_userid" API is '
109 'now deprecated. It will be removed in Pyramid 1.8. Use the '
110 '"unauthenticated_userid" attribute of the Pyramid request instead.',
111)
114def effective_principals(request):
115 """
116 A function that returns the value of the property
117 :attr:`pyramid.request.Request.effective_principals`.
119 .. deprecated:: 1.5
120 Use :attr:`pyramid.request.Request.effective_principals` instead.
121 """
122 return request.effective_principals
125deprecated(
126 'effective_principals',
127 'As of Pyramid 1.5 the "pyramid.security.effective_principals" API is '
128 'now deprecated. It will be removed in Pyramid 1.8. Use the '
129 '"effective_principals" attribute of the Pyramid request instead.',
130)
133def remember(request, userid, **kw):
134 """
135 Returns a sequence of header tuples (e.g. ``[('Set-Cookie', 'foo=abc')]``)
136 on this request's response.
137 These headers are suitable for 'remembering' a set of credentials
138 implied by the data passed as ``userid`` and ``*kw`` using the
139 current :term:`authentication policy`. Common usage might look
140 like so within the body of a view function (``response`` is
141 assumed to be a :term:`WebOb` -style :term:`response` object
142 computed previously by the view code):
144 .. code-block:: python
146 from pyramid.security import remember
147 headers = remember(request, 'chrism', password='123', max_age='86400')
148 response = request.response
149 response.headerlist.extend(headers)
150 return response
152 If no :term:`authentication policy` is in use, this function will
153 always return an empty sequence. If used, the composition and
154 meaning of ``**kw`` must be agreed upon by the calling code and
155 the effective authentication policy.
157 .. versionchanged:: 1.6
158 Deprecated the ``principal`` argument in favor of ``userid`` to clarify
159 its relationship to the authentication policy.
161 .. versionchanged:: 1.10
162 Removed the deprecated ``principal`` argument.
163 """
164 policy = _get_authentication_policy(request)
165 if policy is None:
166 return []
167 return policy.remember(request, userid, **kw)
170def forget(request):
171 """
172 Return a sequence of header tuples (e.g. ``[('Set-Cookie',
173 'foo=abc')]``) suitable for 'forgetting' the set of credentials
174 possessed by the currently authenticated user. A common usage
175 might look like so within the body of a view function
176 (``response`` is assumed to be an :term:`WebOb` -style
177 :term:`response` object computed previously by the view code):
179 .. code-block:: python
181 from pyramid.security import forget
182 headers = forget(request)
183 response.headerlist.extend(headers)
184 return response
186 If no :term:`authentication policy` is in use, this function will
187 always return an empty sequence.
188 """
189 policy = _get_authentication_policy(request)
190 if policy is None:
191 return []
192 return policy.forget(request)
195def principals_allowed_by_permission(context, permission):
196 """ Provided a ``context`` (a resource object), and a ``permission``
197 (a string or unicode object), if an :term:`authorization policy` is
198 in effect, return a sequence of :term:`principal` ids that possess
199 the permission in the ``context``. If no authorization policy is
200 in effect, this will return a sequence with the single value
201 :mod:`pyramid.security.Everyone` (the special principal
202 identifier representing all principals).
204 .. note::
206 Even if an :term:`authorization policy` is in effect,
207 some (exotic) authorization policies may not implement the
208 required machinery for this function; those will cause a
209 :exc:`NotImplementedError` exception to be raised when this
210 function is invoked.
211 """
212 reg = get_current_registry()
213 policy = reg.queryUtility(IAuthorizationPolicy)
214 if policy is None:
215 return [Everyone]
216 return policy.principals_allowed_by_permission(context, permission)
219def view_execution_permitted(context, request, name=''):
220 """ If the view specified by ``context`` and ``name`` is protected
221 by a :term:`permission`, check the permission associated with the
222 view using the effective authentication/authorization policies and
223 the ``request``. Return a boolean result. If no
224 :term:`authorization policy` is in effect, or if the view is not
225 protected by a permission, return ``True``. If no view can view found,
226 an exception will be raised.
228 .. versionchanged:: 1.4a4
229 An exception is raised if no view is found.
231 """
232 reg = _get_registry(request)
233 provides = [IViewClassifier] + map_(providedBy, (request, context))
234 # XXX not sure what to do here about using _find_views or analogue;
235 # for now let's just keep it as-is
236 view = reg.adapters.lookup(provides, ISecuredView, name=name)
237 if view is None:
238 view = reg.adapters.lookup(provides, IView, name=name)
239 if view is None:
240 raise TypeError(
241 'No registered view satisfies the constraints. '
242 'It would not make sense to claim that this view '
243 '"is" or "is not" permitted.'
244 )
245 return Allowed(
246 'Allowed: view name %r in context %r (no permission defined)'
247 % (name, context)
248 )
249 return view.__permitted__(context, request)
252class PermitsResult(int):
253 def __new__(cls, s, *args):
254 """
255 Create a new instance.
257 :param fmt: A format string explaining the reason for denial.
258 :param args: Arguments are stored and used with the format string
259 to generate the ``msg``.
261 """
262 inst = int.__new__(cls, cls.boolval)
263 inst.s = s
264 inst.args = args
265 return inst
267 @property
268 def msg(self):
269 """ A string indicating why the result was generated."""
270 return self.s % self.args
272 def __str__(self):
273 return self.msg
275 def __repr__(self):
276 return '<%s instance at %s with msg %r>' % (
277 self.__class__.__name__,
278 id(self),
279 self.msg,
280 )
283class Denied(PermitsResult):
284 """
285 An instance of ``Denied`` is returned when a security-related
286 API or other :app:`Pyramid` code denies an action unrelated to
287 an ACL check. It evaluates equal to all boolean false types. It
288 has an attribute named ``msg`` describing the circumstances for
289 the deny.
291 """
293 boolval = 0
296class Allowed(PermitsResult):
297 """
298 An instance of ``Allowed`` is returned when a security-related
299 API or other :app:`Pyramid` code allows an action unrelated to
300 an ACL check. It evaluates equal to all boolean true types. It
301 has an attribute named ``msg`` describing the circumstances for
302 the allow.
304 """
306 boolval = 1
309class ACLPermitsResult(PermitsResult):
310 def __new__(cls, ace, acl, permission, principals, context):
311 """
312 Create a new instance.
314 :param ace: The :term:`ACE` that matched, triggering the result.
315 :param acl: The :term:`ACL` containing ``ace``.
316 :param permission: The required :term:`permission`.
317 :param principals: The list of :term:`principals <principal>` provided.
318 :param context: The :term:`context` providing the :term:`lineage`
319 searched.
321 """
322 fmt = (
323 '%s permission %r via ACE %r in ACL %r on context %r for '
324 'principals %r'
325 )
326 inst = PermitsResult.__new__(
327 cls, fmt, cls.__name__, permission, ace, acl, context, principals
328 )
329 inst.permission = permission
330 inst.ace = ace
331 inst.acl = acl
332 inst.principals = principals
333 inst.context = context
334 return inst
337class ACLDenied(ACLPermitsResult, Denied):
338 """
339 An instance of ``ACLDenied`` is a specialization of
340 :class:`pyramid.security.Denied` that represents that a security check
341 made explicitly against ACL was denied. It evaluates equal to all
342 boolean false types. It also has the following attributes: ``acl``,
343 ``ace``, ``permission``, ``principals``, and ``context``. These
344 attributes indicate the security values involved in the request. Its
345 ``__str__`` method prints a summary of these attributes for debugging
346 purposes. The same summary is available as the ``msg`` attribute.
348 """
351class ACLAllowed(ACLPermitsResult, Allowed):
352 """
353 An instance of ``ACLAllowed`` is a specialization of
354 :class:`pyramid.security.Allowed` that represents that a security check
355 made explicitly against ACL was allowed. It evaluates equal to all
356 boolean true types. It also has the following attributes: ``acl``,
357 ``ace``, ``permission``, ``principals``, and ``context``. These
358 attributes indicate the security values involved in the request. Its
359 ``__str__`` method prints a summary of these attributes for debugging
360 purposes. The same summary is available as the ``msg`` attribute.
362 """
365class AuthenticationAPIMixin(object):
366 def _get_authentication_policy(self):
367 reg = _get_registry(self)
368 return reg.queryUtility(IAuthenticationPolicy)
370 @property
371 def authenticated_userid(self):
372 """ Return the userid of the currently authenticated user or
373 ``None`` if there is no :term:`authentication policy` in effect or
374 there is no currently authenticated user.
376 .. versionadded:: 1.5
377 """
378 policy = self._get_authentication_policy()
379 if policy is None:
380 return None
381 return policy.authenticated_userid(self)
383 @property
384 def unauthenticated_userid(self):
385 """ Return an object which represents the *claimed* (not verified) user
386 id of the credentials present in the request. ``None`` if there is no
387 :term:`authentication policy` in effect or there is no user data
388 associated with the current request. This differs from
389 :attr:`~pyramid.request.Request.authenticated_userid`, because the
390 effective authentication policy will not ensure that a record
391 associated with the userid exists in persistent storage.
393 .. versionadded:: 1.5
394 """
395 policy = self._get_authentication_policy()
396 if policy is None:
397 return None
398 return policy.unauthenticated_userid(self)
400 @property
401 def effective_principals(self):
402 """ Return the list of 'effective' :term:`principal` identifiers
403 for the ``request``. If no :term:`authentication policy` is in effect,
404 this will return a one-element list containing the
405 :data:`pyramid.security.Everyone` principal.
407 .. versionadded:: 1.5
408 """
409 policy = self._get_authentication_policy()
410 if policy is None:
411 return [Everyone]
412 return policy.effective_principals(self)
415class AuthorizationAPIMixin(object):
416 def has_permission(self, permission, context=None):
417 """ Given a permission and an optional context, returns an instance of
418 :data:`pyramid.security.Allowed` if the permission is granted to this
419 request with the provided context, or the context already associated
420 with the request. Otherwise, returns an instance of
421 :data:`pyramid.security.Denied`. This method delegates to the current
422 authentication and authorization policies. Returns
423 :data:`pyramid.security.Allowed` unconditionally if no authentication
424 policy has been registered for this request. If ``context`` is not
425 supplied or is supplied as ``None``, the context used is the
426 ``request.context`` attribute.
428 :param permission: Does this request have the given permission?
429 :type permission: unicode, str
430 :param context: A resource object or ``None``
431 :type context: object
432 :returns: Either :class:`pyramid.security.Allowed` or
433 :class:`pyramid.security.Denied`.
435 .. versionadded:: 1.5
437 """
438 if context is None:
439 context = self.context
440 reg = _get_registry(self)
441 authn_policy = reg.queryUtility(IAuthenticationPolicy)
442 if authn_policy is None:
443 return Allowed('No authentication policy in use.')
444 authz_policy = reg.queryUtility(IAuthorizationPolicy)
445 if authz_policy is None:
446 raise ValueError(
447 'Authentication policy registered without '
448 'authorization policy'
449 ) # should never happen
450 principals = authn_policy.effective_principals(self)
451 return authz_policy.permits(context, principals, permission)