Clean up ACLs
[python_utils.git] / simple_acl.py
index 39129ce417206f7bc41dae80f92618de5a512c74..5040304a7913fe4e452ceb2dc5ef5f2a76b36491 100644 (file)
@@ -34,32 +34,44 @@ class SimpleACL(ABC):
 
     def __call__(self, x: Any) -> bool:
         """Returns True if x is allowed, False otherwise."""
+        logger.debug(f'SimpleACL checking {x}')
         if self.order_to_check_allow_deny == ACL_ORDER_ALLOW_DENY:
+            logger.debug('Checking allowed first...')
             if self.check_allowed(x):
+                logger.debug(f'{x} was allowed explicitly.')
                 return True
+            logger.debug('Checking denied next...')
             if self.check_denied(x):
+                logger.debug(f'{x} was denied explicitly.')
                 return False
-            return self.default_answer
         elif self.order_to_check_allow_deny == ACL_ORDER_DENY_ALLOW:
+            logger.debug('Checking denied first...')
             if self.check_denied(x):
+                logger.debug(f'{x} was denied explicitly.')
                 return False
             if self.check_allowed(x):
+                logger.debug(f'{x} was allowed explicitly.')
                 return True
-            return self.default_answer
-        raise Exception('Should never get here.')
+
+        logger.debug(
+            f'{x} was not explicitly allowed or denied; ' +
+            f'using default answer ({self.default_answer})'
+        )
+        return self.default_answer
 
     @abstractmethod
     def check_allowed(self, x: Any) -> bool:
-        """Return True if x is allowed, False otherwise."""
+        """Return True if x is explicitly allowed, False otherwise."""
         pass
 
     @abstractmethod
     def check_denied(self, x: Any) -> bool:
-        """Return True if x is denied, False otherwise."""
+        """Return True if x is explicitly denied, False otherwise."""
         pass
 
 
 class SetBasedACL(SimpleACL):
+    """An ACL that allows or denies based on membership in a set."""
     def __init__(self,
                  *,
                  allow_set: Optional[Set[Any]] = None,
@@ -85,6 +97,7 @@ class SetBasedACL(SimpleACL):
 
 
 class PredicateListBasedACL(SimpleACL):
+    """An ACL that allows or denies by applying predicates."""
     def __init__(self,
                  *,
                  allow_predicate_list: List[Callable[[Any], bool]] = None,
@@ -110,6 +123,7 @@ class PredicateListBasedACL(SimpleACL):
 
 
 class StringWildcardBasedACL(PredicateListBasedACL):
+    """An ACL that allows or denies based on string glob (*, ?) patterns."""
     def __init__(self,
                  *,
                  allowed_patterns: Optional[List[str]] = None,
@@ -139,6 +153,7 @@ class StringWildcardBasedACL(PredicateListBasedACL):
 
 
 class StringREBasedACL(PredicateListBasedACL):
+    """An ACL that allows or denies by applying regexps."""
     def __init__(self,
                  *,
                  allowed_regexs: Optional[List[re.Pattern]] = None,
@@ -167,26 +182,21 @@ class StringREBasedACL(PredicateListBasedACL):
         )
 
 
-class CompoundACL(object):
-    ANY = 1
-    ALL = 2
+class AnyCompoundACL(object):
+    """An ACL that allows if any of its subacls allow."""
+    def __init__(self, subacls: List[SimpleACL]):
+        assert subacls is not None
+        self.subacls = subacls
 
-    def __init__(
-            self,
-            *,
-            subacls: Optional[List[SimpleACL]],
-            match_requirement: int = ALL
-    ) -> None:
+    def __call__(self, x: Any):
+        return any(acl(x) for acl in self.subacls)
+
+
+class AllCompoundACL(object):
+    """An ACL that allows if all of its subacls allow."""
+    def __init__(self, subacls: List[SimpleACL]):
+        assert subacls is not None
         self.subacls = subacls
-        if match_requirement not in (CompoundACL.ANY, CompoundACL.ALL):
-            raise Exception(
-                'match_requirement must be CompoundACL.ANY or CompoundACL.ALL'
-            )
-        self.match_requirement = match_requirement
 
-    def __call__(self, x: Any) -> bool:
-        if self.match_requirement == CompoundACL.ANY:
-            return any(acl(x) for acl in self.subacls)
-        elif self.match_requirement == CompoundACL.ALL:
-            return all(acl(x) for acl in self.subacls)
-        raise Exception('Should never get here.')
+    def __call__(self, x: Any):
+        return all(acl(x) for acl in self.subacls)