[apparmor] [patch] [19/38] Add support for editing paths to FileRule

Christian Boltz apparmor at cboltz.de
Fri Aug 12 20:58:32 UTC 2016


Hello,

$subject.

This means adding
- self.can_edit - True if editing via '(N)ew' should be possible (will
  be False for bare file rules)
- edit_header() - returns the prompt text and the current path
- validate_edit() - checks if the new path matches the original one
- store_edit() - changes the path to the new one (even if it doesn't
  match the old one)

self.can_edit and the 3 functions are also added to BaseRule:
- can_edit is False by default
- the functions raise a NotImplementedError


Also add tests for the added code.


[ 19-add-support-for-editing-paths-to-FileRule.diff ]

=== modified file ./utils/apparmor/rule/file.py
--- utils/apparmor/rule/file.py	2016-03-28 19:08:25.211838730 +0200
+++ utils/apparmor/rule/file.py	2016-03-28 22:14:06.469847397 +0200
@@ -12,6 +12,7 @@
 #
 # ----------------------------------------------------------------------
 
+from apparmor.aare import AARE
 from apparmor.regex import RE_PROFILE_FILE_ENTRY, strip_quotes
 from apparmor.common import AppArmorBug, AppArmorException, type_is_str
 from apparmor.rule import BaseRule, BaseRuleset, check_and_split_list, logprof_value_or_all, parse_modifiers, quote_if_needed
@@ -66,6 +67,7 @@
 
         self.can_glob = not self.all_paths
         self.can_glob_ext = not self.all_paths
+        self.can_edit = not self.all_paths
 
         if type_is_str(perms):
             perms, tmp_exec_perms = split_perms(perms, deny)
@@ -339,6 +341,26 @@
         self.path = self.path.glob_path_withext()
         self.raw_rule = None
 
+    def edit_header(self):
+        if self.all_paths:
+            raise AppArmorBug('Attemp to edit bare file rule')
+
+        return(_('Enter new path: '), self.path.regex)
+
+    def validate_edit(self, newpath):
+        if self.all_paths:
+            raise AppArmorBug('Attemp to edit bare file rule')
+
+        newpath = AARE(newpath, True)  # might raise AppArmorException if the new path doesn't start with / or a variable
+        return newpath.match(self.path)
+
+    def store_edit(self, newpath):
+        if self.all_paths:
+            raise AppArmorBug('Attemp to edit bare file rule')
+
+        self.path = AARE(newpath, True)  # might raise AppArmorException if the new path doesn't start with / or a variable
+        self.raw_rule = None
+
 
 class FileRuleset(BaseRuleset):
     '''Class to handle and store a collection of file rules'''
=== modified file ./utils/apparmor/rule/__init__.py
--- utils/apparmor/rule/__init__.py	2016-03-28 19:08:25.211838730 +0200
+++ utils/apparmor/rule/__init__.py	2016-03-28 22:13:01.022202835 +0200
@@ -43,6 +43,9 @@
     can_glob = False
     can_glob_ext = False
 
+    # defines if the (N)ew option is displayed
+    can_edit = False
+
     def __init__(self, audit=False, deny=False, allow_keyword=False,
                  comment='', log_event=None):
         '''initialize variables needed by all rule types'''
@@ -273,6 +276,23 @@
            returns {'label1': 'value1', 'label2': 'value2'} '''
         raise NotImplementedError("'%s' needs to implement logprof_header(), but didn't" % (str(self)))
 
+    # @abstractmethod  FIXME - uncomment when python3 only
+    def edit_header(self):
+        '''return the prompt for, and the path to edit when using '(N)ew' '''
+        raise NotImplementedError("'%s' needs to implement edit_header(), but didn't" % (str(self)))
+
+    # @abstractmethod  FIXME - uncomment when python3 only
+    def validate_edit(self, newpath):
+        '''validate the new path.
+           Returns True if it covers the previous path, False if it doesn't.'''
+        raise NotImplementedError("'%s' needs to implement validate_edit(), but didn't" % (str(self)))
+
+    # @abstractmethod  FIXME - uncomment when python3 only
+    def store_edit(self, newpath):
+        '''store the changed path.
+           This is done even if the new path doesn't match the original one.'''
+        raise NotImplementedError("'%s' needs to implement store_edit(), but didn't" % (str(self)))
+
     def modifiers_str(self):
         '''return the allow/deny and audit keyword as string, including whitespace'''
 
