Home | History | Annotate | Download | only in diff
      1 # Copyright 2016, Tresys Technology, LLC
      2 #
      3 # This file is part of SETools.
      4 #
      5 # SETools is free software: you can redistribute it and/or modify
      6 # it under the terms of the GNU Lesser General Public License as
      7 # published by the Free Software Foundation, either version 2.1 of
      8 # the License, or (at your option) any later version.
      9 #
     10 # SETools is distributed in the hope that it will be useful,
     11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13 # GNU Lesser General Public License for more details.
     14 #
     15 # You should have received a copy of the GNU Lesser General Public
     16 # License along with SETools.  If not, see
     17 # <http://www.gnu.org/licenses/>.
     18 #
     19 from collections import defaultdict, namedtuple
     20 
     21 from .descriptors import DiffResultDescriptor
     22 from .difference import Difference, SymbolWrapper, Wrapper
     23 
     24 
     25 modified_rbacrule_record = namedtuple("modified_rbacrule", ["rule",
     26                                                             "added_default",
     27                                                             "removed_default"])
     28 
     29 
     30 class RBACRulesDifference(Difference):
     31 
     32     """Determine the difference in RBAC rules between two policies."""
     33 
     34     added_role_allows = DiffResultDescriptor("diff_role_allows")
     35     removed_role_allows = DiffResultDescriptor("diff_role_allows")
     36     # role allows cannot be modified, only added/removed
     37 
     38     added_role_transitions = DiffResultDescriptor("diff_role_transitions")
     39     removed_role_transitions = DiffResultDescriptor("diff_role_transitions")
     40     modified_role_transitions = DiffResultDescriptor("diff_role_transitions")
     41 
     42     # Lists of rules for each policy
     43     _left_rbac_rules = defaultdict(list)
     44     _right_rbac_rules = defaultdict(list)
     45 
     46     def diff_role_allows(self):
     47         """Generate the difference in role allow rules between the policies."""
     48 
     49         self.log.info(
     50             "Generating role allow differences from {0.left_policy} to {0.right_policy}".
     51             format(self))
     52 
     53         if not self._left_rbac_rules or not self._right_rbac_rules:
     54             self._create_rbac_rule_lists()
     55 
     56         self.added_role_allows, self.removed_role_allows, _ = self._set_diff(
     57             self._expand_generator(self._left_rbac_rules["allow"], RoleAllowWrapper),
     58             self._expand_generator(self._right_rbac_rules["allow"], RoleAllowWrapper))
     59 
     60     def diff_role_transitions(self):
     61         """Generate the difference in role_transition rules between the policies."""
     62 
     63         self.log.info(
     64             "Generating role_transition differences from {0.left_policy} to {0.right_policy}".
     65             format(self))
     66 
     67         if not self._left_rbac_rules or not self._right_rbac_rules:
     68             self._create_rbac_rule_lists()
     69 
     70         added, removed, matched = self._set_diff(
     71             self._expand_generator(self._left_rbac_rules["role_transition"], RoleTransitionWrapper),
     72             self._expand_generator(self._right_rbac_rules["role_transition"],
     73                                    RoleTransitionWrapper))
     74 
     75         modified = []
     76         for left_rule, right_rule in matched:
     77             # Criteria for modified rules
     78             # 1. change to default role
     79             if SymbolWrapper(left_rule.default) != SymbolWrapper(right_rule.default):
     80                 modified.append(modified_rbacrule_record(left_rule,
     81                                                          right_rule.default,
     82                                                          left_rule.default))
     83 
     84         self.added_role_transitions = added
     85         self.removed_role_transitions = removed
     86         self.modified_role_transitions = modified
     87 
     88     #
     89     # Internal functions
     90     #
     91     def _create_rbac_rule_lists(self):
     92         """Create rule lists for both policies."""
     93         # do not expand yet, to keep memory
     94         # use down as long as possible
     95         self.log.debug("Building RBAC rule lists from {0.left_policy}".format(self))
     96         for rule in self.left_policy.rbacrules():
     97             self._left_rbac_rules[rule.ruletype].append(rule)
     98 
     99         self.log.debug("Building RBAC rule lists from {0.right_policy}".format(self))
    100         for rule in self.right_policy.rbacrules():
    101             self._right_rbac_rules[rule.ruletype].append(rule)
    102 
    103         self.log.debug("Completed building RBAC rule lists.")
    104 
    105     def _reset_diff(self):
    106         """Reset diff results on policy changes."""
    107         self.log.debug("Resetting RBAC rule differences")
    108         self.added_role_allows = None
    109         self.removed_role_allows = None
    110         self.modified_role_allows = None
    111         self.added_role_transitions = None
    112         self.removed_role_transitions = None
    113         self.modified_role_transitions = None
    114 
    115         # Sets of rules for each policy
    116         self._left_rbac_rules.clear()
    117         self._right_rbac_rules.clear()
    118 
    119 
    120 class RoleAllowWrapper(Wrapper):
    121 
    122     """Wrap role allow rules to allow set operations."""
    123 
    124     def __init__(self, rule):
    125         self.origin = rule
    126         self.ruletype = rule.ruletype
    127         self.source = SymbolWrapper(rule.source)
    128         self.target = SymbolWrapper(rule.target)
    129         self.key = hash(rule)
    130 
    131     def __hash__(self):
    132         return self.key
    133 
    134     def __lt__(self, other):
    135         return self.key < other.key
    136 
    137     def __eq__(self, other):
    138         # because RBACRuleDifference groups rules by ruletype,
    139         # the ruletype always matches.
    140         return self.source == other.source and self.target == other.target
    141 
    142 
    143 class RoleTransitionWrapper(Wrapper):
    144 
    145     """Wrap role_transition rules to allow set operations."""
    146 
    147     def __init__(self, rule):
    148         self.origin = rule
    149         self.ruletype = rule.ruletype
    150         self.source = SymbolWrapper(rule.source)
    151         self.target = SymbolWrapper(rule.target)
    152         self.tclass = SymbolWrapper(rule.tclass)
    153         self.key = hash(rule)
    154 
    155     def __hash__(self):
    156         return self.key
    157 
    158     def __lt__(self, other):
    159         return self.key < other.key
    160 
    161     def __eq__(self, other):
    162         # because RBACRuleDifference groups rules by ruletype,
    163         # the ruletype always matches.
    164         return self.source == other.source and \
    165                self.target == other.target and \
    166                self.tclass == other.tclass
    167