diff --git a/pytest_tests/helpers/container_access.py b/pytest_tests/helpers/container_access.py index f19ab9b7..ae6b899b 100644 --- a/pytest_tests/helpers/container_access.py +++ b/pytest_tests/helpers/container_access.py @@ -10,9 +10,10 @@ from pytest_tests.helpers.object_access import ( can_delete_object, can_get_head_object, can_get_object, + can_get_object_from_random_node, can_get_range_hash_of_object, can_get_range_of_object, - can_put_object, + can_put_object_to_random_node, can_search_object, ) @@ -37,12 +38,12 @@ def assert_access_to_container( endpoint = cluster.default_rpc_endpoint results: dict = {} - results[ape.ObjectOperations.PUT] = can_put_object(wallet, cid, file_name, shell, cluster, bearer, xhdr) + results[ape.ObjectOperations.PUT] = can_put_object_to_random_node(wallet, cid, file_name, shell, cluster, bearer, xhdr) results[ape.ObjectOperations.HEAD] = can_get_head_object(wallet, cid, oid, shell, endpoint, bearer, xhdr) results[ape.ObjectOperations.GET_RANGE] = can_get_range_of_object(wallet, cid, oid, shell, endpoint, bearer, xhdr) results[ape.ObjectOperations.GET_RANGE_HASH] = can_get_range_hash_of_object(wallet, cid, oid, shell, endpoint, bearer, xhdr) results[ape.ObjectOperations.SEARCH] = can_search_object(wallet, cid, shell, endpoint, oid, bearer, xhdr) - results[ape.ObjectOperations.GET] = can_get_object(wallet, cid, oid, file_name, shell, cluster, bearer, xhdr) + results[ape.ObjectOperations.GET] = can_get_object_from_random_node(wallet, cid, oid, file_name, shell, cluster, bearer, xhdr) results[ape.ObjectOperations.DELETE] = can_delete_object(wallet, cid, oid, shell, endpoint, bearer, xhdr) failed_checks = [ diff --git a/pytest_tests/helpers/object_access.py b/pytest_tests/helpers/object_access.py index 9fc873bc..3295647b 100644 --- a/pytest_tests/helpers/object_access.py +++ b/pytest_tests/helpers/object_access.py @@ -6,10 +6,12 @@ from frostfs_testlib.resources.error_patterns import OBJECT_ACCESS_DENIED from frostfs_testlib.shell import Shell from frostfs_testlib.steps.cli.object import ( delete_object, + get_object, get_object_from_random_node, get_range, get_range_hash, head_object, + put_object, put_object_to_random_node, search_object, ) @@ -21,7 +23,7 @@ from frostfs_testlib.utils.file_utils import get_file_hash OPERATION_ERROR_TYPE = RuntimeError -def can_get_object( +def can_get_object_from_random_node( wallet: WalletInfo, cid: str, oid: str, @@ -49,7 +51,35 @@ def can_get_object( return True -def can_put_object( +def can_get_object( + wallet: WalletInfo, + cid: str, + oid: str, + file_name: str, + shell: Shell, + endpoint: str, + bearer: Optional[str] = None, + xhdr: Optional[dict] = None, +) -> bool: + with reporter.step("Try get object from container"): + try: + got_file_path = get_object( + wallet, + cid, + oid, + shell=shell, + endpoint=endpoint, + bearer=bearer, + xhdr=xhdr, + ) + except OPERATION_ERROR_TYPE as err: + assert string_utils.is_str_match_pattern(err, OBJECT_ACCESS_DENIED), f"Expected {err} to match {OBJECT_ACCESS_DENIED}" + return False + assert get_file_hash(file_name) == get_file_hash(got_file_path) + return True + + +def can_put_object_to_random_node( wallet: WalletInfo, cid: str, file_name: str, @@ -77,6 +107,36 @@ def can_put_object( return True +def can_put_object( + wallet: WalletInfo, + cid: str, + file_name: str, + shell: Shell, + endpoint: str, + bearer: Optional[str] = None, + xhdr: Optional[dict] = None, + attributes: Optional[dict] = None, + copies_number: int | None = None, +) -> bool: + with reporter.step("Try put object to container"): + try: + put_object( + wallet=wallet, + cid=cid, + path=file_name, + endpoint=endpoint, + bearer=bearer, + xhdr=xhdr, + attributes=attributes, + shell=shell, + copies_number=copies_number, + ) + except OPERATION_ERROR_TYPE as err: + assert string_utils.is_str_match_pattern(err, OBJECT_ACCESS_DENIED), f"Expected {err} to match {OBJECT_ACCESS_DENIED}" + return False + return True + + def can_delete_object( wallet: WalletInfo, cid: str, diff --git a/pytest_tests/testsuites/ape/test_ape.py b/pytest_tests/testsuites/ape/test_ape.py new file mode 100644 index 00000000..798ae6da --- /dev/null +++ b/pytest_tests/testsuites/ape/test_ape.py @@ -0,0 +1,1639 @@ +import random +import string + +import allure +import base58 +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_OBJECT, + OBJECT_ACCESS_DENIED, + OBJECT_NOT_FOUND, + 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, ClusterNode +from frostfs_testlib.storage.dataclasses.ape import ObjectOperations +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.testing.parallel import parallel +from frostfs_testlib.utils.file_utils import generate_file +from frostfs_testlib_plugin_to.storage.capi.capi_client import CApiClient + +from pytest_tests.helpers.object_access import ( + can_delete_object, + can_get_head_object, + can_get_object, + can_get_range_hash_of_object, + can_get_range_of_object, + can_put_object, + can_search_object, +) + +reporter = get_reporter() + + +@pytest.fixture(scope="session") +def remote_frostfs_cli_first_node(cluster: Cluster): + node = cluster.cluster_nodes[0] + shell = node.host.get_shell() + cli = FrostfsCli( + shell=shell, + frostfs_cli_exec_path=FROSTFS_CLI_EXEC, + config_file=node.storage_node.get_remote_wallet_config_path(), + ) + return cli + + +@pytest.fixture(scope="session") +def remote_adm_node(cluster: Cluster): + node = cluster.cluster_nodes[0] + shell = node.host.get_shell() + adm = FrostfsAdm( + shell=shell, + frostfs_adm_exec_path=FROSTFS_ADM_EXEC, + config_file=FROSTFS_ADM_CONFIG_PATH, + ) + return adm + + +def local_overrides_on_node(node: ClusterNode): + target = "Chain ID" + 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} node"): + 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, + ) + + +def delete_rule_remote_adm_node(remote_adm_node: FrostfsAdm, target_type: str, target_name: str, chain_id: str): + with reporter.step("Delete rule"): + remote_adm_node.morph.remove_rule( + target_type=target_type, + target_name=target_name, + chain_id=chain_id, + ) + + with reporter.step("Сheck the rule has been deleted"): + list_rules = remote_adm_node.morph.list_rules(target_type=target_type, target_name=target_name).stdout + assert not chain_id in list_rules, "The rule hasn't been deleted" + + +def delete_rule_remote_cli_node(remote_cli_node: FrostfsCli, endpoint: str, target_type: str, target_name: str, chain_id: str): + with reporter.step("Delete rule"): + remote_cli_node.control.remove_rule( + target_type=target_type, + target_name=target_name, + chain_id=chain_id, + endpoint=endpoint, + ) + + with reporter.step("Сheck the rule has been deleted"): + list_rules = remote_cli_node.control.list_rules(target_type=target_type, target_name=target_name, endpoint=endpoint).stdout + assert not chain_id in list_rules, "The rule hasn't been deleted" + + +@pytest.fixture(scope="session") +def remove_rule_ape_in_system(cluster: Cluster) -> None: + yield + with reporter.step("Check local overrides on nodes."): + parallel(local_overrides_on_node, cluster.cluster_nodes) + + +@pytest.mark.ape_test +@pytest.mark.ape_morph_rule +class TestApeMorphRuleChain(ClusterTestBase): + @allure.title("MorphRuleChain: Allow to work with objects in root tenant") + def test_morph_rule_chain_allow_to_work_with_objects_root( + self, + default_user: User, + remote_adm_node: FrostfsAdm, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + test_file = generate_file(simple_object_size.value) + + with reporter.step("List rule for the first node"): + remote_adm_node.morph.list_rules( + target_type="namespace", + target_name="root", + ) + + with reporter.step("Create a container on the first node"): + cid_1 = ( + frostfs_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_adm_node.morph.add_rule( + 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=default_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=default_user.wallet, + path=test_file, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Check get object from container on the first node, allow expected"): + check_get_object_1 = can_get_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_get_object_1 == True, "Can't get object from container on the first node" + + with reporter.step("Check get object from container on the second node, allow expected"): + check_get_object_2 = can_get_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_get_object_2 == True, "Can't get object from container on the second node" + + with reporter.step("Check head object from container on the first node, allow expected"): + check_head_object_1 = can_get_head_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_head_object_1 == True, "Can't head object from container on the first node" + + with reporter.step("Check head object from container on the second node, allow expected"): + check_head_object_2 = can_get_head_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_head_object_2 == True, "Can't head object from container on the second node" + + with reporter.step("Check range hash object to container on the first node, allow expected"): + check_range_hash_object_1 = can_get_range_hash_of_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_hash_object_1 == True, "Can't range hash object to container on the first node" + + with reporter.step("Check range hesh object to container on the second node, allow expected"): + check_range_hash_object_2 = can_get_range_hash_of_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_range_hash_object_2 == True, "Can't range hash object to container on the second node" + + with reporter.step("Check search object to container on the first node, allow expected"): + check_search_object_1 = can_search_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_search_object_1 == True, "Can't search object to container on the first node" + + with reporter.step("Check search object to container on the second node, allow expected"): + check_search_object_2 = can_search_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_search_object_2 == True, "Can't search object to container on the second node" + + with reporter.step("Check range object to container on the first node, allow expected"): + check_range_object_1 = can_get_range_of_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_object_1 == True, "Can't range object to container on the first node" + + with reporter.step("Check range object to container on the second node, allow expected"): + check_range_object_2 = can_get_range_of_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_range_object_2 == True, "Can't range object to container on the second node" + + with reporter.step("Check delete object to container on the first node, allow expected"): + check_delete_object_1 = can_delete_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_delete_object_1 == True, "Can't delete object to container on the first node" + + with reporter.step("Check delete object to container on the second node, allow expected"): + check_delete_object_2 = can_delete_object( + wallet=default_user.wallet, + oid=oid_2, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_delete_object_2 == True, "Can't delete object to container on the second node" + + with reporter.step("Delete a rule"): + delete_rule_remote_adm_node( + remote_adm_node=remote_adm_node, + target_type="namespace", + target_name=f"kapusta", + chain_id="allowObjectOperation", + ) + + @allure.title("MorphRuleChain: Deny to GetObject in root tenant") + def test_morph_rule_chain_deny_to_get_object_root( + self, + default_user: User, + remote_adm_node: FrostfsAdm, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_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_adm_node.morph.add_rule( + target_type="container", + target_name=f"{cid}", + chain_id="denyGetObject", + rule=f"deny Object.Get /{cid}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid = put_object( + wallet=default_user.wallet, + path=test_file, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Check get object from container on the first node, expected error"): + check_get_object_1 = can_get_object( + wallet=default_user.wallet, + oid=oid, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_get_object_1 == False, "Can get object from container on the first node, expected object access denied" + + with reporter.step("Check get object from container on the second node, expected error"): + check_get_object_2 = can_get_object( + wallet=default_user.wallet, + oid=oid, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_get_object_2 == False, "Can get object from container on the second node, expected object access denied" + + with reporter.step("Delete a rule"): + delete_rule_remote_adm_node( + remote_adm_node=remote_adm_node, + target_type="container", + target_name=f"{cid}", + chain_id="denyGetObject", + ) + + with reporter.step("Check get object in container on the first node, expected allow"): + check_get_object = can_get_object( + wallet=default_user.wallet, + oid=oid, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_get_object == True, "Can't get object from container on the first node, expected allow" + + @allure.title("MorphRuleChain: Deny to PutObject in root tenant") + def test_morph_rule_chain_deny_to_put_object_root( + self, + default_user: User, + remote_adm_node: FrostfsAdm, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_cli.container.create( + rpc_endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + policy="REP 2 CBF 1", + 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_adm_node.morph.add_rule( + target_type="container", + target_name=f"{cid}", + chain_id="denyPutObject", + rule=f"deny Object.Put /{cid}/*", + ) + + with reporter.step("Check put object to container on the first node, expected error"): + check_put_object_1 = can_put_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_put_object_1 == False, "Can put object to container on the first node, expected object access denied" + + with reporter.step("Check get object from container on the second node, expected error"): + check_put_object_2 = can_put_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_put_object_2 == False, "Can put object to container on the second node, expected object access denied" + + with reporter.step("Delete a rule"): + delete_rule_remote_adm_node( + remote_adm_node, + target_type="container", + target_name=f"{cid}", + chain_id="denyPutObject", + ) + + with reporter.step("Check put object to container on the first node, expected allow"): + check_put_object = can_put_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_put_object == True, "Can't put object to container on the first node, expected allow" + + @allure.title("MorphRuleChain: Deny to HeadObject in root tenant") + def test_morph_rule_chain_deny_to_head_object_root( + self, + default_user: User, + remote_adm_node: FrostfsAdm, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_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_adm_node.morph.add_rule( + target_type="container", + target_name=f"{cid}", + chain_id="denyHeadObject", + rule=f"deny Object.Head /{cid}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid = put_object( + wallet=default_user.wallet, + path=test_file, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Check head object to container on the first node, expected error"): + check_head_object_1 = can_get_head_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_head_object_1 == False, "Can head object to container on the first node, expected object access denied" + + with reporter.step("Check head object from container on the second node, expected error"): + check_head_object_2 = can_get_head_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_head_object_2 == False, "Can head object to container on the second node, expected object access denied" + + with reporter.step("Delete a rule"): + delete_rule_remote_adm_node( + remote_adm_node, + target_type="container", + target_name=f"{cid}", + chain_id="denyHeadObject", + ) + + with reporter.step("Check head object in container on the first node, expected allow"): + check_head_object = can_get_head_object( + wallet=default_user.wallet, + oid=oid, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_head_object == True, "Can't head object from container on the first node, expected allow" + + @allure.title("MorphRuleChain: Deny to SearchObject in root tenant") + def test_morph_rule_chain_deny_to_search_object_root( + self, + default_user: User, + remote_adm_node: FrostfsAdm, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_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_adm_node.morph.add_rule( + target_type="container", + target_name=f"{cid}", + chain_id="denySearchObject", + rule=f"deny Object.Search /{cid}/*", + ) + + with reporter.step("Check search object to container on the first node, expected error"): + check_search_object_1 = can_search_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_search_object_1 == False, "Can search object to container on the first node, expected object access denied" + + with reporter.step("Check search object from container on the second node, expected error"): + check_search_object_2 = can_search_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_search_object_2 == False, "Can search object to container on the second node, expected object access denied" + + with reporter.step("Delete a rule"): + delete_rule_remote_adm_node( + remote_adm_node, + target_type="container", + target_name=f"{cid}", + chain_id="denySearchObject", + ) + + with reporter.step("Check search object in container on the first node, expected allow"): + check_search_object = can_search_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_search_object == True, "Can't search object from container on the first node, expected allow" + + @allure.title("MorphRuleChain: Deny to RangeObject in root tenant") + def test_morph_rule_chain_deny_to_range_object_root( + self, + default_user: User, + remote_adm_node: FrostfsAdm, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_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_adm_node.morph.add_rule( + target_type="container", + target_name=f"{cid}", + chain_id="denyRangeObject", + rule=f"deny Object.Range /{cid}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid = put_object( + wallet=default_user.wallet, + path=test_file, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Check range object to container on the first node, expected error"): + check_range_object_1 = can_get_range_of_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_object_1 == False, "Can range object to container on the first node, expected object access denied" + + with reporter.step("Check range object from container on the second node, expected error"): + check_range_object_2 = can_get_range_of_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_range_object_2 == False, "Can range object to container on the second node, expected object access denied" + + with reporter.step("Delete a rule"): + delete_rule_remote_adm_node( + remote_adm_node, + target_type="container", + target_name=f"{cid}", + chain_id="denyRangeObject", + ) + + with reporter.step("Check range object in container on the first node, expected allow"): + check_range_object = can_get_range_of_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_object == True, "Can't range object from container on the first node, expected allow" + + @allure.title("MorphRuleChain: Deny to HashObject in root tenant") + def test_morph_rule_chain_deny_to_hash_object_root( + self, + default_user: User, + remote_adm_node: FrostfsAdm, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_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_adm_node.morph.add_rule( + target_type="container", + target_name=f"{cid}", + chain_id="denyHashObject", + rule=f"deny Object.Hash /{cid}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid = put_object( + wallet=default_user.wallet, + path=test_file, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Check range hash object to container on the first node, expected error"): + check_range_hash_object_1 = can_get_range_hash_of_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_hash_object_1 == False, "Can range hash object to container on the first node, expected object access denied" + + with reporter.step("Check range hash object from container on the second node, expected error"): + check_range_hash_object_2 = can_get_range_hash_of_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert ( + check_range_hash_object_2 == False + ), "Can range hash object to container on the second node, expected object access denied" + + with reporter.step("Delete a rule"): + delete_rule_remote_adm_node( + remote_adm_node, + target_type="container", + target_name=f"{cid}", + chain_id="denyHashObject", + ) + + with reporter.step("Check range hash object in container on the first node, expected allow"): + check_range_hash_object = can_get_range_of_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_hash_object == True, "Can't range hash object from container on the first node, expected allow" + + @allure.title("MorphRuleChain: Deny to DeleteObject in root tenant") + def test_morph_rule_chain_deny_to_delete_object_root( + self, + default_user: User, + remote_adm_node: FrostfsAdm, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid_1 = ( + frostfs_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_adm_node.morph.add_rule( + target_type="container", + target_name=f"{cid_1}", + chain_id="denyDeleteObject", + rule=f"deny Object.Delete /{cid_1}/*", + ) + + with reporter.step("Put two objects in container on the first node"): + oid_1 = put_object( + wallet=default_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=default_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_in_container_1 = search_object( + wallet=default_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 and oid_2 in search_object_in_container_1 + ), f"Object {oid_1} or {oid_2} was not found" + + with reporter.step("Search object from container on the second node"): + search_object_in_container_2 = search_object( + wallet=default_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 and oid_2 in search_object_in_container_2 + ), f"Object {oid_1} or {oid_2} was not found" + + with reporter.step("Check delete object to container on the first node, expected error"): + check_delete_object_1 = can_delete_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_delete_object_1 == False, "Can delete object to container on the first node, expected object access denied" + + with reporter.step("Check delete object to container on the second node, expected error"): + check_delete_object_2 = can_delete_object( + wallet=default_user.wallet, + oid=oid_2, + cid=cid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_delete_object_2 == False, "Can delete object to container on the second node, expected object access denied" + + with reporter.step("Delete a rule"): + delete_rule_remote_adm_node( + remote_adm_node, + target_type="container", + target_name=f"{cid_1}", + chain_id="denyDeleteObject", + ) + + with reporter.step("Check delete object in container on the first node, expected allow"): + check_range_hash_object = can_get_range_of_object( + wallet=default_user.wallet, + cid=cid_1, + oid=oid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_hash_object == True, "Can't delete object from container on the first node, expected allow" + + +@pytest.mark.ape_test +@pytest.mark.ape_local +class TestApeLocalOverride(ClusterTestBase): + @allure.title("LocalOverride: Deny to GetObject in root tenant") + def test_local_override_deny_to_get_object_root( + self, + default_user: User, + remote_frostfs_cli_first_node: FrostfsCli, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_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_frostfs_cli_first_node.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid}", + chain_id="denyGetObject", + rule=f"deny Object.Get /{cid}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid = put_object( + wallet=default_user.wallet, + path=test_file, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Check get object from container on the first node, expected error"): + check_get_object_1 = can_get_object( + wallet=default_user.wallet, + oid=oid, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_get_object_1 == False, "Can get object from container on the first node, expected object access denied" + + with reporter.step("Check get object from container on the second node, expected allow"): + check_get_object_2 = can_get_object( + wallet=default_user.wallet, + oid=oid, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_get_object_2 == True, "Can't get object from container on the second node, expected allow" + + with reporter.step("Delete a rule"): + delete_rule_remote_cli_node( + remote_cli_node=remote_frostfs_cli_first_node, + target_type="container", + target_name=f"{cid}", + chain_id="denyGetObject", + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + ) + + with reporter.step("Check get object in container on the first node, expected allow"): + check_get_object = can_get_object( + wallet=default_user.wallet, + oid=oid, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_get_object == True, "Can't get object from container on the first node, expected allow" + + @allure.title("LocalOverride: Deny to PutObject in root tenant") + def test_local_override_deny_to_put_object_root( + self, + default_user: User, + remote_frostfs_cli_first_node: FrostfsCli, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_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_frostfs_cli_first_node.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid}", + chain_id="denyPutObject", + rule=f"deny Object.Put /{cid}/*", + ) + + with reporter.step("Check put object to container on the first node, expected error"): + check_put_object_1 = can_put_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_put_object_1 == False, "Can put object to container on the first node, expected object access denied" + + with reporter.step("Check get object from container on the second node, expected allow"): + check_put_object_2 = can_put_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + file_name=test_file.path, + copies_number=3, + ) + assert check_put_object_2 == True, "Can't put object to container on the second node, expected allow" + + with reporter.step("Delete a rule"): + delete_rule_remote_cli_node( + remote_frostfs_cli_first_node, + target_type="container", + target_name=f"{cid}", + chain_id="denyPutObject", + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + ) + + with reporter.step("Check put object to container on the first node, expected allow"): + check_put_object = can_put_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_put_object == True, "Can't put object to container on the first node, expected allow" + + @allure.title("LocalOverride: Deny to HeadObject in root tenant") + def test_local_override_deny_to_head_object_root( + self, + default_user: User, + remote_frostfs_cli_first_node: FrostfsCli, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_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_frostfs_cli_first_node.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid}", + chain_id="denyHeadObject", + rule=f"deny Object.Head /{cid}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid = put_object( + wallet=default_user.wallet, + path=test_file, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Check head object to container on the first node, expected error"): + check_head_object_1 = can_get_head_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_head_object_1 == False, "Can head object to container on the first node, expected object access denied" + + with reporter.step("Check head object from container on the second node, expected allow"): + check_head_object_2 = can_get_head_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_head_object_2 == True, "Can't head object from container on the second node, expected allow" + + with reporter.step("Delete a rule"): + delete_rule_remote_cli_node( + remote_frostfs_cli_first_node, + target_type="container", + target_name=f"{cid}", + chain_id="denyHeadObject", + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + ) + + with reporter.step("Check head object in container on the first node, expected allow"): + check_head_object = can_get_head_object( + wallet=default_user.wallet, + oid=oid, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_head_object == True, "Can't head object from container on the first node, expected allow" + + @allure.title("LocalOverride: Deny to SearchObject in root tenant") + def test_local_override_deny_to_search_object_root( + self, + default_user: User, + remote_frostfs_cli_first_node: FrostfsCli, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_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_frostfs_cli_first_node.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid}", + chain_id="denySearchObject", + rule=f"deny Object.Search /{cid}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid = put_object( + wallet=default_user.wallet, + path=test_file, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Check search object to container on the first node, expected error"): + check_search_object_1 = can_search_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_search_object_1 == False, "Can search object to container on the first node, expected object access denied" + + with reporter.step("Check search object from container on the second node, expected allow"): + check_search_object_2 = can_search_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_search_object_2 == True, "Can't search object from container on the second node, expected allow" + + with reporter.step("Delete a rule"): + delete_rule_remote_cli_node( + remote_frostfs_cli_first_node, + target_type="container", + target_name=f"{cid}", + chain_id="denySearchObject", + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + ) + + with reporter.step("Check search object in container on the first node, expected allow"): + check_search_object = can_search_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_search_object == True, "Can't search object from container on the first node, expected allow" + + @allure.title("LocalOverride: Deny to RangeObject in root tenant") + def test_local_override_deny_to_range_object_root( + self, + default_user: User, + remote_frostfs_cli_first_node: FrostfsCli, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_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_frostfs_cli_first_node.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid}", + chain_id="denyRangeObject", + rule=f"deny Object.Range /{cid}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid = put_object( + wallet=default_user.wallet, + path=test_file, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Check range object to container on the first node, expected error"): + check_range_object_1 = can_get_range_of_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_object_1 == False, "Can range object to container on the first node, expected object access denied" + + with reporter.step("Check range object from container on the second node, expected allow"): + check_range_object_2 = can_get_range_of_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_range_object_2 == True, "Can't range object from container on the second node, expected allow" + + with reporter.step("Delete a rule"): + delete_rule_remote_cli_node( + remote_frostfs_cli_first_node, + target_type="container", + target_name=f"{cid}", + chain_id="denyRangeObject", + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + ) + + with reporter.step("Check range object in container on the first node, expected allow"): + check_range_object = can_get_range_of_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_object == True, "Can't range object from container on the first node, expected allow" + + @allure.title("LocalOverride: Deny to HashObject in root tenant") + def test_local_override_deny_to_hash_object_root( + self, + default_user: User, + remote_frostfs_cli_first_node: FrostfsCli, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_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_frostfs_cli_first_node.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid}", + chain_id="denyHashObject", + rule=f"deny Object.Hash /{cid}/*", + ) + + with reporter.step("Put object in container on the first node"): + oid = put_object( + wallet=default_user.wallet, + path=test_file, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Check range hash object to container on the first node, expected error"): + check_range_hash_object_1 = can_get_range_hash_of_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_hash_object_1 == False, "Can range hash object to container on the first node, expected object access denied" + + with reporter.step("Check range hash object from container on the second node, expected allow"): + check_range_hash_object_2 = can_get_range_hash_of_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_range_hash_object_2 == True, "Can't range hash object from container on the second node, expected allow" + + with reporter.step("Delete a rule"): + delete_rule_remote_cli_node( + remote_frostfs_cli_first_node, + target_type="container", + target_name=f"{cid}", + chain_id="denyHashObject", + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + ) + + with reporter.step("Check range hash object in container on the first node, expected allow"): + check_range_hash_object = can_get_range_of_object( + wallet=default_user.wallet, + cid=cid, + oid=oid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_hash_object == True, "Can't range hash object from container on the first node, expected allow" + + @allure.title("LocalOverride: Deny to DeleteObject in root tenant") + def test_local_override_deny_to_delete_object_root( + self, + default_user: User, + remote_frostfs_cli_first_node: FrostfsCli, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_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_frostfs_cli_first_node.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid}", + chain_id="denyDeleteObject", + rule=f"deny Object.Delete /{cid}/*", + ) + + with reporter.step("Put objects in container on the first node"): + oid_1 = put_object( + wallet=default_user.wallet, + path=test_file, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + oid_2 = put_object( + wallet=default_user.wallet, + path=test_file, + cid=cid, + 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_in_container_1 = search_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert ( + oid_1 in search_object_in_container_1 and oid_2 in search_object_in_container_1 + ), f"Object {oid_1} or {oid_2} was not found" + + with reporter.step("Search object from container on the second node"): + search_object_in_container_2 = search_object( + wallet=default_user.wallet, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert ( + oid_1 in search_object_in_container_2 and oid_2 in search_object_in_container_2 + ), f"Object {oid_1} or {oid_2} was not found" + + with reporter.step("Check delete object to container on the first node, expected error"): + check_delete_object_1 = can_delete_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_delete_object_1 == False, "Can delete object to container on the first node, expected object access denied" + + with reporter.step("Check delete object to container on the second node, expected error"): + check_delete_object_2 = can_delete_object( + wallet=default_user.wallet, oid=oid_2, cid=cid, shell=self.shell, endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint() + ) + assert check_delete_object_2 == True, "Can't delete object from container on the second node, expected allow" + + with reporter.step("Delete a rule"): + delete_rule_remote_cli_node( + remote_frostfs_cli_first_node, + target_type="container", + target_name=f"{cid}", + chain_id="denyDeleteObject", + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + ) + + with reporter.step("Check delete object in container on the first node, expected allow"): + check_range_hash_object = can_get_range_of_object( + wallet=default_user.wallet, + cid=cid, + oid=oid_1, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_hash_object == True, "Can't delete object from container on the first node, expected allow" + + @allure.title("LocalOverride: Allow to work with objects in root tenant") + def test_local_override_allow_to_work_with_objects_root( + self, + default_user: User, + remote_frostfs_cli_first_node: FrostfsCli, + frostfs_cli: FrostfsCli, + simple_object_size: ObjectSize, + ): + + test_file = generate_file(simple_object_size.value) + + with reporter.step("Create a container on the first node"): + cid = ( + frostfs_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_frostfs_cli_first_node.control.add_rule( + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + target_type="container", + target_name=f"{cid}", + 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=default_user.wallet, + path=test_file, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + oid_2 = put_object( + wallet=default_user.wallet, + path=test_file, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + + with reporter.step("Check get object from container on the first node, allow expected"): + check_get_object_1 = can_get_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_get_object_1 == True, "Can't get object from container on the first node" + + with reporter.step("Check get object from container on the second node, expected error"): + check_get_object_2 = can_get_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + file_name=test_file.path, + ) + assert check_get_object_2 == False, "Can get object from container on the second node, expected object access denied" + + with reporter.step("Check head object from container on the first node, allow expected"): + check_head_object_1 = can_get_head_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_head_object_1 == True, "Can't head object from container on the first node" + + with reporter.step("Check head object from container on the second node, expected error"): + check_head_object_2 = can_get_head_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint(), + ) + assert check_head_object_2 == False, "Can head object to container on the second node, expected object access denied" + + with reporter.step("Check range hash object to container on the first node, allow expected"): + check_range_hash_object_1 = can_get_range_hash_of_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_hash_object_1 == True, "Can't range hash object to container on the first node" + + with reporter.step("Check range hesh object to container on the second node, expected error"): + check_range_hash_object_2 = can_get_range_hash_of_object( + wallet=default_user.wallet, oid=oid_1, cid=cid, shell=self.shell, endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint() + ) + assert ( + check_range_hash_object_2 == False + ), "Can range hash object to container on the second node, expected object access denied" + + with reporter.step("Check search object to container on the first node, expected allow"): + check_search_object_1 = can_search_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_search_object_1 == True, "Can't search object to container on the second node" + + with reporter.step("Check search object to container on the second node, expected error"): + check_search_object_2 = can_search_object( + wallet=default_user.wallet, oid=oid_1, cid=cid, shell=self.shell, endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint() + ) + assert check_search_object_2 == False, "Can search object to container on the second node, expected object access denied" + + with reporter.step("Check range object to container on the first node, allow expected"): + check_range_object_1 = can_get_range_of_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_range_object_1 == True, "Can't range object to container on the first node" + + with reporter.step("Check range object to container on the second node, expected error"): + check_range_object_2 = can_get_range_of_object( + wallet=default_user.wallet, oid=oid_1, cid=cid, shell=self.shell, endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint() + ) + assert check_range_object_2 == False, "Can range object to container on the second node, expected object access denied" + + with reporter.step("Check delete object to container on the first node, allow expected"): + check_delete_object_1 = can_delete_object( + wallet=default_user.wallet, + oid=oid_1, + cid=cid, + shell=self.shell, + endpoint=self.cluster.storage_nodes[0].get_rpc_endpoint(), + ) + assert check_delete_object_1 == True, "Can't delete object to container on the first node" + + with reporter.step("Check delete object to container on the second node, allow expected"): + check_delete_object_2 = can_delete_object( + wallet=default_user.wallet, oid=oid_2, cid=cid, shell=self.shell, endpoint=self.cluster.storage_nodes[1].get_rpc_endpoint() + ) + assert check_delete_object_2 == False, "Can delete object to container on the first node, expected object access denied" + + with reporter.step("Delete a rule"): + delete_rule_remote_cli_node( + remote_frostfs_cli_first_node, + target_type="container", + target_name=f"{cid}", + chain_id="allowObjectOperation", + endpoint=self.cluster.storage_nodes[0].get_control_endpoint(), + )