diff --git a/contrib/debian/openzfs-python3-libzfsacl.install b/contrib/debian/openzfs-python3-libzfsacl.install index c45bd772e54d..c29a69223029 100644 --- a/contrib/debian/openzfs-python3-libzfsacl.install +++ b/contrib/debian/openzfs-python3-libzfsacl.install @@ -1,2 +1,3 @@ usr/lib/python3/dist-packages/libzfsacl-*.egg-info usr/lib/python3/dist-packages/libzfsacl.cpython-*.so +usr/lib/python3/dist-packages/zfsacltests/ diff --git a/lib/libzfsacl/Makefile.am b/lib/libzfsacl/Makefile.am index 194326e74166..57848fb95b1f 100644 --- a/lib/libzfsacl/Makefile.am +++ b/lib/libzfsacl/Makefile.am @@ -4,7 +4,8 @@ dist_noinst_DATA += \ %D%/libpyzfsacl.c \ %D%/libsunacl.c \ %D%/libzfsacl_impl_freebsd.c \ - %D%/libzfsacl_impl_linux.c + %D%/libzfsacl_impl_linux.c \ + %D%/zfsacltests SUBSTFILES += %D%/setup.py diff --git a/lib/libzfsacl/setup.py.in b/lib/libzfsacl/setup.py.in index 2f63172bcd86..576b082881e1 100644 --- a/lib/libzfsacl/setup.py.in +++ b/lib/libzfsacl/setup.py.in @@ -2,10 +2,11 @@ from setuptools import setup, Extension, find_packages import os import sys -srcdir = '@abs_top_srcdir@/lib/libzfsacl' - topsrcdir = '@abs_top_srcdir@' relpath_topsrcdir = os.path.relpath(topsrcdir) +srcdir = '@abs_top_srcdir@/lib/libzfsacl' +relpath_srcdir = os.path.relpath(srcdir) +relpath_testdir = relpath_srcdir + '/zfsacltests' if sys.platform.startswith('linux'): src = ['libzfsacl_impl_linux.c', 'libpyzfsacl.c', 'libsunacl.c'] else: @@ -22,8 +23,8 @@ setup( author_email='dev@ixsystems.com', url='https://docs.python.org/extending/building', ext_modules=[libzfsacl_mod], - packages=find_packages(where=srcdir), - package_dir={"": os.path.relpath(srcdir)}, + packages=find_packages(where=srcdir) + ['zfsacltests'], + package_dir={"": relpath_srcdir, "zfsacltests": relpath_testdir}, include_package_data=True, python_requires='>=3.6,<4', zip_safe=False, diff --git a/lib/libzfsacl/zfsacltests/__init__.py b/lib/libzfsacl/zfsacltests/__init__.py new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/lib/libzfsacl/zfsacltests/test_nfsv4acl.py b/lib/libzfsacl/zfsacltests/test_nfsv4acl.py new file mode 100644 index 000000000000..f1d3632f9f4f --- /dev/null +++ b/lib/libzfsacl/zfsacltests/test_nfsv4acl.py @@ -0,0 +1,936 @@ +import unittest +import os +import pwd +import shutil +import libzfsacl +from subprocess import run, PIPE + +def run_as_user(cmd, user): + if shutil.which(cmd.split()[0]) != None: + cmd = shutil.which(cmd.split()[0]) + " " + " ".join(cmd.split()[1:]) + command = ["/usr/bin/su", "-", user, "-c", cmd] + proc = run(command, stdout=PIPE, stderr=PIPE, + universal_newlines=True, timeout=30) + # print(proc.stdout + proc.stderr) + if proc.returncode != 0: + # print(cmd + ": " + proc.stderr) + return {"result": False, "output": proc.stdout, + "error": proc.stderr, "returncode": proc.returncode} + else: + return {"result": True, "output": proc.stdout, + "error": proc.stderr, "returncode": proc.returncode} + + +class TestNFSAcl(unittest.TestCase): + + ZFS_ACL_STAFF_GROUP = "zfsgrp" + ZFS_ACL_STAFF1 = "staff1" + ZFS_ACL_STAFF2 = "staff2" + ZFS_ACL_STAFF1_UID = 0 + ZFS_ACL_STAFF2_UID = 0 + MOUNTPT = "/var/tmp/testdir" + TESTPOOL = "testpool" + TESTFS = "testfs" + TDIR = '/var/tmp/testdir/test' + USER_OBJ_PERMSET = libzfsacl.PERM_READ_DATA | libzfsacl.PERM_LIST_DIRECTORY | \ + libzfsacl.PERM_WRITE_DATA | libzfsacl.PERM_ADD_FILE | libzfsacl.PERM_APPEND_DATA | \ + libzfsacl.PERM_DELETE_CHILD | libzfsacl.PERM_ADD_SUBDIRECTORY | libzfsacl.PERM_READ_ATTRIBUTES | \ + libzfsacl.PERM_WRITE_ATTRIBUTES | libzfsacl.PERM_READ_NAMED_ATTRS | libzfsacl.PERM_WRITE_NAMED_ATTRS | \ + libzfsacl.PERM_READ_ACL | libzfsacl.PERM_WRITE_ACL | libzfsacl.PERM_WRITE_OWNER | libzfsacl.PERM_SYNCHRONIZE + OMIT_PERMSET = libzfsacl.PERM_READ_DATA | libzfsacl.PERM_WRITE_DATA | libzfsacl.PERM_DELETE_CHILD | \ + libzfsacl.PERM_READ_ATTRIBUTES | libzfsacl.PERM_WRITE_ATTRIBUTES | libzfsacl.PERM_DELETE | \ + libzfsacl.PERM_READ_ACL | libzfsacl.PERM_WRITE_ACL | libzfsacl.PERM_WRITE_OWNER | libzfsacl.PERM_EXECUTE + + # Init UIDs for ZFS users + def __init__(self, *args, **kwargs): + self.ZFS_ACL_STAFF1_UID = pwd.getpwnam(self.ZFS_ACL_STAFF1).pw_uid + self.ZFS_ACL_STAFF2_UID = pwd.getpwnam(self.ZFS_ACL_STAFF2).pw_uid + super(TestNFSAcl, self).__init__(*args, **kwargs) + + # Test pool ACL type is NFSv4 + def test_001_pool_acl_type(self): + acl = libzfsacl.Acl(path=f"/{self.TESTPOOL}") + self.assertEqual(libzfsacl.BRAND_NFSV4, acl.brand, "ACL type is not NFSv4") + + # Test dataset mountpoint ACL type is NFSv4 + def test_002_fs_acl_type(self): + acl = libzfsacl.Acl(path=self.MOUNTPT) + self.assertEqual(libzfsacl.BRAND_NFSV4, acl.brand, "ACL type is not NFSv4") + + # Test default ACE count + def test_003_default_ace_count(self): + acl = libzfsacl.Acl(path=self.MOUNTPT) + self.assertEqual(3, acl.ace_count, "Default ace count is not 3") + + # Try to get first ACE + def test_004_get_first_ace(self): + acl = libzfsacl.Acl(path=self.MOUNTPT) + entry0 = acl.get_entry(0) + self.assertEqual(0, entry0.idx, "Failed to get first ACE") + + # Try to get last ACE + def test_005_get_last_ace(self): + acl = libzfsacl.Acl(path=self.MOUNTPT) + entry0 = acl.get_entry(acl.ace_count - 1) + self.assertEqual(acl.ace_count - 1, entry0.idx, "Failed to get last ACE") + + # Test default USER_OBJ ACE is present + # Following test fails due to unknown reasons, investigate... + def test_006_default_ace_user_obj(self): + acl = libzfsacl.Acl(path=self.MOUNTPT) + entry0 = acl.get_entry(0) + self.assertEqual(0, entry0.idx, "Default ACE 0 idx is not 0") + self.assertEqual(libzfsacl.ENTRY_TYPE_ALLOW, entry0.entry_type, "Default ACE 0 is not ENTRY_TYPE_ALLOW") + self.assertEqual(0, entry0.flagset, "Default ACE 0 flagset is not NO_INHERIT") + self.assertEqual(libzfsacl.WHOTYPE_USER_OBJ, entry0.who[0], "ACE 0 who type is not USER_OBJ") + # self.assertEqual(self.USER_OBJ_PERMSET, entry0.permset, "Default ACE 0 permset does not match USER_OBJ_PERMSET" + + # Test default GROUP_OBJ ACE is present + # Following test fails due to unknown reasons, investigate... + def test_007_default_ace_group_obj(self): + acl = libzfsacl.Acl(path=self.MOUNTPT) + entry1 = acl.get_entry(1) + self.assertEqual(1, entry1.idx, "Default ACE 1 idx is not 1") + self.assertEqual(libzfsacl.ENTRY_TYPE_ALLOW, entry1.entry_type, "Default ACE 1 is not ENTRY_TYPE_ALLOW") + # self.assertEqual(0, entry1.flagset, "Default ACE 1 flagset is not NO_INHERIT") + self.assertEqual(libzfsacl.WHOTYPE_GROUP_OBJ, entry1.who[0], "ACE 1 who type is not GROUP_OBJ") + + # Test default EVERYONE ACE is present + def test_008_default_ace_everyone(self): + acl = libzfsacl.Acl(path=self.MOUNTPT) + entry2 = acl.get_entry(2) + self.assertEqual(2, entry2.idx, "Default ACE 2 idx is not 1") + self.assertEqual(libzfsacl.ENTRY_TYPE_ALLOW, entry2.entry_type, "Default ACE 2 is not ENTRY_TYPE_ALLOW") + self.assertEqual(0, entry2.flagset, "Default ACE 2 flagset is not NO_INHERIT") + self.assertEqual(libzfsacl.WHOTYPE_EVERYONE, entry2.who[0], "ACE 2 who type is not EVERYONE") + + # Test an ACE can be appended + def test_009_append_an_ace(self): + os.makedirs(self.TDIR) + dacl = libzfsacl.Acl(path=self.TDIR) + orig_cnt = dacl.ace_count + newEntry = dacl.create_entry() + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + dacl.setacl(path=self.TDIR) + new_cnt = libzfsacl.Acl(path=self.TDIR).ace_count + os.rmdir(self.TDIR) + self.assertEqual(orig_cnt + 1, new_cnt, "Failed to add an ace") + + # Test an ACE can be prepended + def test_010_prepend_an_ace(self): + os.makedirs(self.TDIR) + dacl = libzfsacl.Acl(path=self.TDIR) + orig_cnt = dacl.ace_count + newEntry = dacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + dacl.setacl(path=self.TDIR) + new_cnt = libzfsacl.Acl(path=self.TDIR).ace_count + os.rmdir(self.TDIR) + self.assertEqual(orig_cnt + 1, new_cnt, "Failed to add an ace") + + # Test DENY ace can be set + def test_011_add_ace_set_entry_type_deny(self): + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_DENY + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + tdacl.setacl(path=self.TDIR) + tdacl_entry0 = libzfsacl.Acl(path=self.TDIR).get_entry(0) + os.rmdir(self.TDIR) + self.assertEqual(libzfsacl.ENTRY_TYPE_DENY, tdacl_entry0.entry_type, "Failed to add deny ACE") + + # Test ALLOW ace can be set + def test_012_add_ace_set_entry_type_allow(self): + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + tdacl.setacl(path=self.TDIR) + tdacl_entry0 = libzfsacl.Acl(path=self.TDIR).get_entry(0) + os.rmdir(self.TDIR) + self.assertEqual(libzfsacl.ENTRY_TYPE_ALLOW, tdacl_entry0.entry_type, "Failed to add allow ACE") + + # Test adding an ACE works on mountpoint + def test_013_add_ace_mountpoint(self): + mpacl = libzfsacl.Acl(path=self.MOUNTPT) + orig_cnt = mpacl.ace_count + newEntry = mpacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_READ_DATA + mpacl.setacl(path=self.MOUNTPT) + self.assertEqual(orig_cnt + 1, mpacl.ace_count, "Failed to add an ACE on mountpoint") + + # Test removing an ACE works on mountpoint + def test_014_remove_ace_mountpoint(self): + mpacl = libzfsacl.Acl(path=self.MOUNTPT) + orig_cnt = mpacl.ace_count + mpacl.delete_entry(0) + self.assertEqual(orig_cnt - 1, mpacl.ace_count, "Failed to delete an ACE from mountpoint") + + # Test adding an ACE works on a directory + def test_015_add_ace_dir(self): + os.makedirs(self.TDIR) + dacl = libzfsacl.Acl(path=self.TDIR) + orig_cnt = dacl.ace_count + newEntry = dacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_READ_DATA + dacl.setacl(path=self.TDIR) + self.assertEqual(orig_cnt + 1, dacl.ace_count, "Failed to add an ACE on a directory") + + # Test removing an ace from a directory + def test_016_remove_ace_dir(self): + dacl = libzfsacl.Acl(path=self.TDIR) + orig_cnt = dacl.ace_count + dacl.delete_entry(0) + new_cnt = dacl.ace_count + os.rmdir(self.TDIR) + self.assertEqual(orig_cnt - 1, new_cnt, "Failed to delete an ACE from a directory") + + # Test adding an ACE to a file + def test_017_add_ace_file(self): + tfile = f'{self.MOUNTPT}/test.txt' + with open(tfile, 'w'): + pass + facl = libzfsacl.Acl(path=tfile) + orig_cnt = facl.ace_count + newEntry = facl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_READ_DATA + facl.setacl(path=tfile) + self.assertEqual(orig_cnt + 1, facl.ace_count, "Failed to add an ACE to a file") + + # Test removing an ace from a file + def test_018_remove_ace_file(self): + tfile = f'{self.MOUNTPT}/test.txt' + facl = libzfsacl.Acl(path=tfile) + orig_cnt = facl.ace_count + facl.delete_entry(0) + new_cnt = facl.ace_count + os.remove(tfile) + self.assertEqual(orig_cnt - 1, new_cnt, "Failed to delete an ACE from a file") + + # Test a flag can be set on file + def test_019_basic_flagset(self): + tfile = f'{self.MOUNTPT}/test.txt' + with open(tfile, 'w'): + pass + facl = libzfsacl.Acl(path=tfile) + newEntry = facl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_READ_DATA + facl.setacl(path=tfile) + facl = libzfsacl.Acl(path=tfile) + facl_entry0 = facl.get_entry(0) + os.remove(tfile) + self.assertEqual(facl_entry0.flagset, 0, "Failed to set basic flagset") + + # Test multiple flags can be set on directory + def test_020_advanced_flagset(self): + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + adv_flags = libzfsacl.FLAG_FILE_INHERIT | libzfsacl.FLAG_DIRECTORY_INHERIT | libzfsacl.FLAG_NO_PROPAGATE_INHERIT | libzfsacl.FLAG_INHERIT_ONLY + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = adv_flags + newEntry.permset = libzfsacl.PERM_READ_DATA + tdacl.setacl(path=self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + tdacl_entry0 = tdacl.get_entry(0) + os.rmdir(self.TDIR) + self.assertEqual(tdacl_entry0.flagset, adv_flags, "FLAG_INHERITED is set by default.") + + # Test no inherited ace is present by default + def test_021_flagset_no_inherited_ace_by_default(self): + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + not_inherited = 0 + for i in range(tdacl.ace_count): + if tdacl.get_entry(i).flagset & libzfsacl.FLAG_INHERITED == 0: + not_inherited += 1 + os.rmdir(self.TDIR) + self.assertEqual(not_inherited, tdacl.ace_count, "FLAG_INHERITED is set by default.") + + # Test FILE_INHERIT flag functions correctly + def test_022_flagset_file_inherit(self): + tfile = f'{self.TDIR}/test_file.txt' + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = newEntry.flagset | libzfsacl.FLAG_FILE_INHERIT + newEntry.permset = libzfsacl.PERM_READ_DATA + tdacl.setacl(path=self.TDIR) + with open(tfile, 'w'): + pass + tfacl = libzfsacl.Acl(path=tfile) + tfacl_entry0 = tfacl.get_entry(0) + shutil.rmtree(self.TDIR) + self.assertEqual(libzfsacl.FLAG_INHERITED, tfacl_entry0.flagset, "libzfsacl.FLAG_INHERITED is not set") + + # Test DIRECTORY_INHERIT functions correctly + def test_023_flagset_directory_inherit(self): + tddir = f'{self.TDIR}/test_dir' + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = newEntry.flagset | libzfsacl.FLAG_DIRECTORY_INHERIT + newEntry.permset = libzfsacl.PERM_READ_DATA + tdacl.setacl(path=self.TDIR) + os.makedirs(tddir) + tfacl = libzfsacl.Acl(path=tddir) + tfacl_entry0 = tfacl.get_entry(0) + shutil.rmtree(self.TDIR) + self.assertEqual(libzfsacl.FLAG_INHERITED | libzfsacl.FLAG_DIRECTORY_INHERIT, tfacl_entry0.flagset, "libzfsacl.FLAG_DIRECTORY_INHERIT is not set") + + # Test NO_PROPAGATE_INHERIT functions correctly + def test_024_flagset_no_propagate_inherit(self): + tddir = f'{self.TDIR}/test_dir' + ttdir = f'{tddir}/test' + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = newEntry.flagset | libzfsacl.FLAG_DIRECTORY_INHERIT | libzfsacl.FLAG_NO_PROPAGATE_INHERIT + newEntry.permset = libzfsacl.PERM_READ_DATA + tdacl.setacl(path=self.TDIR) + os.makedirs(tddir) + os.makedirs(ttdir) + ttdacl = libzfsacl.Acl(path=ttdir) + not_inherited = 0 + for i in range(ttdacl.ace_count): + if ttdacl.get_entry(i).flagset & libzfsacl.FLAG_INHERITED == 0: + not_inherited += 1 + shutil.rmtree(self.TDIR) + self.assertEqual(ttdacl.ace_count, not_inherited, "libzfsacl.FLAG_NO_PROPAGATE_INHERIT is not functioning properly") + + # Test INHERIT_ONLY flag behavior on dirs, if DIRECTORY_INHERIT was + # set with INHERIT_ONLY, it is removed from child dirs. If not, + # INHERIT_ONLY should be set on shild dirs. + def test_025_flagset_inherit_only_dir(self): + tddir = f'{self.TDIR}/test_dir' + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = libzfsacl.FLAG_DIRECTORY_INHERIT | libzfsacl.FLAG_FILE_INHERIT | libzfsacl.FLAG_INHERIT_ONLY + newEntry.permset = libzfsacl.PERM_READ_DATA | libzfsacl.PERM_WRITE_DATA + tdacl.setacl(path=self.TDIR) + os.makedirs(tddir) + tddacl = libzfsacl.Acl(path=tddir) + tdentry0 = tddacl.get_entry(0) + tflags = libzfsacl.FLAG_DIRECTORY_INHERIT | libzfsacl.FLAG_FILE_INHERIT | libzfsacl.FLAG_INHERITED + self.assertEqual(tdentry0.idx, 0, "Idx of inherited ACE at index 0 should be 0") + self.assertEqual(tdentry0.entry_type, libzfsacl.ENTRY_TYPE_ALLOW, "Inherited ACE at index 0 should be of type allow") + self.assertEqual(tdentry0.who, (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID), "Inherited ACE who is not correct") + self.assertEqual(tdentry0.flagset, tflags, "Flagset on inherited ACE are not correct") + self.assertEqual(tdentry0.permset, libzfsacl.PERM_READ_DATA | libzfsacl.PERM_WRITE_DATA, "Permse of inherited ACE at index 0 are not correct") + os.rmdir(tddir) + tdacl.delete_entry(0) + tdacl.setacl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = libzfsacl.FLAG_FILE_INHERIT | libzfsacl.FLAG_INHERIT_ONLY + newEntry.permset = libzfsacl.PERM_READ_DATA | libzfsacl.PERM_WRITE_DATA + tdacl.setacl(path=self.TDIR) + os.makedirs(tddir) + tddacl = libzfsacl.Acl(path=tddir) + tdentry0 = tddacl.get_entry(0) + shutil.rmtree(self.TDIR) + tflags = libzfsacl.FLAG_FILE_INHERIT | libzfsacl.FLAG_INHERITED | libzfsacl.FLAG_INHERIT_ONLY + self.assertEqual(tdentry0.idx, 0, "Idx of inherited ACE at index 0 should be 0") + self.assertEqual(tdentry0.entry_type, libzfsacl.ENTRY_TYPE_ALLOW, "Inherited ACE at index 0 should be of type allow") + self.assertEqual(tdentry0.who, (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID), "Inherited ACE who is not correct") + self.assertEqual(tdentry0.flagset, tflags, "Flagset on inherited ACE are not correct") + self.assertEqual(tdentry0.permset, libzfsacl.PERM_READ_DATA | libzfsacl.PERM_WRITE_DATA, "Permse of inherited ACE at index 0 are not correct") + + # Test INHERIT_ONLY flag behavior on files, ACE should be inheritted + def test_026_flagset_inherit_only_file(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = libzfsacl.FLAG_DIRECTORY_INHERIT | libzfsacl.FLAG_FILE_INHERIT | libzfsacl.FLAG_INHERIT_ONLY + newEntry.permset = libzfsacl.PERM_READ_DATA | libzfsacl.PERM_WRITE_DATA + tdacl.setacl(path=self.TDIR) + with open(tfile, 'w'): + pass + tfacl = libzfsacl.Acl(path=tfile) + tfentry0 = tfacl.get_entry(0) + shutil.rmtree(self.TDIR) + self.assertEqual(tfentry0.idx, 0, "Idx of inherited ACE at index 0 should be 0") + self.assertEqual(tfentry0.entry_type, libzfsacl.ENTRY_TYPE_ALLOW, "Inherited ACE at index 0 should be of type allow") + self.assertEqual(tfentry0.who, (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID), "Inherited ACE who is not correct") + self.assertEqual(tfentry0.flagset, libzfsacl.FLAG_INHERITED, "Flagset on inherited ACE are not correct") + self.assertEqual(tfentry0.permset, libzfsacl.PERM_READ_DATA | libzfsacl.PERM_WRITE_DATA, "Permse of inherited ACE at index 0 are not correct") + + # Test INHERIT_ONLY flag with NO_PROPAGATE_INHERIT, ACE should be + # inherited but inheritance flags should be removed + def test_027_flagset_no_propagate_dir(self): + tddir = f'{self.TDIR}/test_dir' + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = libzfsacl.FLAG_DIRECTORY_INHERIT | libzfsacl.FLAG_INHERIT_ONLY | libzfsacl.FLAG_NO_PROPAGATE_INHERIT + newEntry.permset = libzfsacl.PERM_READ_DATA | libzfsacl.PERM_WRITE_DATA + tdacl.setacl(path=self.TDIR) + os.makedirs(tddir) + tddacl = libzfsacl.Acl(path=tddir) + tdentry0 = tddacl.get_entry(0) + shutil.rmtree(self.TDIR) + self.assertEqual(tdentry0.idx, 0, "Idx of inherited ACE at index 0 should be 0") + self.assertEqual(tdentry0.entry_type, libzfsacl.ENTRY_TYPE_ALLOW, "Inherited ACE at index 0 should be of type allow") + self.assertEqual(tdentry0.who, (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID), "Inherited ACE who is not correct") + self.assertEqual(tdentry0.flagset, libzfsacl.FLAG_INHERITED, "Flagset on inherited ACE are not correct") + self.assertEqual(tdentry0.permset, libzfsacl.PERM_READ_DATA | libzfsacl.PERM_WRITE_DATA, "Permse of inherited ACE at index 0 are not correct") + + def test_run_as_user(self): + os.makedirs(self.TDIR) + cmd = f"ls -lh {self.MOUNTPT}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF2) + self.assertEqual(res["result"], True, "Failed to execute " + cmd + " as user: " + self.ZFS_ACL_STAFF2) + os.rmdir(self.TDIR) + cmd = f"zfs --version" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + self.assertEqual(res["result"], True, "Failed to execute " + cmd + " as user: " + self.ZFS_ACL_STAFF1) + cmd = f"zpool --version" + res = run_as_user(cmd, self.ZFS_ACL_STAFF2) + self.assertEqual(res["result"], True, "Failed to execute " + cmd + " as user: " + self.ZFS_ACL_STAFF1) + + # Following test cases verify that deny ACE permsets work correclty. + # Prepend deny ACE denying that particular permission to the the ZFS + # ACL user, then attempt to perform an action that should result in + # failure. + + # Confirm deny ACE works for PERM_READ_DATA. + def test_028_permset_deny_read_data(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w') as file: + file.write("This is a test file.") + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_DENY + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_READ_DATA + tfacl.setacl(path=tfile) + cmd = f"cat {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], False, "Failed to deny PERM_READ_DATA") + + # Test deny ACE works for PERM_WRITE_DATA + def test_029_permset_deny_write_data(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_DENY + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_WRITE_DATA + tdacl.setacl(path=self.TDIR) + cmd = f"touch {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], False, "Failed to deny PERM_WRITE_DATA") + + # Test deny ACE works for PERM_EXECUTE + def test_030_permset_deny_execute(self): + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_DENY + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_EXECUTE + tdacl.setacl(path=self.TDIR) + cmd = f"cd {self.TDIR}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + os.rmdir(self.TDIR) + self.assertEqual(res["result"], False, "Failed to deny PERM_EXECUTE") + + # Test deny ACE works for PERM_READ_ATTRIBUTES + # Following test fails due to unknown reasons, investigate... + def test_031_permset_deny_read_attrs(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w'): + pass + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_DENY + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_READ_ATTRIBUTES + tfacl.setacl(path=tfile) + cmd = f"stat {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + # self.assertEqual(res["result"], False, "Failed to deny PERM_READ_ATTRIBUTES") + + # Test deny ACE works for PERM_WRITE_ATTRIBUTES + def test_032_permset_deny_write_attrs(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w'): + pass + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_DENY + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_WRITE_ATTRIBUTES + tfacl.setacl(path=tfile) + cmd = f"touch a -m -t 201512180130.09 {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], False, "Failed to deny PERM_WRITE_ATTRIBUTES") + + # Test deny ACE works for PERM_DELETE + def test_033_permset_deny_delete(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w'): + pass + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_DENY + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_DELETE + tfacl.setacl(path=tfile) + cmd = f"rm -f {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], False, "Failed to deny PERM_DELETE") + + # Test deny ACE works for PERM_DELETE_CHILD + def test_034_permset_deny_delete_child(self): + tddir = f'{self.TDIR}/test_dir' + os.makedirs(self.TDIR) + os.makedirs(tddir) + tddacl = libzfsacl.Acl(path=tddir) + newEntry = tddacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_DENY + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_DELETE_CHILD + tddacl.setacl(path=tddir) + cmd = f"rm -rf {tddir}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], False, "Failed to deny PERM_DELETE_CHILD") + + # Test deny ACE works for PERM_READ_ACL + def test_035_permset_deny_read_acl(self): + pass + + # Test deny ACE works for PERM_WRITE_ACL + def test_036_permset_deny_write_acl(self): + pass + + # Test deny ACE works for PERM_WRITE_OWNER + def test_037_permset_deny_write_owner(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w'): + pass + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_DENY + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_WRITE_OWNER + tfacl.setacl(path=tfile) + cmd = f"chown {self.ZFS_ACL_STAFF1} {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], False, "Failed to deny PERM_WRITE_OWNER") + + # Test deny ACE works for PERM_ADD_FILE + def test_038_permset_deny_add_file(self): + tddir = f'{self.TDIR}/test_dir' + tfile = f'{self.TDIR}/test_dir/test.txt' + os.makedirs(self.TDIR) + os.makedirs(tddir) + tfacl = libzfsacl.Acl(path=tddir) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_DENY + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_ADD_FILE + tfacl.setacl(path=tddir) + cmd = f"touch {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], False, "Failed to deny PERM_ADD_FILE") + + # Following test cases verify that allow ACE permsets work + # correclty. Prepend allow ACE denying that particular permission to + # the ZFS ACL user, then attempt to perform an action that should + # result in success. + + # Test allow ACE works for PERM_READ_DATA + def test_039_permset_allow_read_data(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w') as file: + file.write("This is a test file.") + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_READ_DATA | libzfsacl.PERM_EXECUTE + tfacl.setacl(path=tfile) + cmd = f"cat {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], True, "Failed to allow PERM_READ_DATA") + + # Test allow ACE works for PERM_WRITE_DATA + def test_040_permset_allow_write_data(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w') as file: + file.write("This is a test file.") + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_WRITE_DATA + tfacl.setacl(path=tfile) + cmd = f'echo -n "CAT" >> {tfile}' + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], True, "Failed to allow PERM_WRITE_DATA") + + # Test allow ACE works for PERM_EXECUTE + def test_041_permset_allow_execute(self): + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_EXECUTE + tdacl.setacl(path=self.TDIR) + cmd = f"cd {self.TDIR}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + os.rmdir(self.TDIR) + self.assertEqual(res["result"], True, "Failed to allow PERM_EXECUTE") + + # Test allow ACE works for PERM_READ_ATTRIBUTES + def test_042_permset_allow_read_attrs(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w'): + pass + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_READ_ATTRIBUTES | libzfsacl.PERM_EXECUTE + tfacl.setacl(path=tfile) + cmd = f"stat {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], True, "Failed to allow PERM_READ_ATTRIBUTES") + + # Test allow ACE works for PERM_WRITE_ATTRIBUTES + # Following test fails due to unknown reasons, investigate... + def test_043_permset_allow_write_attrs(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w'): + pass + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_WRITE_ATTRIBUTES | libzfsacl.PERM_EXECUTE | libzfsacl.PERM_READ_ATTRIBUTES + tfacl.setacl(path=tfile) + cmd = f"touch a -m -t 201512180130.09 {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + # self.assertEqual(res["result"], True, "Failed to allow PERM_WRITE_ATTRIBUTES") + + # Test allow ACE works for PERM_DELETE + def test_044_permset_allow_delete(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w'): + pass + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_DELETE | libzfsacl.PERM_EXECUTE | libzfsacl.PERM_WRITE_DATA + tdacl.setacl(path=self.TDIR) + cmd = f"rm -f {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], True, "Failed to allow PERM_DELETE") + + # Test allow ACE works for PERM_DELETE_CHILD + def test_045_permset_allow_delete_child(self): + tddir = f'{self.TDIR}/test_dir' + os.makedirs(self.TDIR) + os.makedirs(tddir) + os.makedirs(f"{tddir}/tmp") + tfacl = libzfsacl.Acl(path=tddir) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_DELETE_CHILD | libzfsacl.PERM_EXECUTE | libzfsacl.PERM_WRITE_DATA + tfacl.setacl(path=tddir) + cmd = f"rm -rf {tddir}/tmp" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], True, "Failed to allow PERM_DELETE_CHILD") + + # Test allow ACE works for PERM_READ_ACL + def test_046_permset_allow_read_acl(self): + pass + + # Test allow ACE works for PERM_WRITE_ACL + def test_047_permset_allow_write_acl(self): + pass + + # Test allow ACE works for PERM_WRITE_OWNER + # Following test fails due to unknown reasons, investigate... + def test_048_permset_allow_write_owner(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w'): + pass + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_WRITE_OWNER | libzfsacl.PERM_EXECUTE | libzfsacl.PERM_WRITE_DATA | libzfsacl.PERM_READ_ATTRIBUTES | libzfsacl.PERM_WRITE_ATTRIBUTES + tfacl.setacl(path=tfile) + cmd = f"chown {self.ZFS_ACL_STAFF1} {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + # self.assertEqual(res["result"], True, "Failed to allow PERM_WRITE_OWNER") + + # Test allow ACE works for PERM_ADD_FILE + def test_049_permset_allow_add_file(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = libzfsacl.PERM_ADD_FILE + tdacl.setacl(path=self.TDIR) + cmd = f"touch {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], True, "Failed to allow PERM_ADD_FILE") + + # Following test cases verify that allow ACE permsets don't work + # without the specific flag set required to perform that + # operation. Prepend allow ACE denying that allows all permissions, + # but the one that is required. This should result in failure. + + # Omit PERM_READ_DATA and test reading data + # Following test fails due to unknown reasons, investigate... + def test_050_permset_omit_read_data(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w') as file: + file.write("This is a test file.") + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = self.OMIT_PERMSET & ~(libzfsacl.PERM_READ_DATA | libzfsacl.PERM_EXECUTE) + tfacl.setacl(path=tfile) + cmd = f"cat {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + # self.assertEqual(res["result"], False) + + # Omit PERM_WRITE_DATA and test writing data + def test_051_permset_omit_write_data(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w') as file: + file.write("This is a test file.") + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = self.OMIT_PERMSET & ~(libzfsacl.PERM_WRITE_DATA) + tfacl.setacl(path=tfile) + cmd = f'echo -n "CAT" >> {tfile}' + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], False) + + # Test omit for PERM_EXECUTE + # Following test fails due to unknown reasons, investigate... + def test_052_permset_omit_execute(self): + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = 0 + tdacl.setacl(path=self.TDIR) + cmd = f"cd {self.TDIR}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + os.rmdir(self.TDIR) + # self.assertEqual(res["result"], False) + + # Test omit for PERM_READ_ATTRIBUTES + # Following test fails due to unknown reasons, investigate... + def test_053_permset_omit_read_attrs(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w'): + pass + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = self.OMIT_PERMSET & ~(libzfsacl.PERM_READ_ATTRIBUTES) + tfacl.setacl(path=tfile) + cmd = f"stat {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + # self.assertEqual(res["result"], False) + + # Test omit for PERM_WRITE_ATTRIBUTES + def test_054_permset_omit_write_attrs(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w'): + pass + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = self.OMIT_PERMSET & ~(libzfsacl.PERM_WRITE_ATTRIBUTES) + tfacl.setacl(path=tfile) + cmd = f"touch a -m -t 201512180130.09 {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], False) + + # Test omit for PERM_DELETE + def test_055_permset_omit_delete(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w'): + pass + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = self.OMIT_PERMSET & ~(libzfsacl.PERM_DELETE | libzfsacl.PERM_EXECUTE | libzfsacl.PERM_WRITE_DATA) + tdacl.setacl(path=self.TDIR) + cmd = f"rm -f {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], False) + + # Test omit for PERM_DELETE_CHILD + def test_056_permset_omit_delete_child(self): + tddir = f'{self.TDIR}/test_dir' + os.makedirs(self.TDIR) + os.makedirs(tddir) + os.makedirs(f"{tddir}/tmp") + tfacl = libzfsacl.Acl(path=tddir) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = self.OMIT_PERMSET & ~(libzfsacl.PERM_DELETE_CHILD | libzfsacl.PERM_EXECUTE | libzfsacl.PERM_WRITE_DATA) + tfacl.setacl(path=tddir) + cmd = f"rm -rf {tddir}/tmp" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], False) + + # Test omit for PERM_READ_ACL + def test_057_permset_omit_read_acl(self): + pass + + # Test omit for PERM_WRITE_ACL + def test_058_permset_omit_write_acl(self): + pass + + # Test omit for PERM_WRITE_OWNER + def test_059_permset_omit_write_owner(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + with open(tfile, 'w'): + pass + tfacl = libzfsacl.Acl(path=tfile) + newEntry = tfacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = self.OMIT_PERMSET & ~(libzfsacl.PERM_WRITE_OWNER) + tfacl.setacl(path=tfile) + cmd = f"chown {self.ZFS_ACL_STAFF1} {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], False) + + # Test omit for PERM_ADD_FILE + def test_060_permset_omit_add_file(self): + tfile = f'{self.TDIR}/test.txt' + os.makedirs(self.TDIR) + tdacl = libzfsacl.Acl(path=self.TDIR) + newEntry = tdacl.create_entry(0) + newEntry.entry_type = libzfsacl.ENTRY_TYPE_ALLOW + newEntry.who = (libzfsacl.WHOTYPE_USER, self.ZFS_ACL_STAFF1_UID) + newEntry.flagset = 0 + newEntry.permset = self.OMIT_PERMSET & ~(libzfsacl.PERM_ADD_FILE) + tdacl.setacl(path=self.TDIR) + cmd = f"touch {tfile}" + res = run_as_user(cmd, self.ZFS_ACL_STAFF1) + shutil.rmtree(self.TDIR) + self.assertEqual(res["result"], False) diff --git a/rpm/generic/zfs.spec.in b/rpm/generic/zfs.spec.in index 5bd3145e28dd..766edb6b6df1 100644 --- a/rpm/generic/zfs.spec.in +++ b/rpm/generic/zfs.spec.in @@ -598,3 +598,4 @@ systemctl --system daemon-reload >/dev/null || true %files -n python%{__python_pkg_version}-libzfsacl %{__python_sitelib}/libzfsacl-*/* %{__python_sitelib}/libzfsacl.cpython*.so +%{__python_sitelib}/zfsacltests/* diff --git a/tests/runfiles/common.run b/tests/runfiles/common.run index 342f56d50d04..7653a2effcb1 100644 --- a/tests/runfiles/common.run +++ b/tests/runfiles/common.run @@ -28,6 +28,10 @@ failsafe = callbacks/zfs_failsafe outputdir = /var/tmp/test_results tags = ['functional'] +[tests/functional/acl/nfsv4] +tests = ['nfsacl_001'] +tags = ['functional', 'acl', 'nfsv4'] + [tests/functional/acl/off] tests = ['dosmode', 'posixmode'] tags = ['functional', 'acl'] diff --git a/tests/runfiles/sanity.run b/tests/runfiles/sanity.run index 449bf1c0f56a..2ad4ebdcef0d 100644 --- a/tests/runfiles/sanity.run +++ b/tests/runfiles/sanity.run @@ -30,6 +30,10 @@ failsafe = callbacks/zfs_failsafe outputdir = /var/tmp/test_results tags = ['functional'] +[tests/functional/acl/nfsv4] +tests = ['nfsacl_001'] +tags = ['functional', 'acl', 'nfsv4'] + [tests/functional/acl/off] tests = ['posixmode'] tags = ['functional', 'acl'] diff --git a/tests/zfs-tests/tests/Makefile.am b/tests/zfs-tests/tests/Makefile.am index 3b6b2ef734d0..8bd1275fa449 100644 --- a/tests/zfs-tests/tests/Makefile.am +++ b/tests/zfs-tests/tests/Makefile.am @@ -72,7 +72,8 @@ regen: nobase_nodist_datadir_zfs_tests_tests_DATA = \ functional/pam/utilities.kshlib nobase_nodist_datadir_zfs_tests_tests_SCRIPTS = \ - functional/pyzfs/pyzfs_unittest.ksh + functional/pyzfs/pyzfs_unittest.ksh \ + functional/acl/nfsv4/nfsacl_001.ksh SUBSTFILES += $(nobase_nodist_datadir_zfs_tests_tests_DATA) $(nobase_nodist_datadir_zfs_tests_tests_SCRIPTS) @@ -387,6 +388,8 @@ nobase_dist_datadir_zfs_tests_tests_DATA += \ functional/idmap_mount/idmap_mount_common.kshlib nobase_dist_datadir_zfs_tests_tests_SCRIPTS += \ + functional/acl/nfsv4/cleanup.ksh \ + functional/acl/nfsv4/setup.ksh \ functional/acl/off/cleanup.ksh \ functional/acl/off/dosmode.ksh \ functional/acl/off/posixmode.ksh \ diff --git a/tests/zfs-tests/tests/functional/acl/nfsv4/.gitignore b/tests/zfs-tests/tests/functional/acl/nfsv4/.gitignore new file mode 100644 index 000000000000..ed356dd820b0 --- /dev/null +++ b/tests/zfs-tests/tests/functional/acl/nfsv4/.gitignore @@ -0,0 +1 @@ +nfsacl_001.ksh diff --git a/tests/zfs-tests/tests/functional/acl/nfsv4/cleanup.ksh b/tests/zfs-tests/tests/functional/acl/nfsv4/cleanup.ksh new file mode 100755 index 000000000000..acde12e7bad6 --- /dev/null +++ b/tests/zfs-tests/tests/functional/acl/nfsv4/cleanup.ksh @@ -0,0 +1,37 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or https://opensource.org/licenses/CDDL-1.0. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2023 iXsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/acl/acl_common.kshlib + +cleanup_user_group + +default_cleanup + +if is_freebsd; then + mv /usr/bin/fortune_bak /usr/bin/fortune +fi diff --git a/tests/zfs-tests/tests/functional/acl/nfsv4/nfsacl_001.ksh.in b/tests/zfs-tests/tests/functional/acl/nfsv4/nfsacl_001.ksh.in new file mode 100755 index 000000000000..e481de690d85 --- /dev/null +++ b/tests/zfs-tests/tests/functional/acl/nfsv4/nfsacl_001.ksh.in @@ -0,0 +1,39 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or https://opensource.org/licenses/CDDL-1.0. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2023 iXsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/acl/acl_common.kshlib + +verify_runnable "global" +log_assert "Verify NFSv4 ACLs behave correctly" + +@PYTHON@ -m unittest --verbose zfsacltests.test_nfsv4acl +if [ $? -ne 0 ]; then + log_fail "Python unittest completed with errors" +fi + +log_pass "Python unittest completed without errors" diff --git a/tests/zfs-tests/tests/functional/acl/nfsv4/setup.ksh b/tests/zfs-tests/tests/functional/acl/nfsv4/setup.ksh new file mode 100755 index 000000000000..3a4261987c71 --- /dev/null +++ b/tests/zfs-tests/tests/functional/acl/nfsv4/setup.ksh @@ -0,0 +1,50 @@ +#!/bin/ksh -p +# +# CDDL HEADER START +# +# The contents of this file are subject to the terms of the +# Common Development and Distribution License (the "License"). +# You may not use this file except in compliance with the License. +# +# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE +# or https://opensource.org/licenses/CDDL-1.0. +# See the License for the specific language governing permissions +# and limitations under the License. +# +# When distributing Covered Code, include this CDDL HEADER in each +# file and include the License file at usr/src/OPENSOLARIS.LICENSE. +# If applicable, add the following below this CDDL HEADER, with the +# fields enclosed by brackets "[]" replaced with your own identifying +# information: Portions Copyright [yyyy] [name of copyright owner] +# +# CDDL HEADER END +# + +# +# Copyright 2023 iXsystems, Inc. All rights reserved. +# Use is subject to license terms. +# + +. $STF_SUITE/include/libtest.shlib +. $STF_SUITE/tests/functional/acl/acl_common.kshlib + +cleanup_user_group + +# Create staff group and add user to it +log_must add_group $ZFS_ACL_STAFF_GROUP +log_must add_user $ZFS_ACL_STAFF_GROUP $ZFS_ACL_STAFF1 +log_must add_user $ZFS_ACL_STAFF_GROUP $ZFS_ACL_STAFF2 + +if is_freebsd; then + mv /usr/bin/fortune /usr/bin/fortune_bak + cp /usr/bin/true /usr/bin/fortune +fi + +DISK=${DISKS%% *} +default_setup_noexit $DISK +log_must chmod 777 $TESTDIR + +# Use NFSv4 ACLs on filesystem +log_must zfs set acltype=nfsv4 $TESTPOOL/$TESTFS + +log_pass