3 from abc import ABC, abstractmethod
7 from typing import Any, Callable, List, Optional, Set
10 logger = logging.getLogger(__name__)
13 ACL_ORDER_ALLOW_DENY = 1
14 ACL_ORDER_DENY_ALLOW = 2
18 """A simple Access Control List interface."""
23 order_to_check_allow_deny: int,
26 if order_to_check_allow_deny not in (
27 ACL_ORDER_ALLOW_DENY, ACL_ORDER_DENY_ALLOW
30 'order_to_check_allow_deny must be ACL_ORDER_ALLOW_DENY or ' +
31 'ACL_ORDER_DENY_ALLOW')
32 self.order_to_check_allow_deny = order_to_check_allow_deny
33 self.default_answer = default_answer
35 def __call__(self, x: Any) -> bool:
36 """Returns True if x is allowed, False otherwise."""
37 if self.order_to_check_allow_deny == ACL_ORDER_ALLOW_DENY:
38 if self.check_allowed(x):
40 if self.check_denied(x):
42 return self.default_answer
43 elif self.order_to_check_allow_deny == ACL_ORDER_DENY_ALLOW:
44 if self.check_denied(x):
46 if self.check_allowed(x):
48 return self.default_answer
49 raise Exception('Should never get here.')
52 def check_allowed(self, x: Any) -> bool:
53 """Return True if x is allowed, False otherwise."""
57 def check_denied(self, x: Any) -> bool:
58 """Return True if x is denied, False otherwise."""
62 class SetBasedACL(SimpleACL):
65 allow_set: Optional[Set[Any]] = None,
66 deny_set: Optional[Set[Any]] = None,
67 order_to_check_allow_deny: int,
68 default_answer: bool) -> None:
70 order_to_check_allow_deny=order_to_check_allow_deny,
71 default_answer=default_answer
73 self.allow_set = allow_set
74 self.deny_set = deny_set
76 def check_allowed(self, x: Any) -> bool:
77 if self.allow_set is None:
79 return x in self.allow_set
81 def check_denied(self, x: Any) -> bool:
82 if self.deny_set is None:
84 return x in self.deny_set
87 class PredicateListBasedACL(SimpleACL):
90 allow_predicate_list: List[Callable[[Any], bool]] = None,
91 deny_predicate_list: List[Callable[[Any], bool]] = None,
92 order_to_check_allow_deny: int,
93 default_answer: bool) -> None:
95 order_to_check_allow_deny=order_to_check_allow_deny,
96 default_answer=default_answer
98 self.allow_predicate_list = allow_predicate_list
99 self.deny_predicate_list = deny_predicate_list
101 def check_allowed(self, x: Any) -> bool:
102 if self.allow_predicate_list is None:
104 return any(predicate(x) for predicate in self.allow_predicate_list)
106 def check_denied(self, x: Any) -> bool:
107 if self.deny_predicate_list is None:
109 return any(predicate(x) for predicate in self.deny_predicate_list)
112 class StringWildcardBasedACL(PredicateListBasedACL):
115 allowed_patterns: Optional[List[str]] = None,
116 denied_patterns: Optional[List[str]] = None,
117 order_to_check_allow_deny: int,
118 default_answer: bool) -> None:
119 allow_predicates = []
120 if allowed_patterns is not None:
121 for pattern in allowed_patterns:
122 allow_predicates.append(
123 lambda x, pattern=pattern: fnmatch.fnmatch(x, pattern)
125 deny_predicates = None
126 if denied_patterns is not None:
128 for pattern in denied_patterns:
129 deny_predicates.append(
130 lambda x, pattern=pattern: fnmatch.fnmatch(x, pattern)
134 allow_predicate_list=allow_predicates,
135 deny_predicate_list=deny_predicates,
136 order_to_check_allow_deny=order_to_check_allow_deny,
137 default_answer=default_answer,
141 class StringREBasedACL(PredicateListBasedACL):
144 allowed_regexs: Optional[List[re.Pattern]] = None,
145 denied_regexs: Optional[List[re.Pattern]] = None,
146 order_to_check_allow_deny: int,
147 default_answer: bool) -> None:
148 allow_predicates = None
149 if allowed_regexs is not None:
150 allow_predicates = []
151 for pattern in allowed_regexs:
152 allow_predicates.append(
153 lambda x, pattern=pattern: pattern.match(x) is not None
155 deny_predicates = None
156 if denied_regexs is not None:
158 for pattern in denied_regexs:
159 deny_predicates.append(
160 lambda x, pattern=pattern: pattern.match(x) is not None
163 allow_predicate_list=allow_predicates,
164 deny_predicate_list=deny_predicates,
165 order_to_check_allow_deny=order_to_check_allow_deny,
166 default_answer=default_answer,
170 class CompoundACL(object):
177 subacls: Optional[List[SimpleACL]],
178 match_requirement: int = ALL
180 self.subacls = subacls
181 if match_requirement not in (CompoundACL.ANY, CompoundACL.ALL):
183 'match_requirement must be CompoundACL.ANY or CompoundACL.ALL'
185 self.match_requirement = match_requirement
187 def __call__(self, x: Any) -> bool:
188 if self.match_requirement == CompoundACL.ANY:
189 return any(acl(x) for acl in self.subacls)
190 elif self.match_requirement == CompoundACL.ALL:
191 return all(acl(x) for acl in self.subacls)
192 raise Exception('Should never get here.')