def test_ensure_ownership_failed_chown(self): def stat_side_effect(name, limit, ver, path, buf): st = self.convert_stat_pointer(name, buf) if path == limit: st.st_uid = 2 st.st_gid = 2 return 0 else: self.delegate_to_original(name) return -1 with self.run_in_subprocess( "chown", "getpwnam", "__xstat", "__xstat64", ) as (enter, preloads): enter() preloads["chown"].return_value = -1 preloads["getpwnam"].side_effect = ( lambda name: self.make_pointer(Passwd(pw_uid=1, pw_gid=1))) self._set_stat_side_effect(preloads, stat_side_effect, self.db.props.root) self._make_ownership_test() self.assertRaisesDatabaseError( Click.DatabaseError.ENSURE_OWNERSHIP, self.db.ensure_ownership)
def test_ensure_ownership_quick_if_correct(self): def stat_side_effect(name, limit, ver, path, buf): st = self.convert_stat_pointer(name, buf) if path == limit: st.st_uid = 1 st.st_gid = 1 return 0 else: self.delegate_to_original(name) return -1 with self.run_in_subprocess( "chown", "getpwnam", "__xstat", "__xstat64", ) as (enter, preloads): enter() preloads["getpwnam"].side_effect = ( lambda name: self.make_pointer(Passwd(pw_uid=1, pw_gid=1))) self._set_stat_side_effect(preloads, stat_side_effect, self.db.props.root) self._make_ownership_test() self.db.ensure_ownership() self.assertFalse(preloads["chown"].called)
def test_gc_fixes_old_user_registrations(self): with self.run_in_subprocess("getpwnam") as (enter, preloads): enter() # Setup the system hook preloads["getpwnam"].side_effect = ( lambda name: self.make_pointer(Passwd(pw_dir=b"/foo"))) # Setup both databases db1 = os.path.join(self.temp_dir, "db1") db2 = os.path.join(self.temp_dir, "db2") db = Click.DB() db.add(db1) db.add(db2) # Prepare common manifest for the packages manifest = {"hooks": {"test-app": {"test": "foo"}}} # Setup versions 1.0 and 3.0 of package in db1 version1 = os.path.join(db1, "test-package", "1.0") with mkfile( os.path.join(version1, ".click", "info", "test-package.manifest")) as f: json.dump(manifest, f) version3 = os.path.join(db1, "test-package", "3.0") with mkfile( os.path.join(version3, ".click", "info", "test-package.manifest")) as f: json.dump(manifest, f) # Setup version 0.2 of package in db2 version2 = os.path.join(db2, "test-package", "2.0") with mkfile( os.path.join(version2, ".click", "info", "test-package.manifest")) as f: json.dump(manifest, f) # Setup the user registration for 2.0 in db2. registrationPath = os.path.join(db2, ".click", "users", "foo", "test-package") os.makedirs(os.path.dirname(registrationPath)) os.symlink(version2, registrationPath) # Run the garbage collection to update the registrations. db.gc() # Verify that the user still has a registration for the package, # and that it's now registered for version 3.0. self.assertTrue(os.path.lexists(registrationPath)) self.assertEqual(version3, os.readlink(registrationPath)) user_db = Click.User.for_user(db, "foo") try: version = user_db.get_version("test-package") self.assertEqual("3.0", version) except: self.fail("No user registration for 'test-package'")
def test_gc_ignores_non_directory(self): with self.run_in_subprocess("getpwnam") as (enter, preloads): enter() preloads["getpwnam"].side_effect = ( lambda name: self.make_pointer(Passwd(pw_uid=1, pw_gid=1))) a_path = os.path.join(self.temp_dir, "a", "1.0") a_manifest_path = os.path.join(a_path, ".click", "info", "a.manifest") with mkfile(a_manifest_path) as manifest: json.dump({"hooks": {"a-app": {}}}, manifest) a_user_path = os.path.join(self.temp_dir, ".click", "users", "test-user", "a") os.makedirs(os.path.dirname(a_user_path)) os.symlink(a_path, a_user_path) touch(os.path.join(self.temp_dir, "file")) self.db.gc() self.assertTrue(os.path.exists(a_path))
def test_ensure_ownership(self): def stat_side_effect(name, limit, ver, path, buf): st = self.convert_stat_pointer(name, buf) if path == limit: st.st_uid = 2 st.st_gid = 2 return 0 else: self.delegate_to_original(name) return -1 with self.run_in_subprocess( "chown", "getpwnam", "__xstat", "__xstat64", ) as (enter, preloads): enter() preloads["getpwnam"].side_effect = ( lambda name: self.make_pointer(Passwd(pw_uid=1, pw_gid=1))) self._set_stat_side_effect(preloads, stat_side_effect, self.db.props.root) self._make_ownership_test() self.db.ensure_ownership() expected_paths = [ self.temp_dir, os.path.join(self.temp_dir, ".click"), os.path.join(self.temp_dir, ".click", "log"), os.path.join(self.temp_dir, ".click", "users"), os.path.join(self.temp_dir, "a"), os.path.join(self.temp_dir, "a", "1.0"), os.path.join(self.temp_dir, "a", "1.0", ".click"), os.path.join(self.temp_dir, "a", "1.0", ".click", "info"), os.path.join(self.temp_dir, "a", "1.0", ".click", "info", "a.manifest"), os.path.join(self.temp_dir, "a", "current"), ] self.assertCountEqual( [path.encode() for path in expected_paths], [args[0][0] for args in preloads["chown"].call_args_list]) self.assertCountEqual( [(1, 1)], set(args[0][1:] for args in preloads["chown"].call_args_list))
def test_ensure_db_ownership(self): # getpwnam results are cached properly, in a way that doesn't fail # due to confusion with getpwnam returning a pointer to a static # buffer. with self.run_in_subprocess("chown", "geteuid", "getpwnam") as (enter, preloads): enter() preloads["geteuid"].return_value = 0 getpwnam_result = Passwd() def getpwnam_side_effect(name): if name == b"clickpkg": getpwnam_result.pw_uid = 1 getpwnam_result.pw_gid = 1 else: getpwnam_result.pw_uid = 2 getpwnam_result.pw_gid = 2 return self.make_pointer(getpwnam_result) preloads["getpwnam"].side_effect = getpwnam_side_effect registry = Click.User.for_user(self.db, "user") os.makedirs(os.path.join(self.temp_dir, "a", "1.0")) click_dir = os.path.join(self.temp_dir, ".click") registry.set_version("a", "1.0") self.assertEqual(3, preloads["chown"].call_count) preloads["chown"].assert_any_call(click_dir.encode(), 1, 1) preloads["chown"].assert_any_call( os.path.join(click_dir, "users").encode(), 1, 1) preloads["chown"].assert_any_call( os.path.join(click_dir, "users", "user").encode(), 2, 2) # Try again, now that both password file entries should be # cached. shutil.rmtree(os.path.join(self.temp_dir, ".click")) preloads["chown"].reset_mock() registry.set_version("a", "1.0") self.assertEqual(3, preloads["chown"].call_count) preloads["chown"].assert_any_call(click_dir.encode(), 1, 1) preloads["chown"].assert_any_call( os.path.join(click_dir, "users").encode(), 1, 1) preloads["chown"].assert_any_call( os.path.join(click_dir, "users", "user").encode(), 2, 2)
def test_ensure_db_chown_fails(self): with self.run_in_subprocess("chown", "geteuid", "getpwnam") as (enter, preloads): enter() preloads["geteuid"].return_value = 0 getpwnam_result = Passwd() def getpwnam_side_effect(name): if name == b"clickpkg": getpwnam_result.pw_uid = 1 getpwnam_result.pw_gid = 1 else: getpwnam_result.pw_uid = 2 getpwnam_result.pw_gid = 2 return self.make_pointer(getpwnam_result) preloads["getpwnam"].side_effect = getpwnam_side_effect preloads["chown"].return_value = -1 registry = Click.User.for_user(self.db, "user") self.assertRaisesUserError(Click.UserError.CHOWN_DB, registry.set_version, "a", "1.0")
def test_gc(self): with self.run_in_subprocess("click_find_on_path", "g_spawn_sync", "getpwnam") as (enter, preloads): enter() preloads["getpwnam"].side_effect = ( lambda name: self.make_pointer(Passwd(pw_uid=1, pw_gid=1))) os.environ["TEST_QUIET"] = "1" a_path = os.path.join(self.temp_dir, "a", "1.0") a_manifest_path = os.path.join(a_path, ".click", "info", "a.manifest") with mkfile(a_manifest_path) as manifest: json.dump({"hooks": {"a-app": {}}}, manifest) b_path = os.path.join(self.temp_dir, "b", "1.0") b_manifest_path = os.path.join(b_path, ".click", "info", "b.manifest") with mkfile(b_manifest_path) as manifest: json.dump({"hooks": {"b-app": {}}}, manifest) c_path = os.path.join(self.temp_dir, "c", "1.0") c_manifest_path = os.path.join(c_path, ".click", "info", "c.manifest") with mkfile(c_manifest_path) as manifest: json.dump({"hooks": {"c-app": {}}}, manifest) a_user_path = os.path.join(self.temp_dir, ".click", "users", "test-user", "a") os.makedirs(os.path.dirname(a_user_path)) os.symlink(a_path, a_user_path) b_gcinuse_path = os.path.join(self.temp_dir, ".click", "users", "@gcinuse", "b") os.makedirs(os.path.dirname(b_gcinuse_path)) os.symlink(b_path, b_gcinuse_path) preloads["g_spawn_sync"].side_effect = partial( self.g_spawn_sync_side_effect, {b"ubuntu-app-pid": 1 << 8}) preloads["click_find_on_path"].return_value = True self.db.gc() self.assertTrue(os.path.exists(a_path)) self.assertFalse(os.path.exists(b_gcinuse_path)) self.assertFalse(os.path.exists(b_path)) self.assertFalse(os.path.exists(c_path))