diff --git a/pytest_tests/testsuites/ape/test_ape_native_protocol.py b/pytest_tests/testsuites/ape/test_ape_native_protocol.py new file mode 100644 index 0000000..f194cf1 --- /dev/null +++ b/pytest_tests/testsuites/ape/test_ape_native_protocol.py @@ -0,0 +1,1135 @@ +import allure +import pytest +from frostfs_testlib.cli import FrostfsAdm, FrostfsCli +from frostfs_testlib.credentials.interfaces import User +from frostfs_testlib.reporter import get_reporter +from frostfs_testlib.resources.cli import FROSTFS_ADM_CONFIG_PATH, FROSTFS_ADM_EXEC, FROSTFS_CLI_EXEC +from frostfs_testlib.resources.error_patterns import ( + NO_RULE_FOUND_CONTAINER, + NO_RULE_FOUND_OBJECT, + RULE_ACCESS_DENIED_CONTAINER, + RULE_ACCESS_DENIED_OBJECT, +) +from frostfs_testlib.shell.interfaces import Shell +from frostfs_testlib.steps.cli.object import delete_object, get_object, get_range, get_range_hash, head_object, put_object, search_object +from frostfs_testlib.storage.cluster import Cluster +from frostfs_testlib.storage.dataclasses.object_size import ObjectSize +from frostfs_testlib.testing import wait_for_success +from frostfs_testlib.testing.cluster_test_base import ClusterTestBase +from frostfs_testlib.utils.file_utils import generate_file +from frostfs_testlib_plugin_to.storage.capi.capi_client import CApiClient + +reporter = get_reporter() + +GET_CONTAINER = "GetContainer" +PUT_CONTAINER = "PutContainer" +DELETE_CONTAINER = "DeleteContainer" +LIST_CONTAINER = "ListContainers" +GET_OBJECT = "GetObject" +DELETE_OBJECT = "DeleteObject" +HASH_OBJECT = "HashObject" +RANGE_OBJECT = "RangeObject" +SEARCH_OBJECT = "SearchObject" +HEAD_OBJECT = "HeadObject" + + +@pytest.fixture(scope="session") +def remote_clis_nodes(cluster: Cluster): + clis = {} + for char, number in zip(["one", "two"], [0, 1]): + node = cluster.cluster_nodes[number] + shell = node.host.get_shell() + clis[char] = FrostfsCli( + shell=shell, + frostfs_cli_exec_path=FROSTFS_CLI_EXEC, + config_file=node.storage_node.get_remote_wallet_config_path(), + ) + return clis + + +@pytest.fixture(scope="session") +def remove_rule_ape_in_system(cluster: Cluster) -> None: + yield + target = "Chain ID" + with reporter.step("Create remote FrostfsCli for nodes."): + for node in cluster.cluster_nodes: + shell: Shell = node.host.get_shell() + remote_config: str = node.storage_node.get_remote_wallet_config_path() + cli = FrostfsCli(shell=shell, frostfs_cli_exec_path=FROSTFS_CLI_EXEC, config_file=remote_config) + with reporter.step(f"Check local overrides on {node.storage_node.id} nodes"): + rules = cli.control.list_rules( + endpoint=node.storage_node.get_control_endpoint(), target_name="root", target_type="namespace" + ).stdout + if target in rules: + with reporter.step("Delete rules"): + chain_ids = [i.split(" ")[2].strip() for i in rules.split("\n") if "Chain ID" in i] + for chain_id in chain_ids: + cli.control.remove_rule( + endpoint=node.storage_node.get_control_endpoint(), + target_type="namespace", + target_name="root", + chain_id=chain_id, + ) + + +@pytest.mark.ape_local +class TestApeLocalOverride(ClusterTestBase): + @allure.title("Deny to GetContainer in root tenant") + def test_local_override_deny_to_get_container_root( + self, + root_data_users: dict[str, User], + remote_clis_nodes: dict[str, FrostfsCli], + root_data_clis: dict[str, CApiClient], + remove_rule_ape_in_system, + ): + + with reporter.step("Create a namespace and a user for it"): + user = root_data_users["one"] + root_cli = root_data_clis["one"] + + remote_cli_node_1 = remote_clis_nodes["one"] + + with reporter.step("Create a container on the first node"): + cid_1 = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 4", + name="dcl1", + await_mode=True, + basic_acl="public-read-write", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + with reporter.step("Create a namespace rule for the first node"): + remote_cli_node_1.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="namespace", + target_name="root", + chain_id="denyContainerGet", + rule="deny Container.Get *", + ) + + with reporter.step("Get the container property on the first node"): + with pytest.raises(RuntimeError, match=RULE_ACCESS_DENIED_CONTAINER.format(operation=GET_CONTAINER)): + cid = ( + root_cli.container.get( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + cid=cid_1, + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + with reporter.step("Get the container property on the second node"): + cid = ( + root_cli.container.get( + rpc_endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + cid=cid_1, + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + assert cid_1 == cid, f"There is no container with id {cid_1} on the second node" + + with reporter.step("Delete a rule"): + remote_cli_node_1.control.remove_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="namespace", + target_name="root", + chain_id="denyContainerGet", + ) + + with reporter.step("Get the container property on the first node"): + cid = ( + root_cli.container.get( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + cid=cid_1, + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + assert cid_1 == cid, f"There is no container with id {cid_1} on the first node" + + @allure.title("Deny to PutContainer in root tenant") + def test_local_override_deny_to_put_container_root( + self, + root_data_users: dict[str, User], + remote_clis_nodes: dict[str, FrostfsCli], + root_data_clis: dict[str, FrostfsCli], + remove_rule_ape_in_system: None, + ): + + with reporter.step("Create a namespace and a user for it"): + user = root_data_users["one"] + root_cli = root_data_clis["one"] + + remote_cli_node_1 = remote_clis_nodes["one"] + + with reporter.step("Create a namespace rule for the first node"): + remote_cli_node_1.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="namespace", + target_name="root", + chain_id="denyContainerPut", + rule="deny Container.Put *", + ) + + with reporter.step("Create a container on the first node"): + with pytest.raises(RuntimeError, match=RULE_ACCESS_DENIED_CONTAINER.format(operation=PUT_CONTAINER)): + cid_1 = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 4", + name="dcl1", + await_mode=True, + basic_acl="public-read-write", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + cid_2 = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + policy="REP 4", + name="dcl2", + await_mode=True, + basic_acl="public-read-write", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + with reporter.step("Delete a rule"): + remote_cli_node_1.control.remove_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="namespace", + target_name="root", + chain_id="denyContainerPut", + ) + + with reporter.step("Create a container on the first node"): + cid_1 = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 4", + name="dcl3", + await_mode=True, + basic_acl="public-read-write", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + @allure.title("Deny to ListContainer in root tenant") + def test_local_override_deny_to_list_container_root( + self, + root_data_users: dict[str, User], + remote_clis_nodes: dict[str, FrostfsCli], + root_data_clis: dict[str, CApiClient], + remove_rule_ape_in_system: None, + ): + + with reporter.step("Create a namespace and a user for it"): + user = root_data_users["one"] + root_cli = root_data_clis["one"] + + remote_cli_node_1 = remote_clis_nodes["one"] + + with reporter.step("Create a container on the first node"): + cid = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 4", + name="dcl1", + await_mode=True, + basic_acl="public-read-write", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + with reporter.step("Create a namespace rule for the first node"): + remote_cli_node_1.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="namespace", + target_name="root", + chain_id="denyContainerList", + rule="deny Container.List *", + ) + + with reporter.step("List the container properties on the first node"): + with pytest.raises(RuntimeError, match=RULE_ACCESS_DENIED_CONTAINER.format(operation=LIST_CONTAINER)): + list_of_container_ids_on_node_1 = root_cli.container.list( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), ttl=1 + ).stdout.split("\n")[:-1] + + with reporter.step("List the container properties on the second node"): + list_of_container_ids_on_node_2 = root_cli.container.list( + rpc_endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), ttl=1 + ).stdout.split("\n")[:-1] + + assert cid in list_of_container_ids_on_node_2, f"Container id was not found from the list of container ids on the second node" + + with reporter.step("Delete a rule"): + remote_cli_node_1.control.remove_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="namespace", + target_name="root", + chain_id="denyContainerList", + ) + + with reporter.step("Display a list of containers on the first node"): + list_of_container_ids_on_node_1 = root_cli.container.list( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), ttl=1 + ).stdout.split("\n")[:-1] + + assert cid in list_of_container_ids_on_node_1, f"Container id was not found from the list of container ids on the first node" + + @allure.title("Deny to DeleteContainer in root tenant") + def test_local_override_deny_to_delete_container_root( + self, + root_data_users: dict[str, User], + remote_clis_nodes: dict[str, FrostfsCli], + root_data_clis: dict[str, FrostfsCli], + remove_rule_ape_in_system: None, + ): + + with reporter.step("Create a namespace and a user for it"): + user = root_data_users["one"] + root_cli = root_data_clis["one"] + + remote_cli_node_1 = remote_clis_nodes["one"] + + with reporter.step("Create a namespace rule for the first node"): + remote_cli_node_1.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="namespace", + target_name="root", + chain_id="denyContainerDelete", + rule="deny Container.Delete *", + ) + + with reporter.step("Create containers on the first node"): + cid_1 = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 4", + name="dcl1", + await_mode=True, + basic_acl="public-read-write", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + cid_2 = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 4", + name="dcl2", + await_mode=True, + basic_acl="public-read-write", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + with reporter.step("Delete first container from the first node"): + with pytest.raises(RuntimeError, match=RULE_ACCESS_DENIED_CONTAINER.format(operation=DELETE_CONTAINER)): + root_cli.container.delete( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + cid=cid_1, + ttl=1, + ) + + with reporter.step("Delete a second container from the second node"): + root_cli.container.delete( + rpc_endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + cid=cid_2, + ttl=1, + ) + + with reporter.step("Delete a rule"): + remote_cli_node_1.control.remove_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="namespace", + target_name="root", + chain_id="denyContainerDelete", + ) + + with reporter.step("Delete first container from the first node"): + root_cli.container.delete( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + cid=cid_1, + ttl=1, + ) + + @allure.title("Deny to GetObject in root tenant") + def test_local_override_deny_to_get_object_root( + self, + root_data_users: dict[str, User], + remote_clis_nodes: dict[int, FrostfsCli], + root_data_clis: dict[str, FrostfsCli], + simple_object_size: ObjectSize, + ): + + with reporter.step("Create a namespace and a user for it"): + user = root_data_users["one"] + root_cli = root_data_clis["one"] + + test_file = generate_file(simple_object_size.value) + + remote_cli_node_1 = remote_clis_nodes["one"] + + with reporter.step("Create a container on the first node"): + cid_1 = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 4", + name="dcl1", + await_mode=True, + basic_acl="public-read-write", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + with reporter.step("Create a container rule for the first node"): + remote_cli_node_1.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid_1}", + chain_id="denyGetObject", + rule=f"deny Object.Get /{cid_1}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid_1 = put_object( + wallet=user.wallet, + path=test_file, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Get object from container on the first node"): + with pytest.raises(RuntimeError, match=RULE_ACCESS_DENIED_OBJECT.format(operation=GET_OBJECT)): + get_object( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Get object from container on the second node"): + get_object( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + + with reporter.step("Delete a rule"): + remote_cli_node_1.control.remove_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid_1}", + chain_id="denyGetObject", + ) + + with reporter.step("Get object in container on the first node"): + get_object( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + @allure.title("Deny to HeadObject in root tenant") + def test_local_override_deny_to_head_object_root( + self, + root_data_users: dict[str, User], + remote_clis_nodes: dict[int, FrostfsCli], + root_data_clis: dict[str, FrostfsCli], + simple_object_size: ObjectSize, + ): + + with reporter.step("Create a namespace and a user for it"): + user = root_data_users["one"] + root_cli = root_data_clis["one"] + + test_file = generate_file(simple_object_size.value) + + remote_cli_node_1 = remote_clis_nodes["one"] + + with reporter.step("Create a container on the first node"): + cid_1 = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 2", + name="dcl1", + await_mode=True, + basic_acl="public-read-write", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + with reporter.step("Create a container rule for the first node"): + remote_cli_node_1.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid_1}", + chain_id="denyHeadObject", + rule=f"deny Object.Head /{cid_1}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid_1 = put_object( + wallet=user.wallet, + path=test_file, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Head object from container on the first node"): + with pytest.raises(RuntimeError, match=RULE_ACCESS_DENIED_OBJECT.format(operation=HEAD_OBJECT)): + head_object( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Head object from container on the second node"): + head_object_in_container_2 = head_object( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert head_object_in_container_2["objectID"] == oid_1, f"Head object {oid_1} was not found" + + with reporter.step("Delete a rule"): + remote_cli_node_1.control.remove_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid_1}", + chain_id="denyHeadObject", + ) + + with reporter.step("Head object in container on the first node"): + head_object_in_container_1 = head_object( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + assert head_object_in_container_1["objectID"] == oid_1, f"Head object {oid_1} was not found" + + @allure.title("Deny to SearchObject in root tenant") + def test_local_override_deny_to_search_object_root( + self, + root_data_users: dict[str, User], + remote_clis_nodes: dict[int, FrostfsCli], + root_data_clis: dict[str, FrostfsCli], + simple_object_size: ObjectSize, + ): + + with reporter.step("Create a namespace and a user for it"): + user = root_data_users["one"] + root_cli = root_data_clis["one"] + + test_file = generate_file(simple_object_size.value) + + remote_cli_node_1 = remote_clis_nodes["one"] + + with reporter.step("Create a container on the first node"): + cid_1 = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 2", + name="dcl1", + await_mode=True, + basic_acl="public-read-write", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + with reporter.step("Create a container rule for the first node"): + remote_cli_node_1.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid_1}", + chain_id="denySearchObject", + rule=f"deny Object.Search /{cid_1}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid_1 = put_object( + wallet=user.wallet, + path=test_file, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Search object from container on the first node"): + with pytest.raises(RuntimeError, match=RULE_ACCESS_DENIED_OBJECT.format(operation=SEARCH_OBJECT)): + search_object( + wallet=user.wallet, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Search object from container on the second node"): + search_object_in_container_2 = search_object( + wallet=user.wallet, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + + assert oid_1 in search_object_in_container_2, f"Object {oid_1} was not found" + + with reporter.step("Delete a rule"): + remote_cli_node_1.control.remove_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid_1}", + chain_id="denySearchObject", + ) + + with reporter.step("Search object in container on the first node"): + search_object_in_container_1 = search_object( + wallet=user.wallet, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + assert oid_1 in search_object_in_container_1, f"Object {oid_1} was not found" + + @allure.title("Deny to RangeObject in root tenant") + def test_local_override_deny_to_range_object_root( + self, + root_data_users: dict[str, User], + remote_clis_nodes: dict[int, FrostfsCli], + root_data_clis: dict[str, FrostfsCli], + simple_object_size: ObjectSize, + ): + + with reporter.step("Create a namespace and a user for it"): + user = root_data_users["one"] + root_cli = root_data_clis["one"] + + test_file = generate_file(simple_object_size.value) + + remote_cli_node_1 = remote_clis_nodes["one"] + + with reporter.step("Create a container on the first node"): + cid_1 = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 2", + name="dcl1", + await_mode=True, + basic_acl="public-read-write", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + with reporter.step("Create a container rule for the first node"): + remote_cli_node_1.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid_1}", + chain_id="denyRangeObject", + rule=f"deny Object.Range /{cid_1}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid_1 = put_object( + wallet=user.wallet, + path=test_file, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Range object from container on the first node"): + with pytest.raises(RuntimeError, match=RULE_ACCESS_DENIED_OBJECT.format(operation=RANGE_OBJECT)): + get_range( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + range_cut="0:10", + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Range object from container on the second node"): + get_range( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + range_cut="0:10", + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + + with reporter.step("Delete a rule"): + remote_cli_node_1.control.remove_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid_1}", + chain_id="denyRangeObject", + ) + + with reporter.step("Range object in container on the first node"): + get_range( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + range_cut="0:10", + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + @allure.title("Deny to HashObject in root tenant") + def test_local_override_deny_to_hash_object_root( + self, + root_data_users: dict[str, User], + remote_clis_nodes: dict[int, FrostfsCli], + root_data_clis: dict[str, FrostfsCli], + simple_object_size: ObjectSize, + ): + + with reporter.step("Create a namespace and a user for it"): + user = root_data_users["one"] + root_cli = root_data_clis["one"] + + test_file = generate_file(simple_object_size.value) + + remote_cli_node_1 = remote_clis_nodes["one"] + + with reporter.step("Create a container on the first node"): + cid_1 = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 2", + name="dcl1", + await_mode=True, + basic_acl="public-read-write", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + with reporter.step("Create a container rule for the first node"): + remote_cli_node_1.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid_1}", + chain_id="denyHashObject", + rule=f"deny Object.Hash /{cid_1}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid_1 = put_object( + wallet=user.wallet, + path=test_file, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Range hash object from container on the first node"): + with pytest.raises(RuntimeError, match=RULE_ACCESS_DENIED_OBJECT.format(operation=HASH_OBJECT)): + get_range_hash( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + range_cut="0:10", + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Range hash object from container on the second node"): + get_range_hash( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + range_cut="0:10", + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + + with reporter.step("Delete a rule"): + remote_cli_node_1.control.remove_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid_1}", + chain_id="denyHashObject", + ) + + with reporter.step("Range hash object in container on the first node"): + get_range_hash( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + range_cut="0:10", + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + @allure.title("Deny to DeleteObject in root tenant") + def test_local_override_deny_to_delete_object_root( + self, + root_data_users: dict[str, User], + remote_clis_nodes: dict[int, FrostfsCli], + root_data_clis: dict[str, FrostfsCli], + simple_object_size: ObjectSize, + ): + + with reporter.step("Create a namespace and a user for it"): + user = root_data_users["one"] + root_cli = root_data_clis["one"] + + test_file = generate_file(simple_object_size.value) + + remote_cli_node_1 = remote_clis_nodes["one"] + + with reporter.step("Create a container on the first node"): + cid_1 = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 2", + name="dcl1", + await_mode=True, + basic_acl="public-read-write", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + with reporter.step("Create a container rule for the first node"): + remote_cli_node_1.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid_1}", + chain_id="denyDeleteObject", + rule=f"deny Object.Delete /{cid_1}/*", + ) + + with reporter.step("Put objects in container on the first node"): + oid_1 = put_object( + wallet=user.wallet, + path=test_file, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + oid_2 = put_object( + wallet=user.wallet, + path=test_file, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Search object in container on the first node"): + search_object( + wallet=user.wallet, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Search object from container on the second node"): + search_object( + wallet=user.wallet, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + + with reporter.step("Delete object from container on the first node"): + with pytest.raises(RuntimeError, match=RULE_ACCESS_DENIED_OBJECT.format(operation=DELETE_OBJECT)): + delete_object( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Delete object from container on the second node"): + delete_object( + wallet=user.wallet, + oid=oid_2, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + + with reporter.step("Delete a rule"): + remote_cli_node_1.control.remove_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid_1}", + chain_id="denyDeleteObject", + ) + + with reporter.step("Delete object in container on the first node"): + delete_object( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + @allure.title("Allow to work with objects in root tenant") + def test_local_override_allow_to_work_with_objects_root( + self, + root_data_users: dict[str, User], + remote_clis_nodes: dict[int, FrostfsCli], + root_data_clis: dict[str, FrostfsCli], + simple_object_size: ObjectSize, + ): + + with reporter.step("Create a namespace and a user for it"): + user = root_data_users["one"] + root_cli = root_data_clis["one"] + + test_file = generate_file(simple_object_size.value) + + remote_cli_node_1 = remote_clis_nodes["one"] + + with reporter.step("Create a container on the first node"): + cid_1 = ( + ( + root_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 1 IN MOW CBF 1 SELECT 1 FROM MSK AS MOW FILTER SubDivCode EQ MOW AS MSK", + name="dcl1", + await_mode=True, + basic_acl="0", + ) + ) + .stdout.split(" ")[1] + .strip() + .split("\n")[0] + ) + + with reporter.step("Create a namespace rule for the first node"): + remote_cli_node_1.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid_1}", + chain_id="allowObjectOperation", + rule=f"allow Object.Put Object.Delete Object.Get Object.Head Object.Hash Object.Search Object.Range *", + ) + + with reporter.step("Put objects in container on the first node"): + oid_1 = put_object( + wallet=user.wallet, + path=test_file, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + oid_2 = put_object( + wallet=user.wallet, + path=test_file, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Get object in container on the first node"): + get_object( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Get object in container on the second node"): + with pytest.raises(RuntimeError, match=NO_RULE_FOUND_OBJECT.format(operation=GET_OBJECT)): + get_object( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + + with reporter.step("Head object in container on the first node"): + head_object_in_container_1 = head_object( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert head_object_in_container_1["objectID"] == oid_1, f"Head object {oid_1} was not found" + + with reporter.step("Head object in container on the second node"): + with pytest.raises(RuntimeError, match=NO_RULE_FOUND_OBJECT.format(operation=HEAD_OBJECT)): + head_object( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + + with reporter.step("Range hash object in container on the first node"): + get_range_hash( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + range_cut="0:10", + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Range hash object in container on the second node"): + with pytest.raises(RuntimeError, match=NO_RULE_FOUND_OBJECT.format(operation=HASH_OBJECT)): + get_range_hash( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + range_cut="0:10", + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + + with reporter.step("Search object in container on the first node"): + search_object_in_container_1 = search_object( + wallet=user.wallet, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert oid_1 in search_object_in_container_1, f"Object {oid_1} was not found" + + with reporter.step("Search object from container on the second node"): + with pytest.raises(RuntimeError, match=NO_RULE_FOUND_OBJECT.format(operation=SEARCH_OBJECT)): + search_object( + wallet=user.wallet, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + + with reporter.step("Range object in container on the first node"): + get_range( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + range_cut="0:10", + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Range object in container on the second node"): + with pytest.raises(RuntimeError, match=NO_RULE_FOUND_OBJECT.format(operation=RANGE_OBJECT)): + get_range( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + range_cut="0:10", + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + + with reporter.step("Delete object in container on the first node"): + delete_object( + wallet=user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Delete object from container on the second node"): + with pytest.raises(RuntimeError, match=NO_RULE_FOUND_OBJECT.format(operation=HEAD_OBJECT)): + delete_object( + wallet=user.wallet, + oid=oid_2, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + + with reporter.step("Delete a rule"): + remote_cli_node_1.control.remove_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="namespace", + target_name=f"kapusta", + chain_id="allowObjectOperation", + )