From d59444ef0e9c0d4f45b7c425371794e8c7d6ebd8 Mon Sep 17 00:00:00 2001 From: Chris PeBenito <cpebenito@tresys.com> Date: Fri, 8 Jan 2016 15:30:39 -0500 Subject: [PATCH] PolicyDifference: implement Booleans diff --- sediff | 23 +++++++++++++++ setools/diff/__init__.py | 4 ++- setools/diff/bool.py | 64 ++++++++++++++++++++++++++++++++++++++++ tests/diff.py | 29 ++++++++++++++++++ tests/diff_left.conf | 5 ++++ tests/diff_right.conf | 5 ++++ 6 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 setools/diff/bool.py diff --git a/sediff b/sediff index cf5e04c..c9ced6b 100755 --- a/sediff +++ b/sediff @@ -148,6 +148,29 @@ try: print(" - {0}".format(p)) print() + if all_differences or args.bool_: + if diff.added_booleans or diff.removed_booleans or \ + diff.modified_booleans or args.bool_: + print("Booleans ({0} Added, {1} Removed, {2} Modified)".format( + len(diff.added_booleans), len(diff.removed_booleans), + len(diff.modified_booleans))) + if diff.added_booleans and not args.stats: + print(" Added Booleans: {0}".format(len(diff.added_booleans))) + for a in sorted(diff.added_booleans): + print(" + {0}".format(a)) + if diff.removed_booleans and not args.stats: + print(" Removed Booleans: {0}".format(len(diff.removed_booleans))) + for a in sorted(diff.removed_booleans): + print(" - {0}".format(a)) + if diff.modified_booleans and not args.stats: + print(" Modified Booleans: {0}".format(len(diff.modified_booleans))) + for name, mod in sorted(diff.modified_booleans.items()): + print(" * {0} (Modified default state)".format(name)) + print(" + {0}".format(mod.added_state)) + print(" - {0}".format(mod.removed_state)) + + print() + if all_differences or args.role: if diff.added_roles or diff.removed_roles or diff.modified_roles or args.role: print("Roles ({0} Added, {1} Removed, {2} Modified)".format(len(diff.added_roles), diff --git a/setools/diff/__init__.py b/setools/diff/__init__.py index 3171f9e..703ac43 100644 --- a/setools/diff/__init__.py +++ b/setools/diff/__init__.py @@ -16,6 +16,7 @@ # License along with SETools. If not, see # <http://www.gnu.org/licenses/>. # +from .bool import BooleansDifference from .commons import CommonDifference from .mlsrules import MLSRulesDifference from .objclass import ObjClassDifference @@ -29,7 +30,8 @@ from .users import UsersDifference __all__ = ['PolicyDifference'] -class PolicyDifference(CommonDifference, +class PolicyDifference(BooleansDifference, + CommonDifference, MLSRulesDifference, ObjClassDifference, RBACRulesDifference, diff --git a/setools/diff/bool.py b/setools/diff/bool.py new file mode 100644 index 0000000..212a715 --- /dev/null +++ b/setools/diff/bool.py @@ -0,0 +1,64 @@ +# Copyright 2016, Tresys Technology, LLC +# +# This file is part of SETools. +# +# SETools is free software: you can redistribute it and/or modify +# it under the terms of the GNU Lesser General Public License as +# published by the Free Software Foundation, either version 2.1 of +# the License, or (at your option) any later version. +# +# SETools is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with SETools. If not, see +# <http://www.gnu.org/licenses/>. +# +from collections import namedtuple + +from .descriptors import DiffResultDescriptor +from .difference import Difference, SymbolWrapper + + +modified_bool_record = namedtuple("modified_boolean", ["added_state", "removed_state"]) + + +class BooleansDifference(Difference): + + """Determine the difference in type attributes between two policies.""" + + added_booleans = DiffResultDescriptor("diff_booleans") + removed_booleans = DiffResultDescriptor("diff_booleans") + modified_booleans = DiffResultDescriptor("diff_booleans") + + def diff_booleans(self): + """Generate the difference in type attributes between the policies.""" + + self.log.info("Generating Boolean differences from {0.left_policy} to {0.right_policy}". + format(self)) + + self.added_booleans, self.removed_booleans, matched_booleans = \ + self._set_diff( + (SymbolWrapper(b) for b in self.left_policy.bools()), + (SymbolWrapper(b) for b in self.right_policy.bools())) + + self.modified_booleans = dict() + + for left_boolean, right_boolean in matched_booleans: + # Criteria for modified booleans + # 1. change to default state + if left_boolean.state != right_boolean.state: + self.modified_booleans[left_boolean] = modified_bool_record(right_boolean.state, + left_boolean.state) + + # + # Internal functions + # + def _reset_diff(self): + """Reset diff results on policy changes.""" + self.log.debug("Resetting Boolean differences") + self.added_booleans = None + self.removed_booleans = None + self.modified_booleans = None diff --git a/tests/diff.py b/tests/diff.py index 69a65b0..2d94d91 100644 --- a/tests/diff.py +++ b/tests/diff.py @@ -910,6 +910,23 @@ class PolicyDifferenceTest(ValidateRule, unittest.TestCase): self.assertSetEqual(set(["modified_remove_attr"]), self.diff.modified_type_attributes["an_attr"].removed_types) + # + # Booleans + # + def test_added_boolean(self): + """Diff: added boolean.""" + self.assertSetEqual(set(["added_bool"]), self.diff.added_booleans) + + def test_removed_boolean(self): + """Diff: removed boolean.""" + self.assertSetEqual(set(["removed_bool"]), self.diff.removed_booleans) + + def test_modified_boolean(self): + """Diff: modified boolean.""" + self.assertEqual(1, len(self.diff.modified_booleans)) + self.assertTrue(self.diff.modified_booleans["modified_bool"].added_state) + self.assertFalse(self.diff.modified_booleans["modified_bool"].removed_state) + class PolicyDifferenceTestNoDiff(unittest.TestCase): @@ -1110,3 +1127,15 @@ class PolicyDifferenceTestNoDiff(unittest.TestCase): def test_modified_type_attributes(self): """NoDiff: no modified type attribute rules.""" self.assertFalse(self.diff.modified_type_attributes) + + def test_added_booleans(self): + """NoDiff: no added booleans.""" + self.assertFalse(self.diff.added_booleans) + + def test_removed_booleans(self): + """NoDiff: no removed booleans.""" + self.assertFalse(self.diff.removed_booleans) + + def test_modified_booleans(self): + """NoDiff: no modified booleans.""" + self.assertFalse(self.diff.modified_booleans) diff --git a/tests/diff_left.conf b/tests/diff_left.conf index d4b9693..8a2ec7d 100644 --- a/tests/diff_left.conf +++ b/tests/diff_left.conf @@ -161,6 +161,11 @@ role modified_add_type; role modified_remove_type; role modified_remove_type types { system }; +# booleans +bool same_bool true; +bool removed_bool true; +bool modified_bool false; + # Allow rule differences type matched_source; type matched_target; diff --git a/tests/diff_right.conf b/tests/diff_right.conf index dae33ca..5bf073c 100644 --- a/tests/diff_right.conf +++ b/tests/diff_right.conf @@ -162,6 +162,11 @@ role modified_add_type types { system }; role modified_remove_type; +# booleans +bool same_bool true; +bool added_bool true; +bool modified_bool true; + # Allow rule differences type matched_source; type matched_target;