Add static session token container tests
Signed-off-by: Vladimir Avdeev <v.avdeev@yadro.com>
This commit is contained in:
parent
455cafa08a
commit
9b0ac8579b
8 changed files with 444 additions and 200 deletions
|
@ -1,7 +1,5 @@
|
|||
import logging
|
||||
from dataclasses import dataclass
|
||||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
from typing import Optional
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -12,16 +10,16 @@ class ObjectRef:
|
|||
|
||||
@dataclass
|
||||
class LockObjectInfo(ObjectRef):
|
||||
lifetime: int = None
|
||||
expire_at: int = None
|
||||
lifetime: Optional[int] = None
|
||||
expire_at: Optional[int] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class StorageObjectInfo(ObjectRef):
|
||||
size: str = None
|
||||
wallet_file_path: str = None
|
||||
file_path: str = None
|
||||
file_hash: str = None
|
||||
attributes: list[dict[str, str]] = None
|
||||
tombstone: str = None
|
||||
locks: list[LockObjectInfo] = None
|
||||
size: Optional[int] = None
|
||||
wallet_file_path: Optional[str] = None
|
||||
file_path: Optional[str] = None
|
||||
file_hash: Optional[str] = None
|
||||
attributes: Optional[list[dict[str, str]]] = None
|
||||
tombstone: Optional[str] = None
|
||||
locks: Optional[list[LockObjectInfo]] = None
|
||||
|
|
|
@ -4,14 +4,14 @@ import logging
|
|||
import os
|
||||
import uuid
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional
|
||||
from enum import Enum
|
||||
from typing import Any, Optional
|
||||
|
||||
import allure
|
||||
import json_transformers
|
||||
from common import ASSETS_DIR, NEOFS_CLI_EXEC, NEOFS_ENDPOINT, WALLET_CONFIG
|
||||
from data_formatters import get_wallet_public_key
|
||||
from json_transformers import encode_for_json
|
||||
from neo3 import wallet
|
||||
from neofs_testlib.cli import NeofsCli
|
||||
from neofs_testlib.shell import Shell
|
||||
from storage_object_info import StorageObjectInfo
|
||||
|
@ -19,16 +19,6 @@ from wallet import WalletFile
|
|||
|
||||
logger = logging.getLogger("NeoLogger")
|
||||
|
||||
PUT_VERB = "PUT"
|
||||
DELETE_VERB = "DELETE"
|
||||
LOCK_VERB = "LOCK"
|
||||
|
||||
GET_VERB = "GET"
|
||||
RANGEHASH_VERB = "RANGEHASH"
|
||||
RANGE_VERB = "RANGE"
|
||||
HEAD_VERB = "HEAD"
|
||||
SEARCH_VERB = "SEARCH"
|
||||
|
||||
UNRELATED_KEY = "unrelated key in the session"
|
||||
UNRELATED_OBJECT = "unrelated object in the session"
|
||||
UNRELATED_CONTAINER = "unrelated container in the session"
|
||||
|
@ -36,6 +26,22 @@ WRONG_VERB = "wrong verb of the session"
|
|||
INVALID_SIGNATURE = "invalid signature of the session data"
|
||||
|
||||
|
||||
class ObjectVerb(Enum):
|
||||
PUT = "PUT"
|
||||
DELETE = "DELETE"
|
||||
GET = "GET"
|
||||
RANGEHASH = "RANGEHASH"
|
||||
RANGE = "RANGE"
|
||||
HEAD = "HEAD"
|
||||
SEARCH = "SEARCH"
|
||||
|
||||
|
||||
class ContainerVerb(Enum):
|
||||
CREATE = "PUT"
|
||||
DELETE = "DELETE"
|
||||
SETEACL = "SETEACL"
|
||||
|
||||
|
||||
@dataclass
|
||||
class Lifetime:
|
||||
exp: int = 100000000
|
||||
|
@ -44,76 +50,20 @@ class Lifetime:
|
|||
|
||||
|
||||
@allure.step("Generate Session Token")
|
||||
def generate_session_token(owner: str, session_wallet: str, cid: str = "") -> str:
|
||||
"""
|
||||
This function generates session token for ContainerSessionContext
|
||||
and writes it to the file. It is able to prepare session token file
|
||||
for a specific container (<cid>) or for every container (adds
|
||||
"wildcard" field).
|
||||
Args:
|
||||
owner(str): wallet address of container owner
|
||||
session_wallet(str): the path to wallet to which we grant the
|
||||
access via session token
|
||||
cid(optional, str): container ID of the container; if absent,
|
||||
we assume the session token is generated for any
|
||||
container
|
||||
Returns:
|
||||
(str): the path to the generated session token file
|
||||
"""
|
||||
file_path = os.path.join(os.getcwd(), ASSETS_DIR, str(uuid.uuid4()))
|
||||
|
||||
session_wlt_content = ""
|
||||
with open(session_wallet) as fout:
|
||||
session_wlt_content = json.load(fout)
|
||||
session_wlt = wallet.Wallet.from_json(session_wlt_content, password="")
|
||||
pub_key_64 = base64.b64encode(bytes.fromhex(str(session_wlt.accounts[0].public_key))).decode(
|
||||
"utf-8"
|
||||
)
|
||||
|
||||
session_token = {
|
||||
"body": {
|
||||
"id": f"{base64.b64encode(uuid.uuid4().bytes).decode('utf-8')}",
|
||||
"ownerID": {"value": f"{json_transformers.encode_for_json(owner)}"},
|
||||
"lifetime": {"exp": "100000000", "nbf": "0", "iat": "0"},
|
||||
"sessionKey": f"{pub_key_64}",
|
||||
"container": {
|
||||
"verb": "PUT",
|
||||
"wildcard": cid != "",
|
||||
**({"containerID": {"value": f"{encode_for_json(cid)}"}} if cid != "" else {}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
logger.info(f"Got this Session Token: {session_token}")
|
||||
with open(file_path, "w", encoding="utf-8") as session_token_file:
|
||||
json.dump(session_token, session_token_file, ensure_ascii=False, indent=4)
|
||||
|
||||
return file_path
|
||||
|
||||
|
||||
@allure.step("Generate Session Token For Object")
|
||||
def generate_object_session_token(
|
||||
def generate_session_token(
|
||||
owner_wallet: WalletFile,
|
||||
session_wallet: WalletFile,
|
||||
oids: list[str],
|
||||
cid: str,
|
||||
verb: str,
|
||||
session: dict[str, dict[str, Any]],
|
||||
tokens_dir: str,
|
||||
lifetime: Optional[Lifetime] = None,
|
||||
) -> str:
|
||||
"""
|
||||
This function generates session token for ObjectSessionContext
|
||||
and writes it to the file. It is able to prepare session token file
|
||||
for a specific container (<cid>) or for every container (adds
|
||||
"wildcard" field).
|
||||
This function generates session token and writes it to the file.
|
||||
Args:
|
||||
owner_wallet: wallet of container owner
|
||||
session_wallet: wallet to which we grant the
|
||||
access via session token
|
||||
cid: container ID of the container
|
||||
oids: list of objectIDs to put into session
|
||||
verb: verb to grant access to;
|
||||
Valid verbs are: GET, RANGE, RANGEHASH, HEAD, SEARCH.
|
||||
session_wallet: wallet to which we grant the access via session token
|
||||
session: Contains allowed operation with parameters
|
||||
tokens_dir: Dir for token
|
||||
lifetime: lifetime options for session
|
||||
Returns:
|
||||
The path to the generated session token file
|
||||
|
@ -123,7 +73,7 @@ def generate_object_session_token(
|
|||
|
||||
pub_key_64 = get_wallet_public_key(session_wallet.path, session_wallet.password, "base64")
|
||||
|
||||
lifetime = lifetime if lifetime else Lifetime()
|
||||
lifetime = lifetime or Lifetime()
|
||||
|
||||
session_token = {
|
||||
"body": {
|
||||
|
@ -137,15 +87,9 @@ def generate_object_session_token(
|
|||
"iat": f"{lifetime.iat}",
|
||||
},
|
||||
"sessionKey": pub_key_64,
|
||||
"object": {
|
||||
"verb": verb,
|
||||
"target": {
|
||||
"container": {"value": encode_for_json(cid)},
|
||||
"objects": [{"value": encode_for_json(oid)} for oid in oids],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
session_token["body"].update(session)
|
||||
|
||||
logger.info(f"Got this Session Token: {session_token}")
|
||||
with open(file_path, "w", encoding="utf-8") as session_token_file:
|
||||
|
@ -154,12 +98,117 @@ def generate_object_session_token(
|
|||
return file_path
|
||||
|
||||
|
||||
@allure.step("Generate Session Token For Container")
|
||||
def generate_container_session_token(
|
||||
owner_wallet: WalletFile,
|
||||
session_wallet: WalletFile,
|
||||
verb: ContainerVerb,
|
||||
tokens_dir: str,
|
||||
lifetime: Optional[Lifetime] = None,
|
||||
cid: Optional[str] = None,
|
||||
) -> str:
|
||||
"""
|
||||
This function generates session token for ContainerSessionContext
|
||||
and writes it to the file. It is able to prepare session token file
|
||||
for a specific container (<cid>) or for every container (adds
|
||||
"wildcard" field).
|
||||
Args:
|
||||
owner_wallet: wallet of container owner.
|
||||
session_wallet: wallet to which we grant the access via session token.
|
||||
verb: verb to grant access to.
|
||||
lifetime: lifetime options for session.
|
||||
cid: container ID of the container
|
||||
Returns:
|
||||
The path to the generated session token file
|
||||
"""
|
||||
session = {
|
||||
"container": {
|
||||
"verb": verb.value,
|
||||
"wildcard": cid is not None,
|
||||
**({"containerID": {"value": f"{encode_for_json(cid)}"}} if cid is not None else {}),
|
||||
},
|
||||
}
|
||||
|
||||
return generate_session_token(
|
||||
owner_wallet=owner_wallet,
|
||||
session_wallet=session_wallet,
|
||||
session=session,
|
||||
tokens_dir=tokens_dir,
|
||||
lifetime=lifetime,
|
||||
)
|
||||
|
||||
|
||||
@allure.step("Generate Session Token For Object")
|
||||
def generate_object_session_token(
|
||||
owner_wallet: WalletFile,
|
||||
session_wallet: WalletFile,
|
||||
oids: list[str],
|
||||
cid: str,
|
||||
verb: ObjectVerb,
|
||||
tokens_dir: str,
|
||||
lifetime: Optional[Lifetime] = None,
|
||||
) -> str:
|
||||
"""
|
||||
This function generates session token for ObjectSessionContext
|
||||
and writes it to the file.
|
||||
Args:
|
||||
owner_wallet: wallet of container owner
|
||||
session_wallet: wallet to which we grant the access via session token
|
||||
cid: container ID of the container
|
||||
oids: list of objectIDs to put into session
|
||||
verb: verb to grant access to; Valid verbs are: ObjectVerb.
|
||||
lifetime: lifetime options for session
|
||||
Returns:
|
||||
The path to the generated session token file
|
||||
"""
|
||||
session = {
|
||||
"object": {
|
||||
"verb": verb.value,
|
||||
"target": {
|
||||
"container": {"value": encode_for_json(cid)},
|
||||
"objects": [{"value": encode_for_json(oid)} for oid in oids],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return generate_session_token(
|
||||
owner_wallet=owner_wallet,
|
||||
session_wallet=session_wallet,
|
||||
session=session,
|
||||
tokens_dir=tokens_dir,
|
||||
lifetime=lifetime,
|
||||
)
|
||||
|
||||
|
||||
@allure.step("Get signed token for object session")
|
||||
def get_container_signed_token(
|
||||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
verb: ContainerVerb,
|
||||
shell: Shell,
|
||||
tokens_dir: str,
|
||||
lifetime: Optional[Lifetime] = None,
|
||||
) -> str:
|
||||
"""
|
||||
Returns signed token file path for static object session
|
||||
"""
|
||||
session_token_file = generate_container_session_token(
|
||||
owner_wallet=owner_wallet,
|
||||
session_wallet=user_wallet,
|
||||
verb=verb,
|
||||
tokens_dir=tokens_dir,
|
||||
lifetime=lifetime,
|
||||
)
|
||||
return sign_session_token(shell, session_token_file, owner_wallet)
|
||||
|
||||
|
||||
@allure.step("Get signed token for object session")
|
||||
def get_object_signed_token(
|
||||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
cid: str,
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
verb: str,
|
||||
verb: ObjectVerb,
|
||||
shell: Shell,
|
||||
tokens_dir: str,
|
||||
lifetime: Optional[Lifetime] = None,
|
||||
|
@ -169,15 +218,15 @@ def get_object_signed_token(
|
|||
"""
|
||||
storage_object_ids = [storage_object.oid for storage_object in storage_objects]
|
||||
session_token_file = generate_object_session_token(
|
||||
owner_wallet,
|
||||
user_wallet,
|
||||
storage_object_ids,
|
||||
owner_wallet.containers[0],
|
||||
verb,
|
||||
tokens_dir,
|
||||
owner_wallet=owner_wallet,
|
||||
session_wallet=user_wallet,
|
||||
oids=storage_object_ids,
|
||||
cid=cid,
|
||||
verb=verb,
|
||||
tokens_dir=tokens_dir,
|
||||
lifetime=lifetime,
|
||||
)
|
||||
return sign_session_token(shell, session_token_file, owner_wallet.path)
|
||||
return sign_session_token(shell, session_token_file, owner_wallet)
|
||||
|
||||
|
||||
@allure.step("Create Session Token")
|
||||
|
@ -212,7 +261,7 @@ def create_session_token(
|
|||
|
||||
|
||||
@allure.step("Sign Session Token")
|
||||
def sign_session_token(shell: Shell, session_token_file: str, wlt: str) -> str:
|
||||
def sign_session_token(shell: Shell, session_token_file: str, wlt: WalletFile) -> str:
|
||||
"""
|
||||
This function signs the session token by the given wallet.
|
||||
|
||||
|
@ -227,6 +276,6 @@ def sign_session_token(shell: Shell, session_token_file: str, wlt: str) -> str:
|
|||
signed_token_file = os.path.join(os.getcwd(), ASSETS_DIR, str(uuid.uuid4()))
|
||||
neofscli = NeofsCli(shell=shell, neofs_cli_exec_path=NEOFS_CLI_EXEC, config_file=WALLET_CONFIG)
|
||||
neofscli.util.sign_session_token(
|
||||
wallet=wlt, from_file=session_token_file, to_file=signed_token_file
|
||||
wallet=wlt.path, from_file=session_token_file, to_file=signed_token_file
|
||||
)
|
||||
return signed_token_file
|
||||
|
|
26
pytest_tests/testsuites/session_token/conftest.py
Normal file
26
pytest_tests/testsuites/session_token/conftest.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
import pytest
|
||||
from wallet import WalletFactory, WalletFile
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def owner_wallet(wallet_factory: WalletFactory) -> WalletFile:
|
||||
"""
|
||||
Returns wallet which owns containers and objects
|
||||
"""
|
||||
return wallet_factory.create_wallet()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def user_wallet(wallet_factory: WalletFactory) -> WalletFile:
|
||||
"""
|
||||
Returns wallet which will use objects from owner via static session
|
||||
"""
|
||||
return wallet_factory.create_wallet()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def stranger_wallet(wallet_factory: WalletFactory) -> WalletFile:
|
||||
"""
|
||||
Returns stranger wallet which should fail to obtain data
|
||||
"""
|
||||
return wallet_factory.create_wallet()
|
|
@ -6,7 +6,6 @@ from common import COMPLEX_OBJ_SIZE, SIMPLE_OBJ_SIZE
|
|||
from epoch import ensure_fresh_epoch, tick_epoch
|
||||
from file_helper import generate_file
|
||||
from grpc_responses import MALFORMED_REQUEST, OBJECT_ACCESS_DENIED, OBJECT_NOT_FOUND
|
||||
from neofs_testlib.hosting import Hosting
|
||||
from neofs_testlib.shell import Shell
|
||||
from pytest import FixtureRequest
|
||||
from python_keywords.container import create_container
|
||||
|
@ -20,23 +19,17 @@ from python_keywords.neofs_verbs import (
|
|||
put_object,
|
||||
search_object,
|
||||
)
|
||||
from wallet import WalletFactory, WalletFile
|
||||
from wallet import WalletFile
|
||||
|
||||
from helpers.storage_object_info import StorageObjectInfo
|
||||
from steps.session_token import (
|
||||
DELETE_VERB,
|
||||
GET_VERB,
|
||||
HEAD_VERB,
|
||||
INVALID_SIGNATURE,
|
||||
PUT_VERB,
|
||||
RANGE_VERB,
|
||||
RANGEHASH_VERB,
|
||||
SEARCH_VERB,
|
||||
UNRELATED_CONTAINER,
|
||||
UNRELATED_KEY,
|
||||
UNRELATED_OBJECT,
|
||||
WRONG_VERB,
|
||||
Lifetime,
|
||||
ObjectVerb,
|
||||
generate_object_session_token,
|
||||
get_object_signed_token,
|
||||
sign_session_token,
|
||||
|
@ -48,6 +41,14 @@ logger = logging.getLogger("NeoLogger")
|
|||
RANGE_OFFSET_FOR_COMPLEX_OBJECT = 200
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def storage_containers(owner_wallet: WalletFile, client_shell: Shell) -> list[str]:
|
||||
# Separate containers for complex/simple objects to avoid side-effects
|
||||
cid = create_container(owner_wallet.path, shell=client_shell)
|
||||
other_cid = create_container(owner_wallet.path, shell=client_shell)
|
||||
yield [cid, other_cid]
|
||||
|
||||
|
||||
@pytest.fixture(
|
||||
params=[SIMPLE_OBJ_SIZE, COMPLEX_OBJ_SIZE],
|
||||
ids=["simple object", "complex object"],
|
||||
|
@ -55,27 +56,26 @@ RANGE_OFFSET_FOR_COMPLEX_OBJECT = 200
|
|||
scope="module",
|
||||
)
|
||||
def storage_objects(
|
||||
owner_wallet: WalletFile, client_shell: Shell, request: FixtureRequest
|
||||
owner_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_containers: list[str],
|
||||
request: FixtureRequest,
|
||||
) -> list[StorageObjectInfo]:
|
||||
|
||||
file_path = generate_file(request.param)
|
||||
storage_objects = []
|
||||
|
||||
# Separate containers for complex/simple objects to avoid side-effects
|
||||
cid = create_container(owner_wallet.path, shell=client_shell)
|
||||
other_cid = create_container(owner_wallet.path, shell=client_shell)
|
||||
owner_wallet.containers = [cid, other_cid]
|
||||
|
||||
with allure.step("Put objects"):
|
||||
# upload couple objects
|
||||
for _ in range(3):
|
||||
storage_object_id = put_object(
|
||||
wallet=owner_wallet.path,
|
||||
path=file_path,
|
||||
cid=cid,
|
||||
cid=storage_containers[0],
|
||||
shell=client_shell,
|
||||
)
|
||||
|
||||
storage_object = StorageObjectInfo(cid, storage_object_id)
|
||||
storage_object = StorageObjectInfo(storage_containers[0], storage_object_id)
|
||||
storage_object.size = request.param
|
||||
storage_object.wallet_file_path = owner_wallet.path
|
||||
storage_object.file_path = file_path
|
||||
|
@ -102,56 +102,38 @@ def get_ranges(storage_object: StorageObjectInfo, shell: Shell) -> list[str]:
|
|||
return [
|
||||
"0:10",
|
||||
f"{object_size-10}:10",
|
||||
f"{max_object_size - RANGE_OFFSET_FOR_COMPLEX_OBJECT}:{RANGE_OFFSET_FOR_COMPLEX_OBJECT * 2}",
|
||||
f"{max_object_size - RANGE_OFFSET_FOR_COMPLEX_OBJECT}:"
|
||||
f"{RANGE_OFFSET_FOR_COMPLEX_OBJECT * 2}",
|
||||
]
|
||||
else:
|
||||
return ["0:10", f"{object_size-10}:10"]
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def owner_wallet(wallet_factory: WalletFactory) -> WalletFile:
|
||||
"""
|
||||
Returns wallet which owns containers and objects
|
||||
"""
|
||||
return wallet_factory.create_wallet()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def user_wallet(wallet_factory: WalletFactory) -> WalletFile:
|
||||
"""
|
||||
Returns wallet which will use objects from owner via static session
|
||||
"""
|
||||
return wallet_factory.create_wallet()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def stranger_wallet(wallet_factory: WalletFactory) -> WalletFile:
|
||||
"""
|
||||
Returns stranger wallet which should fail to obtain data
|
||||
"""
|
||||
return wallet_factory.create_wallet()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def static_sessions(
|
||||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
storage_containers: list[str],
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
client_shell: Shell,
|
||||
prepare_tmp_dir: str,
|
||||
) -> dict[str, str]:
|
||||
) -> dict[ObjectVerb, str]:
|
||||
"""
|
||||
Returns dict with static session token file paths for all verbs with default lifetime with valid container and first two objects
|
||||
Returns dict with static session token file paths for all verbs with default lifetime with
|
||||
valid container and first two objects
|
||||
"""
|
||||
verbs = [GET_VERB, RANGEHASH_VERB, RANGE_VERB, HEAD_VERB, SEARCH_VERB, DELETE_VERB, PUT_VERB]
|
||||
sessions = {}
|
||||
|
||||
for verb in verbs:
|
||||
sessions[verb] = get_object_signed_token(
|
||||
owner_wallet, user_wallet, storage_objects[0:2], verb, client_shell, prepare_tmp_dir
|
||||
return {
|
||||
verb: get_object_signed_token(
|
||||
owner_wallet,
|
||||
user_wallet,
|
||||
storage_containers[0],
|
||||
storage_objects[0:2],
|
||||
verb,
|
||||
client_shell,
|
||||
prepare_tmp_dir,
|
||||
)
|
||||
|
||||
return sessions
|
||||
for verb in ObjectVerb
|
||||
}
|
||||
|
||||
|
||||
@allure.title("Validate static session with read operations")
|
||||
|
@ -159,17 +141,17 @@ def static_sessions(
|
|||
@pytest.mark.parametrize(
|
||||
"method_under_test,verb",
|
||||
[
|
||||
(head_object, HEAD_VERB),
|
||||
(get_object, GET_VERB),
|
||||
(head_object, ObjectVerb.HEAD),
|
||||
(get_object, ObjectVerb.GET),
|
||||
],
|
||||
)
|
||||
def test_static_session_read(
|
||||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
static_sessions: list[str],
|
||||
static_sessions: dict[ObjectVerb, str],
|
||||
method_under_test,
|
||||
verb: str,
|
||||
verb: ObjectVerb,
|
||||
request: FixtureRequest,
|
||||
):
|
||||
"""
|
||||
|
@ -193,15 +175,15 @@ def test_static_session_read(
|
|||
@pytest.mark.static_session
|
||||
@pytest.mark.parametrize(
|
||||
"method_under_test,verb",
|
||||
[(get_range, RANGE_VERB), (get_range_hash, RANGEHASH_VERB)],
|
||||
[(get_range, ObjectVerb.RANGE), (get_range_hash, ObjectVerb.RANGEHASH)],
|
||||
)
|
||||
def test_static_session_range(
|
||||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
static_sessions: list[str],
|
||||
static_sessions: dict[ObjectVerb, str],
|
||||
method_under_test,
|
||||
verb: str,
|
||||
verb: ObjectVerb,
|
||||
request: FixtureRequest,
|
||||
):
|
||||
"""
|
||||
|
@ -233,7 +215,7 @@ def test_static_session_search(
|
|||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
static_sessions: list[str],
|
||||
static_sessions: dict[ObjectVerb, str],
|
||||
request: FixtureRequest,
|
||||
):
|
||||
"""
|
||||
|
@ -244,7 +226,7 @@ def test_static_session_search(
|
|||
cid = storage_objects[0].cid
|
||||
expected_object_ids = [storage_object.oid for storage_object in storage_objects[0:2]]
|
||||
actual_object_ids = search_object(
|
||||
user_wallet.path, cid, client_shell, session=static_sessions[SEARCH_VERB], root=True
|
||||
user_wallet.path, cid, client_shell, session=static_sessions[ObjectVerb.SEARCH], root=True
|
||||
)
|
||||
assert expected_object_ids == actual_object_ids
|
||||
|
||||
|
@ -255,7 +237,7 @@ def test_static_session_unrelated_object(
|
|||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
static_sessions: list[str],
|
||||
static_sessions: dict[ObjectVerb, str],
|
||||
request: FixtureRequest,
|
||||
):
|
||||
"""
|
||||
|
@ -270,7 +252,7 @@ def test_static_session_unrelated_object(
|
|||
storage_objects[2].cid,
|
||||
storage_objects[2].oid,
|
||||
client_shell,
|
||||
session=static_sessions[HEAD_VERB],
|
||||
session=static_sessions[ObjectVerb.HEAD],
|
||||
)
|
||||
|
||||
|
||||
|
@ -280,7 +262,7 @@ def test_static_session_head_unrelated_user(
|
|||
stranger_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
static_sessions: list[str],
|
||||
static_sessions: dict[ObjectVerb, str],
|
||||
request: FixtureRequest,
|
||||
):
|
||||
"""
|
||||
|
@ -297,7 +279,7 @@ def test_static_session_head_unrelated_user(
|
|||
storage_object.cid,
|
||||
storage_object.oid,
|
||||
client_shell,
|
||||
session=static_sessions[HEAD_VERB],
|
||||
session=static_sessions[ObjectVerb.HEAD],
|
||||
)
|
||||
|
||||
|
||||
|
@ -307,7 +289,7 @@ def test_static_session_head_wrong_verb(
|
|||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
static_sessions: list[str],
|
||||
static_sessions: dict[ObjectVerb, str],
|
||||
request: FixtureRequest,
|
||||
):
|
||||
"""
|
||||
|
@ -324,7 +306,7 @@ def test_static_session_head_wrong_verb(
|
|||
storage_object.cid,
|
||||
storage_object.oid,
|
||||
client_shell,
|
||||
session=static_sessions[HEAD_VERB],
|
||||
session=static_sessions[ObjectVerb.HEAD],
|
||||
)
|
||||
|
||||
|
||||
|
@ -334,8 +316,9 @@ def test_static_session_unrelated_container(
|
|||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_containers: list[str],
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
static_sessions: list[str],
|
||||
static_sessions: dict[ObjectVerb, str],
|
||||
request: FixtureRequest,
|
||||
):
|
||||
"""
|
||||
|
@ -349,10 +332,10 @@ def test_static_session_unrelated_container(
|
|||
with pytest.raises(Exception, match=UNRELATED_CONTAINER):
|
||||
get_object(
|
||||
user_wallet.path,
|
||||
owner_wallet.containers[1],
|
||||
storage_containers[1],
|
||||
storage_object.oid,
|
||||
client_shell,
|
||||
session=static_sessions[GET_VERB],
|
||||
session=static_sessions[ObjectVerb.GET],
|
||||
)
|
||||
|
||||
|
||||
|
@ -363,6 +346,7 @@ def test_static_session_signed_by_other(
|
|||
user_wallet: WalletFile,
|
||||
stranger_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_containers: list[str],
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
prepare_tmp_dir: str,
|
||||
request: FixtureRequest,
|
||||
|
@ -379,11 +363,11 @@ def test_static_session_signed_by_other(
|
|||
owner_wallet,
|
||||
user_wallet,
|
||||
[storage_object.oid],
|
||||
owner_wallet.containers[0],
|
||||
HEAD_VERB,
|
||||
storage_containers[0],
|
||||
ObjectVerb.HEAD,
|
||||
prepare_tmp_dir,
|
||||
)
|
||||
signed_token_file = sign_session_token(client_shell, session_token_file, stranger_wallet.path)
|
||||
signed_token_file = sign_session_token(client_shell, session_token_file, stranger_wallet)
|
||||
with pytest.raises(Exception, match=OBJECT_ACCESS_DENIED):
|
||||
head_object(
|
||||
user_wallet.path,
|
||||
|
@ -400,6 +384,7 @@ def test_static_session_signed_for_other_container(
|
|||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_containers: list[str],
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
prepare_tmp_dir: str,
|
||||
request: FixtureRequest,
|
||||
|
@ -411,12 +396,12 @@ def test_static_session_signed_for_other_container(
|
|||
f"Validate static session which signed for another container for {request.node.callspec.id}"
|
||||
)
|
||||
storage_object = storage_objects[0]
|
||||
container = owner_wallet.containers[1]
|
||||
container = storage_containers[1]
|
||||
|
||||
session_token_file = generate_object_session_token(
|
||||
owner_wallet, user_wallet, [storage_object.oid], container, HEAD_VERB, prepare_tmp_dir
|
||||
owner_wallet, user_wallet, [storage_object.oid], container, ObjectVerb.HEAD, prepare_tmp_dir
|
||||
)
|
||||
signed_token_file = sign_session_token(client_shell, session_token_file, owner_wallet.path)
|
||||
signed_token_file = sign_session_token(client_shell, session_token_file, owner_wallet)
|
||||
with pytest.raises(Exception, match=OBJECT_NOT_FOUND):
|
||||
head_object(
|
||||
user_wallet.path, container, storage_object.oid, client_shell, session=signed_token_file
|
||||
|
@ -429,6 +414,7 @@ def test_static_session_without_sign(
|
|||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_containers: list[str],
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
prepare_tmp_dir: str,
|
||||
request: FixtureRequest,
|
||||
|
@ -445,8 +431,8 @@ def test_static_session_without_sign(
|
|||
owner_wallet,
|
||||
user_wallet,
|
||||
[storage_object.oid],
|
||||
owner_wallet.containers[0],
|
||||
HEAD_VERB,
|
||||
storage_containers[0],
|
||||
ObjectVerb.HEAD,
|
||||
prepare_tmp_dir,
|
||||
)
|
||||
with pytest.raises(Exception, match=INVALID_SIGNATURE):
|
||||
|
@ -465,6 +451,7 @@ def test_static_session_expiration_at_next(
|
|||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_containers: list[str],
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
prepare_tmp_dir: str,
|
||||
request: FixtureRequest,
|
||||
|
@ -477,15 +464,16 @@ def test_static_session_expiration_at_next(
|
|||
)
|
||||
epoch = ensure_fresh_epoch(client_shell)
|
||||
|
||||
container = owner_wallet.containers[0]
|
||||
container = storage_containers[0]
|
||||
object_id = storage_objects[0].oid
|
||||
expiration = Lifetime(epoch + 1, epoch, epoch)
|
||||
|
||||
token_expire_at_next_epoch = get_object_signed_token(
|
||||
owner_wallet,
|
||||
user_wallet,
|
||||
container,
|
||||
storage_objects,
|
||||
HEAD_VERB,
|
||||
ObjectVerb.HEAD,
|
||||
client_shell,
|
||||
prepare_tmp_dir,
|
||||
expiration,
|
||||
|
@ -509,6 +497,7 @@ def test_static_session_start_at_next(
|
|||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_containers: list[str],
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
prepare_tmp_dir: str,
|
||||
request: FixtureRequest,
|
||||
|
@ -517,19 +506,21 @@ def test_static_session_start_at_next(
|
|||
Validate static session which is valid starting from next epoch
|
||||
"""
|
||||
allure.dynamic.title(
|
||||
f"Validate static session which is valid starting from next epoch for {request.node.callspec.id}"
|
||||
"Validate static session which is valid starting from next epoch "
|
||||
f"for {request.node.callspec.id}"
|
||||
)
|
||||
epoch = ensure_fresh_epoch(client_shell)
|
||||
|
||||
container = owner_wallet.containers[0]
|
||||
container = storage_containers[0]
|
||||
object_id = storage_objects[0].oid
|
||||
expiration = Lifetime(epoch + 2, epoch + 1, epoch)
|
||||
|
||||
token_start_at_next_epoch = get_object_signed_token(
|
||||
owner_wallet,
|
||||
user_wallet,
|
||||
container,
|
||||
storage_objects,
|
||||
HEAD_VERB,
|
||||
ObjectVerb.HEAD,
|
||||
client_shell,
|
||||
prepare_tmp_dir,
|
||||
expiration,
|
||||
|
@ -558,6 +549,7 @@ def test_static_session_already_expired(
|
|||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_containers: list[str],
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
prepare_tmp_dir: str,
|
||||
request: FixtureRequest,
|
||||
|
@ -570,15 +562,16 @@ def test_static_session_already_expired(
|
|||
)
|
||||
epoch = ensure_fresh_epoch(client_shell)
|
||||
|
||||
container = owner_wallet.containers[0]
|
||||
container = storage_containers[0]
|
||||
object_id = storage_objects[0].oid
|
||||
expiration = Lifetime(epoch - 1, epoch - 2, epoch - 2)
|
||||
|
||||
token_already_expired = get_object_signed_token(
|
||||
owner_wallet,
|
||||
user_wallet,
|
||||
container,
|
||||
storage_objects,
|
||||
HEAD_VERB,
|
||||
ObjectVerb.HEAD,
|
||||
client_shell,
|
||||
prepare_tmp_dir,
|
||||
expiration,
|
||||
|
@ -594,8 +587,9 @@ def test_static_session_already_expired(
|
|||
def test_static_session_delete_verb(
|
||||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_containers: list[str],
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
static_sessions: list[str],
|
||||
static_sessions: dict[ObjectVerb, str],
|
||||
request: FixtureRequest,
|
||||
):
|
||||
"""
|
||||
|
@ -611,7 +605,7 @@ def test_static_session_delete_verb(
|
|||
storage_object.cid,
|
||||
storage_object.oid,
|
||||
client_shell,
|
||||
session=static_sessions[DELETE_VERB],
|
||||
session=static_sessions[ObjectVerb.DELETE],
|
||||
)
|
||||
|
||||
|
||||
|
@ -620,7 +614,7 @@ def test_static_session_put_verb(
|
|||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
static_sessions: list[str],
|
||||
static_sessions: dict[ObjectVerb, str],
|
||||
request: FixtureRequest,
|
||||
):
|
||||
"""
|
||||
|
@ -636,7 +630,7 @@ def test_static_session_put_verb(
|
|||
storage_object.file_path,
|
||||
storage_object.cid,
|
||||
client_shell,
|
||||
session=static_sessions[PUT_VERB],
|
||||
session=static_sessions[ObjectVerb.PUT],
|
||||
)
|
||||
|
||||
|
||||
|
@ -646,6 +640,7 @@ def test_static_session_invalid_issued_epoch(
|
|||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
storage_containers: list[str],
|
||||
storage_objects: list[StorageObjectInfo],
|
||||
prepare_tmp_dir: str,
|
||||
request: FixtureRequest,
|
||||
|
@ -658,15 +653,16 @@ def test_static_session_invalid_issued_epoch(
|
|||
)
|
||||
epoch = ensure_fresh_epoch(client_shell)
|
||||
|
||||
container = owner_wallet.containers[0]
|
||||
container = storage_containers[0]
|
||||
object_id = storage_objects[0].oid
|
||||
expiration = Lifetime(epoch + 10, 0, epoch + 1)
|
||||
|
||||
token_invalid_issue_time = get_object_signed_token(
|
||||
owner_wallet,
|
||||
user_wallet,
|
||||
container,
|
||||
storage_objects,
|
||||
HEAD_VERB,
|
||||
ObjectVerb.HEAD,
|
||||
client_shell,
|
||||
prepare_tmp_dir,
|
||||
expiration,
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
import allure
|
||||
import pytest
|
||||
from file_helper import generate_file
|
||||
from neofs_testlib.shell import Shell
|
||||
from python_keywords.acl import (
|
||||
EACLAccess,
|
||||
EACLOperation,
|
||||
EACLRole,
|
||||
EACLRule,
|
||||
create_eacl,
|
||||
set_eacl,
|
||||
wait_for_cache_expired,
|
||||
)
|
||||
from python_keywords.container import (
|
||||
create_container,
|
||||
delete_container,
|
||||
get_container,
|
||||
list_containers,
|
||||
)
|
||||
from python_keywords.object_access import can_put_object
|
||||
from wallet import WalletFile
|
||||
from wellknown_acl import PUBLIC_ACL
|
||||
|
||||
from steps.session_token import ContainerVerb, get_container_signed_token
|
||||
|
||||
|
||||
class TestSessionTokenContainer:
|
||||
@pytest.fixture(scope="module")
|
||||
def static_sessions(
|
||||
self,
|
||||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
client_shell: Shell,
|
||||
prepare_tmp_dir: str,
|
||||
) -> dict[ContainerVerb, str]:
|
||||
"""
|
||||
Returns dict with static session token file paths for all verbs with default lifetime
|
||||
"""
|
||||
return {
|
||||
verb: get_container_signed_token(
|
||||
owner_wallet, user_wallet, verb, client_shell, prepare_tmp_dir
|
||||
)
|
||||
for verb in ContainerVerb
|
||||
}
|
||||
|
||||
def test_static_session_token_container_create(
|
||||
self,
|
||||
client_shell: Shell,
|
||||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
static_sessions: dict[ContainerVerb, str],
|
||||
):
|
||||
"""
|
||||
Validate static session with create operation
|
||||
"""
|
||||
with allure.step("Create container with static session token"):
|
||||
cid = create_container(
|
||||
user_wallet.path,
|
||||
session_token=static_sessions[ContainerVerb.CREATE],
|
||||
shell=client_shell,
|
||||
wait_for_creation=False,
|
||||
)
|
||||
|
||||
container_info: dict[str, str] = get_container(owner_wallet.path, cid, shell=client_shell)
|
||||
assert container_info["ownerID"] == owner_wallet.get_address()
|
||||
|
||||
assert cid not in list_containers(user_wallet.path, shell=client_shell)
|
||||
assert cid in list_containers(owner_wallet.path, shell=client_shell)
|
||||
|
||||
@pytest.mark.skip("Failed with timeout")
|
||||
def test_static_session_token_container_create_with_other_verb(
|
||||
self,
|
||||
client_shell: Shell,
|
||||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
static_sessions: dict[ContainerVerb, str],
|
||||
):
|
||||
"""
|
||||
Validate static session without create operation
|
||||
"""
|
||||
with allure.step("Try create container with static session token without PUT rule"):
|
||||
for verb in [verb for verb in ContainerVerb if verb != ContainerVerb.CREATE]:
|
||||
with pytest.raises(Exception):
|
||||
create_container(
|
||||
user_wallet.path,
|
||||
session_token=static_sessions[verb],
|
||||
shell=client_shell,
|
||||
wait_for_creation=False,
|
||||
)
|
||||
|
||||
@pytest.mark.skip("Failed with timeout")
|
||||
def test_static_session_token_container_create_with_other_wallet(
|
||||
self,
|
||||
client_shell: Shell,
|
||||
owner_wallet: WalletFile,
|
||||
stranger_wallet: WalletFile,
|
||||
static_sessions: dict[ContainerVerb, str],
|
||||
):
|
||||
"""
|
||||
Validate static session with create operation for other wallet
|
||||
"""
|
||||
with allure.step("Try create container with static session token without PUT rule"):
|
||||
with pytest.raises(Exception):
|
||||
create_container(
|
||||
stranger_wallet.path,
|
||||
session_token=static_sessions[ContainerVerb.CREATE],
|
||||
shell=client_shell,
|
||||
wait_for_creation=False,
|
||||
)
|
||||
|
||||
def test_static_session_token_container_delete(
|
||||
self,
|
||||
client_shell: Shell,
|
||||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
static_sessions: dict[ContainerVerb, str],
|
||||
):
|
||||
"""
|
||||
Validate static session with delete operation
|
||||
"""
|
||||
with allure.step("Create container"):
|
||||
cid = create_container(owner_wallet.path, shell=client_shell, wait_for_creation=False)
|
||||
with allure.step("Delete container with static session token"):
|
||||
delete_container(
|
||||
wallet=user_wallet.path,
|
||||
cid=cid,
|
||||
session_token=static_sessions[ContainerVerb.DELETE],
|
||||
shell=client_shell,
|
||||
)
|
||||
|
||||
assert cid not in list_containers(owner_wallet.path, shell=client_shell)
|
||||
|
||||
def test_static_session_token_container_set_eacl(
|
||||
self,
|
||||
client_shell: Shell,
|
||||
owner_wallet: WalletFile,
|
||||
user_wallet: WalletFile,
|
||||
stranger_wallet: WalletFile,
|
||||
static_sessions: dict[ContainerVerb, str],
|
||||
):
|
||||
"""
|
||||
Validate static session with set eacl operation
|
||||
"""
|
||||
with allure.step("Create container"):
|
||||
cid = create_container(owner_wallet.path, basic_acl=PUBLIC_ACL, shell=client_shell)
|
||||
file_path = generate_file()
|
||||
assert can_put_object(stranger_wallet.path, cid, file_path, client_shell)
|
||||
|
||||
with allure.step(f"Deny all operations for other via eACL"):
|
||||
eacl_deny = [
|
||||
EACLRule(access=EACLAccess.DENY, role=EACLRole.OTHERS, operation=op)
|
||||
for op in EACLOperation
|
||||
]
|
||||
set_eacl(
|
||||
user_wallet.path,
|
||||
cid,
|
||||
create_eacl(cid, eacl_deny, shell=client_shell),
|
||||
shell=client_shell,
|
||||
session_token=static_sessions[ContainerVerb.SETEACL],
|
||||
)
|
||||
wait_for_cache_expired()
|
||||
|
||||
assert not can_put_object(stranger_wallet.path, cid, file_path, client_shell)
|
|
@ -130,7 +130,13 @@ def get_eacl(wallet_path: str, cid: str, shell: Shell) -> Optional[str]:
|
|||
|
||||
|
||||
@allure.title("Set extended ACL")
|
||||
def set_eacl(wallet_path: str, cid: str, eacl_table_path: str, shell: Shell) -> None:
|
||||
def set_eacl(
|
||||
wallet_path: str,
|
||||
cid: str,
|
||||
eacl_table_path: str,
|
||||
shell: Shell,
|
||||
session_token: Optional[str] = None,
|
||||
) -> None:
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, WALLET_CONFIG)
|
||||
cli.container.set_eacl(
|
||||
wallet=wallet_path,
|
||||
|
@ -138,6 +144,7 @@ def set_eacl(wallet_path: str, cid: str, eacl_table_path: str, shell: Shell) ->
|
|||
cid=cid,
|
||||
table=eacl_table_path,
|
||||
await_mode=True,
|
||||
session=session_token,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -165,7 +165,9 @@ def get_container(
|
|||
@allure.step("Delete Container")
|
||||
# TODO: make the error message about a non-found container more user-friendly
|
||||
# https://github.com/nspcc-dev/neofs-contract/issues/121
|
||||
def delete_container(wallet: str, cid: str, shell: Shell, force: bool = False) -> None:
|
||||
def delete_container(
|
||||
wallet: str, cid: str, shell: Shell, force: bool = False, session_token: Optional[str] = None
|
||||
) -> None:
|
||||
"""
|
||||
A wrapper for `neofs-cli container delete` call.
|
||||
Args:
|
||||
|
@ -173,11 +175,14 @@ def delete_container(wallet: str, cid: str, shell: Shell, force: bool = False) -
|
|||
cid (str): ID of the container to delete
|
||||
shell: executor for cli command
|
||||
force (bool): do not check whether container contains locks and remove immediately
|
||||
session_token: a path to session token file
|
||||
This function doesn't return anything.
|
||||
"""
|
||||
|
||||
cli = NeofsCli(shell, NEOFS_CLI_EXEC, WALLET_CONFIG)
|
||||
cli.container.delete(wallet=wallet, cid=cid, rpc_endpoint=NEOFS_ENDPOINT, force=force)
|
||||
cli.container.delete(
|
||||
wallet=wallet, cid=cid, rpc_endpoint=NEOFS_ENDPOINT, force=force, session=session_token
|
||||
)
|
||||
|
||||
|
||||
def _parse_cid(output: str) -> str:
|
||||
|
|
|
@ -4,7 +4,7 @@ import os
|
|||
import random
|
||||
import re
|
||||
import uuid
|
||||
from typing import Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
import allure
|
||||
import json_transformers
|
||||
|
@ -406,7 +406,7 @@ def get_netmap_netinfo(
|
|||
address: Optional[str] = None,
|
||||
ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None,
|
||||
) -> dict[str, object]:
|
||||
) -> dict[str, Any]:
|
||||
"""
|
||||
Get netmap netinfo output from node
|
||||
|
||||
|
|
Loading…
Reference in a new issue