3 from abc import ABC, abstractmethod
8 from typing import Any, Callable, List, Optional, Set, Sequence
10 from overrides import overrides
12 # This module is commonly used by others in here and should avoid
13 # taking any unnecessary dependencies back on them.
15 logger = logging.getLogger(__name__)
18 class Order(enum.Enum):
19 """A helper to express the order of evaluation for allows/denies
20 in an Access Control List.
28 """A simple Access Control List interface."""
33 order_to_check_allow_deny: Order,
36 if order_to_check_allow_deny not in (
37 Order.ALLOW_DENY, Order.DENY_ALLOW
40 'order_to_check_allow_deny must be Order.ALLOW_DENY or ' +
42 self.order_to_check_allow_deny = order_to_check_allow_deny
43 self.default_answer = default_answer
45 def __call__(self, x: Any) -> bool:
46 """Returns True if x is allowed, False otherwise."""
47 logger.debug(f'SimpleACL checking {x}')
48 if self.order_to_check_allow_deny == Order.ALLOW_DENY:
49 logger.debug('Checking allowed first...')
50 if self.check_allowed(x):
51 logger.debug(f'{x} was allowed explicitly.')
53 logger.debug('Checking denied next...')
54 if self.check_denied(x):
55 logger.debug(f'{x} was denied explicitly.')
57 elif self.order_to_check_allow_deny == Order.DENY_ALLOW:
58 logger.debug('Checking denied first...')
59 if self.check_denied(x):
60 logger.debug(f'{x} was denied explicitly.')
62 if self.check_allowed(x):
63 logger.debug(f'{x} was allowed explicitly.')
67 f'{x} was not explicitly allowed or denied; ' +
68 f'using default answer ({self.default_answer})'
70 return self.default_answer
73 def check_allowed(self, x: Any) -> bool:
74 """Return True if x is explicitly allowed, False otherwise."""
78 def check_denied(self, x: Any) -> bool:
79 """Return True if x is explicitly denied, False otherwise."""
83 class SetBasedACL(SimpleACL):
84 """An ACL that allows or denies based on membership in a set."""
87 allow_set: Optional[Set[Any]] = None,
88 deny_set: Optional[Set[Any]] = None,
89 order_to_check_allow_deny: Order,
90 default_answer: bool) -> None:
92 order_to_check_allow_deny=order_to_check_allow_deny,
93 default_answer=default_answer
95 self.allow_set = allow_set
96 self.deny_set = deny_set
99 def check_allowed(self, x: Any) -> bool:
100 if self.allow_set is None:
102 return x in self.allow_set
105 def check_denied(self, x: Any) -> bool:
106 if self.deny_set is None:
108 return x in self.deny_set
111 class AllowListACL(SetBasedACL):
112 """Convenience subclass for a list that only allows known items.
117 allow_set: Optional[Set[Any]]) -> None:
119 allow_set = allow_set,
120 order_to_check_allow_deny = Order.ALLOW_DENY,
121 default_answer = False)
124 class DenyListACL(SetBasedACL):
125 """Convenience subclass for a list that only disallows known items.
130 deny_set: Optional[Set[Any]]) -> None:
133 order_to_check_allow_deny = Order.ALLOW_DENY,
134 default_answer = True)
137 class BlockListACL(SetBasedACL):
138 """Convenience subclass for a list that only disallows known items.
143 deny_set: Optional[Set[Any]]) -> None:
146 order_to_check_allow_deny = Order.ALLOW_DENY,
147 default_answer = True)
150 class PredicateListBasedACL(SimpleACL):
151 """An ACL that allows or denies by applying predicates."""
154 allow_predicate_list: Sequence[Callable[[Any], bool]] = None,
155 deny_predicate_list: Sequence[Callable[[Any], bool]] = None,
156 order_to_check_allow_deny: Order,
157 default_answer: bool) -> None:
159 order_to_check_allow_deny=order_to_check_allow_deny,
160 default_answer=default_answer
162 self.allow_predicate_list = allow_predicate_list
163 self.deny_predicate_list = deny_predicate_list
166 def check_allowed(self, x: Any) -> bool:
167 if self.allow_predicate_list is None:
169 return any(predicate(x) for predicate in self.allow_predicate_list)
172 def check_denied(self, x: Any) -> bool:
173 if self.deny_predicate_list is None:
175 return any(predicate(x) for predicate in self.deny_predicate_list)
178 class StringWildcardBasedACL(PredicateListBasedACL):
179 """An ACL that allows or denies based on string glob (*, ?) patterns."""
182 allowed_patterns: Optional[List[str]] = None,
183 denied_patterns: Optional[List[str]] = None,
184 order_to_check_allow_deny: Order,
185 default_answer: bool) -> None:
186 allow_predicates = []
187 if allowed_patterns is not None:
188 for pattern in allowed_patterns:
189 allow_predicates.append(
190 lambda x, pattern=pattern: fnmatch.fnmatch(x, pattern)
192 deny_predicates = None
193 if denied_patterns is not None:
195 for pattern in denied_patterns:
196 deny_predicates.append(
197 lambda x, pattern=pattern: fnmatch.fnmatch(x, pattern)
201 allow_predicate_list=allow_predicates,
202 deny_predicate_list=deny_predicates,
203 order_to_check_allow_deny=order_to_check_allow_deny,
204 default_answer=default_answer,
208 class StringREBasedACL(PredicateListBasedACL):
209 """An ACL that allows or denies by applying regexps."""
212 allowed_regexs: Optional[List[re.Pattern]] = None,
213 denied_regexs: Optional[List[re.Pattern]] = None,
214 order_to_check_allow_deny: Order,
215 default_answer: bool) -> None:
216 allow_predicates = None
217 if allowed_regexs is not None:
218 allow_predicates = []
219 for pattern in allowed_regexs:
220 allow_predicates.append(
221 lambda x, pattern=pattern: pattern.match(x) is not None
223 deny_predicates = None
224 if denied_regexs is not None:
226 for pattern in denied_regexs:
227 deny_predicates.append(
228 lambda x, pattern=pattern: pattern.match(x) is not None
231 allow_predicate_list=allow_predicates,
232 deny_predicate_list=deny_predicates,
233 order_to_check_allow_deny=order_to_check_allow_deny,
234 default_answer=default_answer,
238 class AnyCompoundACL(SimpleACL):
239 """An ACL that allows if any of its subacls allow."""
242 subacls: Optional[List[SimpleACL]] = None,
243 order_to_check_allow_deny: Order,
244 default_answer: bool) -> None:
246 order_to_check_allow_deny = order_to_check_allow_deny,
247 default_answer = default_answer
249 self.subacls = subacls
252 def check_allowed(self, x: Any) -> bool:
253 if self.subacls is None:
255 return any(acl(x) for acl in self.subacls)
258 def check_denied(self, x: Any) -> bool:
259 if self.subacls is None:
261 return any(not acl(x) for acl in self.subacls)
264 class AllCompoundACL(SimpleACL):
265 """An ACL that allows if all of its subacls allow."""
268 subacls: Optional[List[SimpleACL]] = None,
269 order_to_check_allow_deny: Order,
270 default_answer: bool) -> None:
272 order_to_check_allow_deny = order_to_check_allow_deny,
273 default_answer = default_answer
275 self.subacls = subacls
278 def check_allowed(self, x: Any) -> bool:
279 if self.subacls is None:
281 return all(acl(x) for acl in self.subacls)
284 def check_denied(self, x: Any) -> bool:
285 if self.subacls is None:
287 return any(not acl(x) for acl in self.subacls)