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

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.interface import implementer
3from pyramid.interfaces import IAuthorizationPolicy
5from pyramid.location import lineage
7from pyramid.compat import is_nonstr_iter
9from pyramid.security import ACLAllowed, ACLDenied, Allow, Deny, Everyone
12@implementer(IAuthorizationPolicy)
13class ACLAuthorizationPolicy(object):
14 """ An :term:`authorization policy` which consults an :term:`ACL`
15 object attached to a :term:`context` to determine authorization
16 information about a :term:`principal` or multiple principals.
17 If the context is part of a :term:`lineage`, the context's parents
18 are consulted for ACL information too. The following is true
19 about this security policy.
21 - When checking whether the 'current' user is permitted (via the
22 ``permits`` method), the security policy consults the
23 ``context`` for an ACL first. If no ACL exists on the context,
24 or one does exist but the ACL does not explicitly allow or deny
25 access for any of the effective principals, consult the
26 context's parent ACL, and so on, until the lineage is exhausted
27 or we determine that the policy permits or denies.
29 During this processing, if any :data:`pyramid.security.Deny`
30 ACE is found matching any principal in ``principals``, stop
31 processing by returning an
32 :class:`pyramid.security.ACLDenied` instance (equals
33 ``False``) immediately. If any
34 :data:`pyramid.security.Allow` ACE is found matching any
35 principal, stop processing by returning an
36 :class:`pyramid.security.ACLAllowed` instance (equals
37 ``True``) immediately. If we exhaust the context's
38 :term:`lineage`, and no ACE has explicitly permitted or denied
39 access, return an instance of
40 :class:`pyramid.security.ACLDenied` (equals ``False``).
42 - When computing principals allowed by a permission via the
43 :func:`pyramid.security.principals_allowed_by_permission`
44 method, we compute the set of principals that are explicitly
45 granted the ``permission`` in the provided ``context``. We do
46 this by walking 'up' the object graph *from the root* to the
47 context. During this walking process, if we find an explicit
48 :data:`pyramid.security.Allow` ACE for a principal that
49 matches the ``permission``, the principal is included in the
50 allow list. However, if later in the walking process that
51 principal is mentioned in any :data:`pyramid.security.Deny`
52 ACE for the permission, the principal is removed from the allow
53 list. If a :data:`pyramid.security.Deny` to the principal
54 :data:`pyramid.security.Everyone` is encountered during the
55 walking process that matches the ``permission``, the allow list
56 is cleared for all principals encountered in previous ACLs. The
57 walking process ends after we've processed the any ACL directly
58 attached to ``context``; a set of principals is returned.
60 Objects of this class implement the
61 :class:`pyramid.interfaces.IAuthorizationPolicy` interface.
62 """
64 def permits(self, context, principals, permission):
65 """ Return an instance of
66 :class:`pyramid.security.ACLAllowed` instance if the policy
67 permits access, return an instance of
68 :class:`pyramid.security.ACLDenied` if not."""
70 acl = '<No ACL found on any object in resource lineage>'
72 for location in lineage(context):
73 try:
74 acl = location.__acl__
75 except AttributeError:
76 continue
78 if acl and callable(acl):
79 acl = acl()
81 for ace in acl:
82 ace_action, ace_principal, ace_permissions = ace
83 if ace_principal in principals:
84 if not is_nonstr_iter(ace_permissions):
85 ace_permissions = [ace_permissions]
86 if permission in ace_permissions:
87 if ace_action == Allow:
88 return ACLAllowed(
89 ace, acl, permission, principals, location
90 )
91 else:
92 return ACLDenied(
93 ace, acl, permission, principals, location
94 )
96 # default deny (if no ACL in lineage at all, or if none of the
97 # principals were mentioned in any ACE we found)
98 return ACLDenied(
99 '<default deny>', acl, permission, principals, context
100 )
102 def principals_allowed_by_permission(self, context, permission):
103 """ Return the set of principals explicitly granted the
104 permission named ``permission`` according to the ACL directly
105 attached to the ``context`` as well as inherited ACLs based on
106 the :term:`lineage`."""
107 allowed = set()
109 for location in reversed(list(lineage(context))):
110 # NB: we're walking *up* the object graph from the root
111 try:
112 acl = location.__acl__
113 except AttributeError:
114 continue
116 allowed_here = set()
117 denied_here = set()
119 if acl and callable(acl):
120 acl = acl()
122 for ace_action, ace_principal, ace_permissions in acl:
123 if not is_nonstr_iter(ace_permissions):
124 ace_permissions = [ace_permissions]
125 if (ace_action == Allow) and (permission in ace_permissions):
126 if ace_principal not in denied_here:
127 allowed_here.add(ace_principal)
128 if (ace_action == Deny) and (permission in ace_permissions):
129 denied_here.add(ace_principal)
130 if ace_principal == Everyone:
131 # clear the entire allowed set, as we've hit a
132 # deny of Everyone ala (Deny, Everyone, ALL)
133 allowed = set()
134 break
135 elif ace_principal in allowed:
136 allowed.remove(ace_principal)
138 allowed.update(allowed_here)
140 return allowed