else:
can = stat.S_IMODE(mode1) != stat.S_IMODE(mode2)
finally:
- os.unlink(TESTFN)
+ unlink(TESTFN)
_can_chmod = can
return can
return test if ok else unittest.skip(msg)(test)
+# Check whether the current effective user has the capability to override
+# DAC (discretionary access control). Typically user root is able to
+# bypass file read, write, and execute permission checks. The capability
+# is independent of the effective user. See capabilities(7).
+_can_dac_override = None
+
+def can_dac_override():
+ global _can_dac_override
+
+ if not can_chmod():
+ _can_dac_override = False
+ if _can_dac_override is not None:
+ return _can_dac_override
+
+ try:
+ with open(TESTFN, "wb") as f:
+ os.chmod(TESTFN, 0o400)
+ try:
+ with open(TESTFN, "wb"):
+ pass
+ except OSError:
+ _can_dac_override = False
+ else:
+ _can_dac_override = True
+ finally:
+ unlink(TESTFN)
+
+ return _can_dac_override
+
+
+def skip_if_dac_override(test):
+ ok = not can_dac_override()
+ msg = "incompatible with CAP_DAC_OVERRIDE"
+ return test if ok else unittest.skip(msg)(test)
+
+
+def skip_unless_dac_override(test):
+ ok = can_dac_override()
+ msg = "requires CAP_DAC_OVERRIDE"
+ return test if ok else unittest.skip(msg)(test)
+
+
def unlink(filename):
try:
_unlink(filename)
return self.name == other.name
-@unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
- "non-root user required")
+@os_helper.skip_if_dac_override
class TestFileTypeW(TempDirMixin, ParserTestCase):
"""Test the FileType option/argument type for writing files"""
('-x - -', NS(x=eq_stdout, spam=eq_stdout)),
]
-@unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
- "non-root user required")
+
+@os_helper.skip_if_dac_override
class TestFileTypeX(TempDirMixin, ParserTestCase):
"""Test the FileType option/argument type for writing new files only"""
]
-@unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
- "non-root user required")
+@os_helper.skip_if_dac_override
class TestFileTypeWB(TempDirMixin, ParserTestCase):
"""Test the FileType option/argument type for writing binary files"""
]
-@unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
- "non-root user required")
+@os_helper.skip_if_dac_override
class TestFileTypeXB(TestFileTypeX):
"Test the FileType option/argument type for writing new binary files only"
@unittest.skipUnless(os.name == 'posix',
"test meaningful only on posix systems")
- @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
- "due to varying filesystem permission semantics (issue #11956)")
@skip_if_dont_write_bytecode
@os_helper.skip_unless_working_chmod
+ @os_helper.skip_if_dac_override
@unittest.skipIf(is_emscripten, "umask is a stub")
def test_unwritable_directory(self):
# When the umask causes the new __pycache__ directory to be
self.assertTrue(os.path.exists(self.pyc_path))
self.assertFalse(os.path.exists(self.cache_path))
- @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
- 'non-root user required')
+ @os_helper.skip_if_dac_override
@unittest.skipIf(os.name == 'nt',
'cannot control directory permissions on Windows')
@os_helper.skip_unless_working_chmod
@unittest.skipIf(sys.platform[:6] == 'cygwin',
"This test can't be run on Cygwin (issue #1071513).")
- @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
- "This test can't be run reliably as root (issue #1076467).")
+ @os_helper.skip_if_dac_override
@os_helper.skip_unless_working_chmod
def test_on_error(self):
self.errorState = 0
@os_helper.skip_unless_symlink
@os_helper.skip_unless_xattr
- @unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() == 0,
- 'root privileges required')
+ @os_helper.skip_unless_dac_override
def test_copyxattr_symlinks(self):
# On Linux, it's only possible to access non-user xattr for symlinks;
# which in turn require root privileges. This test should be expanded
# Other platforms: shouldn't match in the current directory.
self.assertIsNone(rv)
- @unittest.skipIf(hasattr(os, 'geteuid') and os.geteuid() == 0,
- 'non-root user required')
+ @os_helper.skip_if_dac_override
def test_non_matching_mode(self):
# Set the file read-only and ask for writeable files.
os.chmod(self.temp_file.name, stat.S_IREAD)
os.rmdir(dst_dir)
- @unittest.skipUnless(hasattr(os, 'geteuid') and os.geteuid() == 0
- and hasattr(os, 'lchflags')
+ @os_helper.skip_unless_dac_override
+ @unittest.skipUnless(hasattr(os, 'lchflags')
and hasattr(stat, 'SF_IMMUTABLE')
and hasattr(stat, 'UF_OPAQUE'),
- 'root privileges required')
+ 'requires lchflags')
def test_move_dir_permission_denied(self):
# bpo-42782: shutil.move should not create destination directories
# if the source directory cannot be removed.
--- /dev/null
+Tests now check for DAC override capability instead of relying on
+:func:`os.geteuid`.