=== modified file ./utils/test/test-baserule.py
--- utils/test/test-baserule.py	2016-03-28 19:08:25.195838799 +0200
+++ utils/test/test-baserule.py	2016-03-28 22:44:34.867670716 +0200
@@ -68,6 +68,21 @@
         with self.assertRaises(NotImplementedError):
             obj.logprof_header_localvars()
 
+    def test_edit_header_localvars(self):
+        obj = BaseRule()
+        with self.assertRaises(NotImplementedError):
+            obj.edit_header()
+
+    def test_validate_edit_localvars(self):
+        obj = BaseRule()
+        with self.assertRaises(NotImplementedError):
+            obj.validate_edit('/foo')
+
+    def test_store_edit_localvars(self):
+        obj = BaseRule()
+        with self.assertRaises(NotImplementedError):
+            obj.store_edit('/foo')
+
 
 setup_all_loops(__name__)
 if __name__ == '__main__':
=== modified file ./utils/test/test-file.py
--- utils/test/test-file.py	2016-03-28 19:08:25.211838730 +0200
+++ utils/test/test-file.py	2016-03-28 22:39:44.013280194 +0200
@@ -718,6 +718,63 @@
 #        obj = FileRule._parse(params)
 #        self.assertEqual(obj.logprof_header(), expected)
 
+class FileEditHeaderTest(AATest):
+    def _run_test(self, params, expected):
+        rule_obj = FileRule.parse(params)
+        self.assertEqual(rule_obj.can_edit, True)
+        prompt, path_to_edit = rule_obj.edit_header()
+        self.assertEqual(path_to_edit, expected)
+
+    tests = [
+        ('/foo/bar/baz r,',         '/foo/bar/baz'),
+        ('/foo/**/baz r,',          '/foo/**/baz'),
+    ]
+
+    def test_edit_header_bare_file(self):
+        rule_obj = FileRule.parse('file,')
+        self.assertEqual(rule_obj.can_edit, False)
+        with self.assertRaises(AppArmorBug):
+            rule_obj.edit_header()
+
+class FileValidateAndStoreEditTest(AATest):
+    def _run_test(self, params, expected):
+        rule_obj = FileRule('/foo/bar/baz', 'r', None, FileRule.ALL, False, False, False, log_event=True)
+
+        self.assertEqual(rule_obj.validate_edit(params), expected)
+
+        rule_obj.store_edit(params)
+        self.assertEqual(rule_obj.get_raw(), '%s r,' % params)
+
+    tests = [
+        # edited path           match
+        ('/foo/bar/baz',        True),
+        ('/foo/bar/*',          True),
+        ('/foo/bar/???',        True),
+        ('/foo/xy**',           False),
+        ('/foo/bar/baz/',       False),
+    ]
+
+    def test_validate_not_a_path(self):
+        rule_obj = FileRule.parse('/foo/bar/baz r,')
+
+        with self.assertRaises(AppArmorException):
+            rule_obj.validate_edit('foo/bar/baz')
+
+        with self.assertRaises(AppArmorException):
+            rule_obj.store_edit('foo/bar/baz')
+
+    def test_validate_edit_bare_file(self):
+        rule_obj = FileRule.parse('file,')
+        self.assertEqual(rule_obj.can_edit, False)
+
+        with self.assertRaises(AppArmorBug):
+            rule_obj.validate_edit('/foo/bar')
+
+        with self.assertRaises(AppArmorBug):
+            rule_obj.store_edit('/foo/bar')
+
+
+
 ## --- tests for FileRuleset --- #
 
 class FileRulesTest(AATest):



Regards,

Christian Boltz
-- 
For the simple cases, the results would be easier studied by cause and
effect, rather than code. For complicated cases, the code will be
unreadable. (And I say that as a friend. :)  [Seth Arnold in apparmor]
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: This is a digitally signed message part.
URL: <https://lists.ubuntu.com/archives/apparmor/attachments/20160812/0c16fffb/attachment-0001.pgp>


More information about the AppArmor mailing list