Hide keyboard shortcuts

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 

2 

3from pyramid.interfaces import IAuthorizationPolicy 

4 

5from pyramid.location import lineage 

6 

7from pyramid.compat import is_nonstr_iter 

8 

9from pyramid.security import ACLAllowed, ACLDenied, Allow, Deny, Everyone 

10 

11 

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. 

20 

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. 

28 

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``). 

41 

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. 

59 

60 Objects of this class implement the 

61 :class:`pyramid.interfaces.IAuthorizationPolicy` interface. 

62 """ 

63 

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.""" 

69 

70 acl = '<No ACL found on any object in resource lineage>' 

71 

72 for location in lineage(context): 

73 try: 

74 acl = location.__acl__ 

75 except AttributeError: 

76 continue 

77 

78 if acl and callable(acl): 

79 acl = acl() 

80 

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 ) 

95 

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 ) 

101 

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() 

108 

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 

115 

116 allowed_here = set() 

117 denied_here = set() 

118 

119 if acl and callable(acl): 

120 acl = acl() 

121 

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) 

137 

138 allowed.update(allowed_here) 

139 

140 return allowed