From 6567aa72a9213d67db3681ae7daa66ca17a50ac5 Mon Sep 17 00:00:00 2001 From: Andrey Berezin Date: Tue, 6 Dec 2022 18:06:39 +0300 Subject: [PATCH] Add bearer token tests for s3 wallet api calls Signed-off-by: Andrey Berezin --- pytest_tests/helpers/cluster.py | 1 + pytest_tests/helpers/container.py | 45 +++-- pytest_tests/helpers/wallet.py | 14 +- pytest_tests/pytest.ini | 1 + .../object/test_object_api_bearer.py | 159 ++++++++++++++++++ robot/resources/lib/python_keywords/acl.py | 4 +- .../lib/python_keywords/container.py | 2 + robot/variables/wellknown_acl.py | 2 + 8 files changed, 211 insertions(+), 17 deletions(-) create mode 100644 pytest_tests/testsuites/object/test_object_api_bearer.py diff --git a/pytest_tests/helpers/cluster.py b/pytest_tests/helpers/cluster.py index f5f2555e..391b9470 100644 --- a/pytest_tests/helpers/cluster.py +++ b/pytest_tests/helpers/cluster.py @@ -202,6 +202,7 @@ class Cluster: default_rpc_endpoint: str default_s3_gate_endpoint: str + default_http_gate_endpoint: str def __init__(self, hosting: Hosting) -> None: self._hosting = hosting diff --git a/pytest_tests/helpers/container.py b/pytest_tests/helpers/container.py index 0aac2115..3a94a062 100644 --- a/pytest_tests/helpers/container.py +++ b/pytest_tests/helpers/container.py @@ -5,7 +5,7 @@ import allure from cluster import Cluster from file_helper import generate_file, get_file_hash from neofs_testlib.shell import Shell -from neofs_verbs import put_object_to_random_node +from neofs_verbs import put_object, put_object_to_random_node from storage_object import StorageObjectInfo from wallet import WalletFile @@ -33,24 +33,47 @@ class StorageContainer: def get_wallet_path(self) -> str: return self.storage_container_info.wallet_file.path + def get_wallet_config_path(self) -> str: + return self.storage_container_info.wallet_file.config_path + @allure.step("Generate new object and put in container") - def generate_object(self, size: int, expire_at: Optional[int] = None) -> StorageObjectInfo: + def generate_object( + self, + size: int, + expire_at: Optional[int] = None, + bearer_token: Optional[str] = None, + endpoint: Optional[str] = None, + ) -> StorageObjectInfo: with allure.step(f"Generate object with size {size}"): file_path = generate_file(size) file_hash = get_file_hash(file_path) container_id = self.get_id() wallet_path = self.get_wallet_path() - + wallet_config = self.get_wallet_config_path() with allure.step(f"Put object with size {size} to container {container_id}"): - object_id = put_object_to_random_node( - wallet=wallet_path, - path=file_path, - cid=container_id, - expire_at=expire_at, - shell=self.shell, - cluster=self.cluster, - ) + if endpoint: + object_id = put_object( + wallet=wallet_path, + path=file_path, + cid=container_id, + expire_at=expire_at, + shell=self.shell, + endpoint=endpoint, + bearer=bearer_token, + wallet_config=wallet_config, + ) + else: + object_id = put_object_to_random_node( + wallet=wallet_path, + path=file_path, + cid=container_id, + expire_at=expire_at, + shell=self.shell, + cluster=self.cluster, + bearer=bearer_token, + wallet_config=wallet_config, + ) storage_object = StorageObjectInfo( container_id, diff --git a/pytest_tests/helpers/wallet.py b/pytest_tests/helpers/wallet.py index fa8069a1..50b7b6a2 100644 --- a/pytest_tests/helpers/wallet.py +++ b/pytest_tests/helpers/wallet.py @@ -1,10 +1,9 @@ import os import uuid from dataclasses import dataclass -from typing import Optional -from cluster import Cluster -from common import FREE_STORAGE, WALLET_PASS +from cluster import Cluster, NodeBase +from common import FREE_STORAGE, WALLET_CONFIG, WALLET_PASS from neofs_testlib.shell import Shell from neofs_testlib.utils.wallet import get_last_address_from_wallet, init_wallet from python_keywords.payment_neogo import deposit_gas, transfer_gas @@ -13,7 +12,14 @@ from python_keywords.payment_neogo import deposit_gas, transfer_gas @dataclass class WalletFile: path: str - password: str + password: str = WALLET_PASS + config_path: str = WALLET_CONFIG + + @staticmethod + def from_node(node: NodeBase): + return WalletFile( + node.get_wallet_path(), node.get_wallet_password(), node.get_wallet_config_path() + ) def get_address(self) -> str: """ diff --git a/pytest_tests/pytest.ini b/pytest_tests/pytest.ini index a5794f4d..bbf21604 100644 --- a/pytest_tests/pytest.ini +++ b/pytest_tests/pytest.ini @@ -29,6 +29,7 @@ markers = node_mgmt: neofs control commands session_token: tests for operations with session token static_session: tests for operations with static session token + bearer: tests for bearer tokens acl: All tests for ACL acl_basic: tests for basic ACL acl_bearer: tests for ACL with bearer diff --git a/pytest_tests/testsuites/object/test_object_api_bearer.py b/pytest_tests/testsuites/object/test_object_api_bearer.py new file mode 100644 index 00000000..63688b47 --- /dev/null +++ b/pytest_tests/testsuites/object/test_object_api_bearer.py @@ -0,0 +1,159 @@ +import allure +import pytest +from cluster import Cluster +from common import COMPLEX_OBJ_SIZE, SIMPLE_OBJ_SIZE +from container import REP_2_FOR_3_NODES_PLACEMENT_RULE, SINGLE_PLACEMENT_RULE, create_container +from epoch import get_epoch +from neofs_testlib.shell import Shell +from neofs_verbs import delete_object, get_object +from pytest import FixtureRequest +from python_keywords.acl import EACLAccess, EACLOperation, EACLRole, EACLRule, form_bearertoken_file +from wellknown_acl import EACL_PUBLIC_READ_WRITE + +from helpers.container import StorageContainer, StorageContainerInfo +from helpers.test_control import expect_not_raises +from helpers.wallet import WalletFile +from steps.cluster_test_base import ClusterTestBase +from steps.storage_object import StorageObjectInfo + + +@pytest.fixture(scope="module") +@allure.title("Create bearer token for OTHERS with all operations allowed for all containers") +def bearer_token_file_all_allow(default_wallet: str, client_shell: Shell, cluster: Cluster) -> str: + bearer = form_bearertoken_file( + default_wallet, + "", + [ + EACLRule(operation=op, access=EACLAccess.ALLOW, role=EACLRole.OTHERS) + for op in EACLOperation + ], + shell=client_shell, + endpoint=cluster.default_rpc_endpoint, + ) + + return bearer + + +@pytest.fixture(scope="module") +@allure.title("Create user container for bearer token usage") +def user_container( + default_wallet: str, client_shell: Shell, cluster: Cluster, request: FixtureRequest +) -> StorageContainer: + container_id = create_container( + default_wallet, + shell=client_shell, + rule=request.param, + basic_acl=EACL_PUBLIC_READ_WRITE, + endpoint=cluster.default_rpc_endpoint, + ) + # Deliberately using s3gate wallet here to test bearer token + s3gate = cluster.s3gates[0] + return StorageContainer( + StorageContainerInfo(container_id, WalletFile.from_node(s3gate)), + client_shell, + cluster, + ) + + +@pytest.fixture() +def storage_objects( + user_container: StorageContainer, + bearer_token_file_all_allow: str, + request: FixtureRequest, + client_shell: Shell, + cluster: Cluster, +) -> list[StorageObjectInfo]: + epoch = get_epoch(client_shell, cluster) + storage_objects: list[StorageObjectInfo] = [] + for node in cluster.storage_nodes: + storage_objects.append( + user_container.generate_object( + request.param, + epoch + 3, + bearer_token=bearer_token_file_all_allow, + endpoint=node.get_rpc_endpoint(), + ) + ) + return storage_objects + + +@pytest.mark.smoke +@pytest.mark.bearer +class TestObjectApiWithBearerToken(ClusterTestBase): + @pytest.mark.parametrize( + "user_container", + [SINGLE_PLACEMENT_RULE], + ids=["single replica for all nodes placement rule"], + indirect=True, + ) + @pytest.mark.parametrize( + "storage_objects", + [SIMPLE_OBJ_SIZE, COMPLEX_OBJ_SIZE], + ids=["simple object", "complex object"], + indirect=True, + ) + def test_delete_object_with_s3_wallet_bearer( + self, + storage_objects: list[StorageObjectInfo], + bearer_token_file_all_allow: str, + request: FixtureRequest, + ): + allure.dynamic.title( + f"Check that objects can be deleted from any node using s3gate wallet with bearer token for {request.node.callspec.id}" + ) + + s3_gate_wallet = self.cluster.s3gates[0] + with allure.step("Try to delete each object from first storage node"): + for storage_object in storage_objects: + with expect_not_raises(): + delete_object( + s3_gate_wallet.get_wallet_path(), + storage_object.cid, + storage_object.oid, + self.shell, + endpoint=self.cluster.default_rpc_endpoint, + bearer=bearer_token_file_all_allow, + wallet_config=s3_gate_wallet.get_wallet_config_path(), + ) + + @pytest.mark.parametrize( + "user_container", + [REP_2_FOR_3_NODES_PLACEMENT_RULE], + ids=["2 replicas for 3 nodes placement rule"], + indirect=True, + ) + @pytest.mark.parametrize( + "file_size", + [SIMPLE_OBJ_SIZE, COMPLEX_OBJ_SIZE], + ids=["simple object", "complex object"], + ) + def test_get_object_with_s3_wallet_bearer_from_all_nodes( + self, + user_container: StorageContainer, + file_size: int, + bearer_token_file_all_allow: str, + request: FixtureRequest, + ): + allure.dynamic.title( + f"Check that objects can be deleted from any node using s3gate wallet with bearer token for {request.node.callspec.id}" + ) + + s3_gate_wallet = self.cluster.s3gates[0] + with allure.step("Put one object to container"): + epoch = self.get_epoch() + storage_object = user_container.generate_object( + file_size, epoch + 3, bearer_token=bearer_token_file_all_allow + ) + + with allure.step("Try to fetch object from each storage node"): + for node in self.cluster.storage_nodes: + with expect_not_raises(): + get_object( + s3_gate_wallet.get_wallet_path(), + storage_object.cid, + storage_object.oid, + self.shell, + endpoint=node.get_rpc_endpoint(), + bearer=bearer_token_file_all_allow, + wallet_config=s3_gate_wallet.get_wallet_config_path(), + ) diff --git a/robot/resources/lib/python_keywords/acl.py b/robot/resources/lib/python_keywords/acl.py index 78f9ed92..36bdd38d 100644 --- a/robot/resources/lib/python_keywords/acl.py +++ b/robot/resources/lib/python_keywords/acl.py @@ -178,7 +178,7 @@ def form_bearertoken_file( then extends it with filters taken from , signs with bearer token and writes to file """ - enc_cid = _encode_cid_for_eacl(cid) + enc_cid = _encode_cid_for_eacl(cid) if cid else None file_path = os.path.join(os.getcwd(), ASSETS_DIR, str(uuid.uuid4())) eacl = get_eacl(wif, cid, shell, endpoint) @@ -189,7 +189,7 @@ def form_bearertoken_file( logger.info(json_eacl) eacl_result = { "body": { - "eaclTable": {"containerID": {"value": enc_cid}, "records": []}, + "eaclTable": {"containerID": {"value": enc_cid} if cid else enc_cid, "records": []}, "lifetime": {"exp": EACL_LIFETIME, "nbf": "1", "iat": "0"}, } } diff --git a/robot/resources/lib/python_keywords/container.py b/robot/resources/lib/python_keywords/container.py index a8783a05..6b3279bb 100644 --- a/robot/resources/lib/python_keywords/container.py +++ b/robot/resources/lib/python_keywords/container.py @@ -18,6 +18,8 @@ from neofs_testlib.shell import Shell logger = logging.getLogger("NeoLogger") DEFAULT_PLACEMENT_RULE = "REP 2 IN X CBF 1 SELECT 4 FROM * AS X" +SINGLE_PLACEMENT_RULE = "REP 1 IN X CBF 1 SELECT 4 FROM * AS X" +REP_2_FOR_3_NODES_PLACEMENT_RULE = "REP 2 IN X CBF 1 SELECT 3 FROM * AS X" @allure.step("Create Container") diff --git a/robot/variables/wellknown_acl.py b/robot/variables/wellknown_acl.py index d9c78d14..3e610a9c 100644 --- a/robot/variables/wellknown_acl.py +++ b/robot/variables/wellknown_acl.py @@ -7,3 +7,5 @@ READONLY_ACL_F = "1FBF8CFF" PUBLIC_ACL = "0FBFBFFF" INACCESSIBLE_ACL = "40000000" STICKYBIT_PUB_ACL = "3FFFFFFF" + +EACL_PUBLIC_READ_WRITE = "eacl-public-read-write"