forked from TrueCloudLab/frostfs-testcases
Add ACL and eACL PyTest tests
Signed-off-by: Vladimir Avdeev <v.avdeev@yadro.com>
This commit is contained in:
parent
590a5cfb0e
commit
6d040c6834
36 changed files with 979 additions and 1318 deletions
|
@ -1,63 +1,119 @@
|
|||
#!/usr/bin/python3.9
|
||||
|
||||
import base64
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
import uuid
|
||||
from enum import Enum, auto
|
||||
from typing import Optional
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
from time import sleep
|
||||
from typing import Any, Dict, List, Optional, Union
|
||||
|
||||
import allure
|
||||
import base58
|
||||
from cli import NeofsCli
|
||||
from cli_helpers import _cmd_run
|
||||
from common import ASSETS_DIR, NEOFS_CLI_EXEC, NEOFS_ENDPOINT, WALLET_CONFIG
|
||||
from data_formatters import get_wallet_public_key
|
||||
from robot.api import logger
|
||||
from robot.api.deco import keyword
|
||||
|
||||
"""
|
||||
Robot Keywords and helper functions for work with NeoFS ACL.
|
||||
"""
|
||||
|
||||
ROBOT_AUTO_KEYWORDS = False
|
||||
logger = logging.getLogger('NeoLogger')
|
||||
EACL_LIFETIME = 100500
|
||||
NEOFS_CONTRACT_CACHE_TIMEOUT = 30
|
||||
|
||||
|
||||
class AutoName(Enum):
|
||||
def _generate_next_value_(name, start, count, last_values):
|
||||
return name
|
||||
class EACLOperation(Enum):
|
||||
PUT = 'put'
|
||||
GET = 'get'
|
||||
HEAD = 'head'
|
||||
GET_RANGE = 'getrange'
|
||||
GET_RANGE_HASH = 'getrangehash'
|
||||
SEARCH = 'search'
|
||||
DELETE = 'delete'
|
||||
|
||||
|
||||
class Role(AutoName):
|
||||
USER = auto()
|
||||
SYSTEM = auto()
|
||||
OTHERS = auto()
|
||||
class EACLAccess(Enum):
|
||||
ALLOW = 'allow'
|
||||
DENY = 'deny'
|
||||
|
||||
|
||||
@keyword('Get eACL')
|
||||
class EACLRole(Enum):
|
||||
OTHERS = 'others'
|
||||
USER = 'user'
|
||||
SYSTEM = 'system'
|
||||
|
||||
|
||||
class EACLHeaderType(Enum):
|
||||
REQUEST = 'req' # Filter request headers
|
||||
OBJECT = 'obj' # Filter object headers
|
||||
SERVICE = 'SERVICE' # Filter service headers. These are not processed by NeoFS nodes and exist for service use only
|
||||
|
||||
|
||||
class EACLMatchType(Enum):
|
||||
STRING_EQUAL = '=' # Return true if strings are equal
|
||||
STRING_NOT_EQUAL = '!=' # Return true if strings are different
|
||||
|
||||
|
||||
@dataclass
|
||||
class EACLFilter:
|
||||
header_type: EACLHeaderType = EACLHeaderType.REQUEST
|
||||
match_type: EACLMatchType = EACLMatchType.STRING_EQUAL
|
||||
key: Optional[str] = None
|
||||
value: Optional[str] = None
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return {'headerType': self.header_type, 'matchType': self.match_type, 'key': self.key, 'value': self.value}
|
||||
|
||||
|
||||
@dataclass
|
||||
class EACLFilters:
|
||||
filters: Optional[List[EACLFilter]] = None
|
||||
|
||||
def __str__(self):
|
||||
return ','.join(
|
||||
[f'{filter.header_type.value}:{filter.key}{filter.match_type.value}{filter.value}'
|
||||
for filter in self.filters]
|
||||
) if self.filters else []
|
||||
|
||||
|
||||
@dataclass
|
||||
class EACLPubKey:
|
||||
keys: Optional[List[str]] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class EACLRule:
|
||||
operation: Optional[EACLOperation] = None
|
||||
access: Optional[EACLAccess] = None
|
||||
role: Optional[Union[EACLRole, str]] = None
|
||||
filters: Optional[EACLFilters] = None
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return {'Operation': self.operation, 'Access': self.access, 'Role': self.role,
|
||||
'Filters': self.filters or []}
|
||||
|
||||
def __str__(self):
|
||||
role = self.role.value if isinstance(self.role, EACLRole) else f'pubkey:{get_wallet_public_key(self.role, "")}'
|
||||
return f'{self.access.value} {self.operation.value} {self.filters or ""} {role}'
|
||||
|
||||
|
||||
@allure.title('Get extended ACL')
|
||||
def get_eacl(wallet_path: str, cid: str) -> Optional[str]:
|
||||
cmd = (
|
||||
f'{NEOFS_CLI_EXEC} --rpc-endpoint {NEOFS_ENDPOINT} --wallet {wallet_path} '
|
||||
f'container get-eacl --cid {cid} --config {WALLET_CONFIG}'
|
||||
)
|
||||
cli = NeofsCli(config=WALLET_CONFIG)
|
||||
try:
|
||||
output = _cmd_run(cmd)
|
||||
if re.search(r'extended ACL table is not set for this container', output):
|
||||
return None
|
||||
return output
|
||||
output = cli.container.get_eacl(wallet=wallet_path, rpc_endpoint=NEOFS_ENDPOINT, cid=cid)
|
||||
except RuntimeError as exc:
|
||||
logger.info("Extended ACL table is not set for this container")
|
||||
logger.info(f"Got exception while getting eacl: {exc}")
|
||||
return None
|
||||
if 'extended ACL table is not set for this container' in output:
|
||||
return None
|
||||
return output
|
||||
|
||||
|
||||
@keyword('Set eACL')
|
||||
@allure.title('Set extended ACL')
|
||||
def set_eacl(wallet_path: str, cid: str, eacl_table_path: str) -> None:
|
||||
cmd = (
|
||||
f'{NEOFS_CLI_EXEC} --rpc-endpoint {NEOFS_ENDPOINT} --wallet {wallet_path} '
|
||||
f'container set-eacl --cid {cid} --table {eacl_table_path} --config {WALLET_CONFIG} --await'
|
||||
)
|
||||
_cmd_run(cmd)
|
||||
cli = NeofsCli(config=WALLET_CONFIG, timeout=60)
|
||||
cli.container.set_eacl(wallet=wallet_path, rpc_endpoint=NEOFS_ENDPOINT, cid=cid, table=eacl_table_path,
|
||||
await_mode=True)
|
||||
|
||||
|
||||
def _encode_cid_for_eacl(cid: str) -> str:
|
||||
|
@ -65,13 +121,9 @@ def _encode_cid_for_eacl(cid: str) -> str:
|
|||
return base64.b64encode(cid_base58).decode("utf-8")
|
||||
|
||||
|
||||
@keyword('Create eACL')
|
||||
def create_eacl(cid: str, rules_list: list) -> str:
|
||||
def create_eacl(cid: str, rules_list: List[EACLRule]) -> str:
|
||||
table_file_path = f"{os.getcwd()}/{ASSETS_DIR}/eacl_table_{str(uuid.uuid4())}.json"
|
||||
rules = " ".join(f"--rule '{rule}'" for rule in rules_list)
|
||||
|
||||
cmd = f"{NEOFS_CLI_EXEC} acl extended create --cid {cid} {rules} --out {table_file_path}"
|
||||
_cmd_run(cmd)
|
||||
NeofsCli().acl.extended_create(cid=cid, out=table_file_path, rule=rules_list)
|
||||
|
||||
with open(table_file_path, 'r') as file:
|
||||
table_data = file.read()
|
||||
|
@ -80,11 +132,10 @@ def create_eacl(cid: str, rules_list: list) -> str:
|
|||
return table_file_path
|
||||
|
||||
|
||||
@keyword('Form BearerToken File')
|
||||
def form_bearertoken_file(wif: str, cid: str, eacl_records: list) -> str:
|
||||
def form_bearertoken_file(wif: str, cid: str, eacl_rule_list: List[Union[EACLRule, EACLPubKey]]) -> str:
|
||||
"""
|
||||
This function fetches eACL for given <cid> on behalf of <wif>,
|
||||
then extends it with filters taken from <eacl_records>, signs
|
||||
then extends it with filters taken from <eacl_rules>, signs
|
||||
with bearer token and writes to file
|
||||
"""
|
||||
enc_cid = _encode_cid_for_eacl(cid)
|
||||
|
@ -93,8 +144,7 @@ def form_bearertoken_file(wif: str, cid: str, eacl_records: list) -> str:
|
|||
eacl = get_eacl(wif, cid)
|
||||
json_eacl = dict()
|
||||
if eacl:
|
||||
eacl = eacl.replace('eACL: ', '')
|
||||
eacl = eacl.split('Signature')[0]
|
||||
eacl = eacl.replace('eACL: ', '').split('Signature')[0]
|
||||
json_eacl = json.loads(eacl)
|
||||
logger.info(json_eacl)
|
||||
eacl_result = {
|
||||
|
@ -117,32 +167,28 @@ def form_bearertoken_file(wif: str, cid: str, eacl_records: list) -> str:
|
|||
}
|
||||
}
|
||||
|
||||
if not eacl_records:
|
||||
raise (f"Got empty eacl_records list: {eacl_records}")
|
||||
for record in eacl_records:
|
||||
assert eacl_rules, 'Got empty eacl_records list'
|
||||
for rule in eacl_rule_list:
|
||||
op_data = {
|
||||
"operation": record['Operation'],
|
||||
"action": record['Access'],
|
||||
"filters": [],
|
||||
"operation": rule.operation.value.upper(),
|
||||
"action": rule.access.value.upper(),
|
||||
"filters": rule.filters or [],
|
||||
"targets": []
|
||||
}
|
||||
|
||||
if Role(record['Role']):
|
||||
if isinstance(rule.role, EACLRole):
|
||||
op_data['targets'] = [
|
||||
{
|
||||
"role": record['Role']
|
||||
"role": rule.role.value.upper()
|
||||
}
|
||||
]
|
||||
else:
|
||||
elif isinstance(rule.role, EACLPubKey):
|
||||
op_data['targets'] = [
|
||||
{
|
||||
"keys": [record['Role']]
|
||||
'keys': rule.role.keys
|
||||
}
|
||||
]
|
||||
|
||||
if 'Filters' in record.keys():
|
||||
op_data["filters"].append(record['Filters'])
|
||||
|
||||
eacl_result["body"]["eaclTable"]["records"].append(op_data)
|
||||
|
||||
# Add records from current eACL
|
||||
|
@ -158,7 +204,6 @@ def form_bearertoken_file(wif: str, cid: str, eacl_records: list) -> str:
|
|||
return file_path
|
||||
|
||||
|
||||
@keyword('EACL Rules')
|
||||
def eacl_rules(access: str, verbs: list, user: str) -> list[str]:
|
||||
"""
|
||||
This function creates a list of eACL rules.
|
||||
|
@ -188,3 +233,9 @@ def sign_bearer_token(wallet_path: str, eacl_rules_file: str) -> None:
|
|||
f'--to {eacl_rules_file} --wallet {wallet_path} --config {WALLET_CONFIG} --json'
|
||||
)
|
||||
_cmd_run(cmd)
|
||||
|
||||
|
||||
@allure.title('Wait for eACL cache expired')
|
||||
def wait_for_cache_expired():
|
||||
sleep(NEOFS_CONTRACT_CACHE_TIMEOUT)
|
||||
return
|
||||
|
|
47
robot/resources/lib/python_keywords/cli/acl.py
Normal file
47
robot/resources/lib/python_keywords/cli/acl.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
from typing import Optional
|
||||
|
||||
from .cli_command import NeofsCliCommandBase
|
||||
|
||||
|
||||
class NeofsCliACL(NeofsCliCommandBase):
|
||||
def extended_create(self, cid: str, out: str, file: Optional[str] = None, rule: Optional[list] = None) -> str:
|
||||
|
||||
"""Create extended ACL from the text representation.
|
||||
|
||||
Rule consist of these blocks: <action> <operation> [<filter1> ...] [<target1> ...]
|
||||
Action is 'allow' or 'deny'.
|
||||
Operation is an object service verb: 'get', 'head', 'put', 'search', 'delete', 'getrange', or 'getrangehash'.
|
||||
|
||||
Filter consists of <typ>:<key><match><value>
|
||||
Typ is 'obj' for object applied filter or 'req' for request applied filter.
|
||||
Key is a valid unicode string corresponding to object or request header key.
|
||||
Well-known system object headers start with '$Object:' prefix.
|
||||
User defined headers start without prefix.
|
||||
Read more about filter keys at:
|
||||
http://github.com/nspcc-dev/neofs-api/blob/master/proto-docs/acl.md#message-eaclrecordfilter
|
||||
Match is '=' for matching and '!=' for non-matching filter.
|
||||
Value is a valid unicode string corresponding to object or request header value.
|
||||
|
||||
Target is
|
||||
'user' for container owner,
|
||||
'system' for Storage nodes in container and Inner Ring nodes,
|
||||
'others' for all other request senders,
|
||||
'pubkey:<key1>,<key2>,...' for exact request sender, where <key> is a hex-encoded 33-byte public key.
|
||||
|
||||
When both '--rule' and '--file' arguments are used, '--rule' records will be placed higher in resulting
|
||||
extended ACL table.
|
||||
|
||||
Args:
|
||||
cid: Container ID
|
||||
file: Read list of extended ACL table records from from text file
|
||||
out: Save JSON formatted extended ACL table in file
|
||||
rule: Extended ACL table record to apply
|
||||
|
||||
Returns:
|
||||
str: Command string
|
||||
|
||||
"""
|
||||
return self._execute(
|
||||
'acl extended create',
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self']}
|
||||
)
|
|
@ -3,6 +3,7 @@ from typing import Optional
|
|||
from common import NEOFS_CLI_EXEC
|
||||
|
||||
from .accounting import NeofsCliAccounting
|
||||
from .acl import NeofsCliACL
|
||||
from .cli_command import NeofsCliCommandBase
|
||||
from .container import NeofsCliContainer
|
||||
from .object import NeofsCliObject
|
||||
|
@ -12,6 +13,7 @@ class NeofsCli:
|
|||
neofs_cli_exec_path: Optional[str] = None
|
||||
config: Optional[str] = None
|
||||
accounting: Optional[NeofsCliAccounting] = None
|
||||
acl: Optional[NeofsCliACL] = None
|
||||
container: Optional[NeofsCliContainer] = None
|
||||
object: Optional[NeofsCliObject] = None
|
||||
|
||||
|
@ -19,6 +21,7 @@ class NeofsCli:
|
|||
self.config = config # config(str): config file (default is $HOME/.config/neofs-cli/config.yaml)
|
||||
self.neofs_cli_exec_path = neofs_cli_exec_path or NEOFS_CLI_EXEC
|
||||
self.accounting = NeofsCliAccounting(self.neofs_cli_exec_path, timeout=timeout, config=config)
|
||||
self.acl = NeofsCliACL(self.neofs_cli_exec_path, timeout=timeout, config=config)
|
||||
self.container = NeofsCliContainer(self.neofs_cli_exec_path, timeout=timeout, config=config)
|
||||
self.object = NeofsCliObject(self.neofs_cli_exec_path, timeout=timeout, config=config)
|
||||
|
||||
|
|
|
@ -24,13 +24,21 @@ class NeofsCliCommandBase:
|
|||
continue
|
||||
if isinstance(value, bool):
|
||||
param_str.append(f'--{param}')
|
||||
elif isinstance(value, int):
|
||||
param_str.append(f'--{param} {value}')
|
||||
elif isinstance(value, list):
|
||||
param_str.append(f'--{param} \'{",".join(value)}\'')
|
||||
for value_item in value:
|
||||
val_str = str(value_item).replace("'", "\\'")
|
||||
param_str.append(f"--{param} '{val_str}'")
|
||||
elif isinstance(value, dict):
|
||||
param_str.append(f'--{param} \'{",".join(f"{key}={val}" for key, val in value.items())}\'')
|
||||
else:
|
||||
value_str = str(value).replace("'", "\\'")
|
||||
param_str.append(f"--{param} '{value_str}'")
|
||||
if "'" in str(value):
|
||||
value_str = str(value).replace('"', '\\"')
|
||||
param_str.append(f'--{param} "{value_str}"')
|
||||
else:
|
||||
param_str.append(f"--{param} '{value}'")
|
||||
|
||||
param_str = ' '.join(param_str)
|
||||
|
||||
return f'{self.neofs_cli_exec} {self.__base_params} {command or ""} {param_str}'
|
||||
|
|
|
@ -8,7 +8,7 @@ class NeofsCliContainer(NeofsCliCommandBase):
|
|||
basic_acl: Optional[str] = None, await_mode: bool = False, disable_timestamp: bool = False,
|
||||
name: Optional[str] = None, nonce: Optional[str] = None, policy: Optional[str] = None,
|
||||
session: Optional[str] = None, subnet: Optional[str] = None, ttl: Optional[int] = None,
|
||||
xhdr: Optional[list] = None) -> str:
|
||||
xhdr: Optional[dict] = None) -> str:
|
||||
"""Create a new container and register it in the NeoFS.
|
||||
It will be stored in the sidechain when the Inner Ring accepts it.
|
||||
|
||||
|
@ -39,7 +39,8 @@ class NeofsCliContainer(NeofsCliCommandBase):
|
|||
)
|
||||
|
||||
def delete(self, rpc_endpoint: str, wallet: str, cid: str, address: Optional[str] = None, await_mode: bool = False,
|
||||
session: Optional[str] = None, ttl: Optional[int] = None, xhdr: Optional[list] = None) -> str:
|
||||
session: Optional[str] = None, ttl: Optional[int] = None, xhdr: Optional[dict] = None,
|
||||
force: bool = False) -> str:
|
||||
"""Delete an existing container.
|
||||
Only the owner of the container has permission to remove the container.
|
||||
|
||||
|
@ -47,6 +48,7 @@ class NeofsCliContainer(NeofsCliCommandBase):
|
|||
address: address of wallet account
|
||||
await_mode: block execution until container is removed
|
||||
cid: container ID
|
||||
force: do not check whether container contains locks and remove immediately
|
||||
rpc_endpoint: remote node address (as 'multiaddr' or '<host>:<port>')
|
||||
session: path to a JSON-encoded container session token
|
||||
ttl: TTL value in request meta header (default 2)
|
||||
|
|
|
@ -6,7 +6,7 @@ from .cli_command import NeofsCliCommandBase
|
|||
class NeofsCliObject(NeofsCliCommandBase):
|
||||
def delete(self, rpc_endpoint: str, wallet: str, cid: str, oid: str, address: Optional[str] = None,
|
||||
bearer: Optional[str] = None, session: Optional[str] = None, ttl: Optional[int] = None,
|
||||
xhdr: Optional[list] = None, **params) -> str:
|
||||
xhdr: Optional[dict] = None) -> str:
|
||||
"""Delete object from NeoFS
|
||||
|
||||
Args:
|
||||
|
@ -26,13 +26,13 @@ class NeofsCliObject(NeofsCliCommandBase):
|
|||
"""
|
||||
return self._execute(
|
||||
'object delete',
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self', 'params']}
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self']}
|
||||
)
|
||||
|
||||
def get(self, rpc_endpoint: str, wallet: str, cid: str, oid: str, address: Optional[str] = None,
|
||||
bearer: Optional[str] = None, file: Optional[str] = None,
|
||||
header: Optional[str] = None, no_progress: bool = False, raw: bool = False,
|
||||
session: Optional[str] = None, ttl: Optional[int] = None, xhdr: Optional[list] = None, **params) -> str:
|
||||
session: Optional[str] = None, ttl: Optional[int] = None, xhdr: Optional[dict] = None) -> str:
|
||||
"""Get object from NeoFS
|
||||
|
||||
Args:
|
||||
|
@ -56,13 +56,12 @@ class NeofsCliObject(NeofsCliCommandBase):
|
|||
"""
|
||||
return self._execute(
|
||||
'object get',
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self', 'params']}
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self']}
|
||||
)
|
||||
|
||||
def hash(self, rpc_endpoint: str, wallet: str, cid: str, oid: str, address: Optional[str] = None,
|
||||
bearer: Optional[str] = None, range: Optional[str] = None, salt: Optional[str] = None,
|
||||
ttl: Optional[int] = None, hash_type: Optional[str] = None, xhdr: Optional[list] = None,
|
||||
**params) -> str:
|
||||
ttl: Optional[int] = None, hash_type: Optional[str] = None, xhdr: Optional[dict] = None) -> str:
|
||||
"""Get object hash
|
||||
|
||||
Args:
|
||||
|
@ -90,7 +89,7 @@ class NeofsCliObject(NeofsCliCommandBase):
|
|||
def head(self, rpc_endpoint: str, wallet: str, cid: str, oid: str, address: Optional[str] = None,
|
||||
bearer: Optional[str] = None, file: Optional[str] = None,
|
||||
json_mode: bool = False, main_only: bool = False, proto: bool = False, raw: bool = False,
|
||||
session: Optional[str] = None, ttl: Optional[int] = None, xhdr: Optional[list] = None, **params) -> str:
|
||||
session: Optional[str] = None, ttl: Optional[int] = None, xhdr: Optional[dict] = None) -> str:
|
||||
"""Get object header
|
||||
|
||||
Args:
|
||||
|
@ -116,12 +115,12 @@ class NeofsCliObject(NeofsCliCommandBase):
|
|||
"""
|
||||
return self._execute(
|
||||
'object head',
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self', 'params']}
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self']}
|
||||
)
|
||||
|
||||
def lock(self, rpc_endpoint: str, wallet: str, cid: str, oid: str, lifetime: int, address: Optional[str] = None,
|
||||
bearer: Optional[str] = None, session: Optional[str] = None,
|
||||
ttl: Optional[int] = None, xhdr: Optional[list] = None, **params) -> str:
|
||||
bearer: Optional[str] = None, session: Optional[str] = None, ttl: Optional[int] = None,
|
||||
xhdr: Optional[dict] = None) -> str:
|
||||
"""Lock object in container
|
||||
|
||||
Args:
|
||||
|
@ -143,14 +142,14 @@ class NeofsCliObject(NeofsCliCommandBase):
|
|||
"""
|
||||
return self._execute(
|
||||
'object lock',
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self', 'params']}
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self']}
|
||||
)
|
||||
|
||||
def put(self, rpc_endpoint: str, wallet: str, cid: str, file: str, address: Optional[str] = None,
|
||||
attributes: Optional[dict] = None, bearer: Optional[str] = None, disable_filename: bool = False,
|
||||
disable_timestamp: bool = False, expire_at: Optional[int] = None, no_progress: bool = False,
|
||||
notify: Optional[str] = None, session: Optional[str] = None, ttl: Optional[int] = None,
|
||||
xhdr: Optional[list] = None, **params) -> str:
|
||||
xhdr: Optional[dict] = None) -> str:
|
||||
"""Put object to NeoFS
|
||||
|
||||
Args:
|
||||
|
@ -176,12 +175,12 @@ class NeofsCliObject(NeofsCliCommandBase):
|
|||
"""
|
||||
return self._execute(
|
||||
'object put',
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self', 'params']}
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self']}
|
||||
)
|
||||
|
||||
def range(self, rpc_endpoint: str, wallet: str, cid: str, oid: str, range: str, address: Optional[str] = None,
|
||||
bearer: Optional[str] = None, file: Optional[str] = None, json_mode: bool = False, raw: bool = False,
|
||||
session: Optional[str] = None, ttl: Optional[int] = None, xhdr: Optional[list] = None, **params) -> str:
|
||||
session: Optional[str] = None, ttl: Optional[int] = None, xhdr: Optional[dict] = None) -> str:
|
||||
"""Get payload range data of an object
|
||||
|
||||
Args:
|
||||
|
@ -206,13 +205,13 @@ class NeofsCliObject(NeofsCliCommandBase):
|
|||
"""
|
||||
return self._execute(
|
||||
'object range',
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self', 'params']}
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self']}
|
||||
)
|
||||
|
||||
def search(self, rpc_endpoint: str, wallet: str, cid: str, address: Optional[str] = None,
|
||||
bearer: Optional[str] = None, filters: Optional[list] = None, oid: Optional[str] = None,
|
||||
phy: bool = False, root: bool = False, session: Optional[str] = None, ttl: Optional[int] = None,
|
||||
xhdr: Optional[list] = None, **params) -> str:
|
||||
xhdr: Optional[dict] = None) -> str:
|
||||
"""Search object
|
||||
|
||||
Args:
|
||||
|
@ -236,5 +235,5 @@ class NeofsCliObject(NeofsCliCommandBase):
|
|||
"""
|
||||
return self._execute(
|
||||
'object search',
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self', 'params']}
|
||||
**{param: param_value for param, param_value in locals().items() if param not in ['self']}
|
||||
)
|
||||
|
|
|
@ -130,17 +130,18 @@ def get_container(wallet: str, cid: str, json_mode: bool = True) -> Union[dict,
|
|||
@keyword('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) -> None:
|
||||
def delete_container(wallet: str, cid: str, force: bool = False) -> None:
|
||||
"""
|
||||
A wrapper for `neofs-cli container delete` call.
|
||||
Args:
|
||||
wallet (str): path to a wallet on whose behalf we delete the container
|
||||
cid (str): ID of the container to delete
|
||||
force (bool): do not check whether container contains locks and remove immediately
|
||||
This function doesn't return anything.
|
||||
"""
|
||||
|
||||
cli = NeofsCli(config=WALLET_CONFIG)
|
||||
cli.container.delete(wallet=wallet, cid=cid, rpc_endpoint=NEOFS_ENDPOINT)
|
||||
cli.container.delete(wallet=wallet, cid=cid, rpc_endpoint=NEOFS_ENDPOINT, force=force)
|
||||
|
||||
|
||||
def _parse_cid(output: str) -> str:
|
||||
|
|
70
robot/resources/lib/python_keywords/container_access.py
Normal file
70
robot/resources/lib/python_keywords/container_access.py
Normal file
|
@ -0,0 +1,70 @@
|
|||
from typing import List, Optional
|
||||
|
||||
from acl import EACLOperation
|
||||
from python_keywords.object_access import (can_get_object, can_put_object, can_delete_object, can_get_head_object,
|
||||
can_get_range_hash_of_object, can_get_range_of_object, can_search_object)
|
||||
|
||||
|
||||
def check_full_access_to_container(wallet: str, cid: str, oid: str, file_name: str,
|
||||
bearer: Optional[str] = None, wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None):
|
||||
assert can_put_object(wallet, cid, file_name, bearer, wallet_config, xhdr)
|
||||
assert can_get_head_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert can_get_range_of_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert can_get_range_hash_of_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert can_search_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert can_get_object(wallet, cid, oid, file_name, bearer, wallet_config, xhdr)
|
||||
assert can_delete_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
|
||||
|
||||
def check_no_access_to_container(wallet: str, cid: str, oid: str, file_name: str,
|
||||
bearer: Optional[str] = None, wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None):
|
||||
assert not can_put_object(wallet, cid, file_name, bearer, wallet_config, xhdr)
|
||||
assert not can_get_head_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert not can_get_range_of_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert not can_get_range_hash_of_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert not can_search_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
assert not can_get_object(wallet, cid, oid, file_name, bearer, wallet_config, xhdr)
|
||||
assert not can_delete_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
|
||||
|
||||
def check_custom_access_to_container(wallet: str, cid: str, oid: str, file_name: str,
|
||||
deny_operations: Optional[List[EACLOperation]] = None,
|
||||
ignore_operations: Optional[List[EACLOperation]] = None,
|
||||
bearer: Optional[str] = None, wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None):
|
||||
deny_operations = [op.value for op in deny_operations or []]
|
||||
ignore_operations = [op.value for op in ignore_operations or []]
|
||||
checks: dict = {}
|
||||
if EACLOperation.PUT.value not in ignore_operations:
|
||||
checks[EACLOperation.PUT.value] = can_put_object(wallet, cid, file_name, bearer, wallet_config, xhdr)
|
||||
if EACLOperation.HEAD.value not in ignore_operations:
|
||||
checks[EACLOperation.HEAD.value] = can_get_head_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
if EACLOperation.GET_RANGE.value not in ignore_operations:
|
||||
checks[EACLOperation.GET_RANGE.value] = can_get_range_of_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
if EACLOperation.GET_RANGE_HASH.value not in ignore_operations:
|
||||
checks[EACLOperation.GET_RANGE_HASH.value] = can_get_range_hash_of_object(wallet, cid, oid, bearer,
|
||||
wallet_config, xhdr)
|
||||
if EACLOperation.SEARCH.value not in ignore_operations:
|
||||
checks[EACLOperation.SEARCH.value] = can_search_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
if EACLOperation.GET.value not in ignore_operations:
|
||||
checks[EACLOperation.GET.value] = can_get_object(wallet, cid, oid, file_name, bearer, wallet_config, xhdr)
|
||||
if EACLOperation.DELETE.value not in ignore_operations:
|
||||
checks[EACLOperation.DELETE.value] = can_delete_object(wallet, cid, oid, bearer, wallet_config, xhdr)
|
||||
|
||||
failed_checks = (
|
||||
[f'allowed {action} failed' for action, success in checks.items() if
|
||||
not success and action not in deny_operations] +
|
||||
[f'denied {action} succeeded' for action, success in checks.items() if
|
||||
success and action in deny_operations])
|
||||
|
||||
assert not failed_checks, ", ".join(failed_checks)
|
||||
|
||||
|
||||
def check_read_only_container(wallet: str, cid: str, oid: str, file_name: str,
|
||||
bearer: Optional[str] = None, wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None):
|
||||
return check_custom_access_to_container(wallet, cid, oid, file_name,
|
||||
deny_operations=[EACLOperation.PUT, EACLOperation.DELETE],
|
||||
bearer=bearer, wallet_config=wallet_config, xhdr=xhdr)
|
|
@ -21,8 +21,8 @@ ROBOT_AUTO_KEYWORDS = False
|
|||
|
||||
@keyword('Get object')
|
||||
def get_object(wallet: str, cid: str, oid: str, bearer_token: Optional[str] = None, write_object: str = "",
|
||||
endpoint: str = "", options: Optional[dict] = None, wallet_config: str = WALLET_CONFIG,
|
||||
no_progress: bool = True):
|
||||
endpoint: str = "", xhdr: Optional[dict] = None, wallet_config: Optional[str] = None,
|
||||
no_progress: bool = True) -> str:
|
||||
"""
|
||||
GET from NeoFS.
|
||||
|
||||
|
@ -35,11 +35,12 @@ def get_object(wallet: str, cid: str, oid: str, bearer_token: Optional[str] = No
|
|||
endpoint (optional, str): NeoFS endpoint to send request to, appends to `--rpc-endpoint` key
|
||||
wallet_config(optional, str): path to the wallet config
|
||||
no_progress(optional, bool): do not show progress bar
|
||||
options (optional, str): any options which `neofs-cli object get` accepts
|
||||
xhdr (optional, dict): Request X-Headers in form of Key=Value
|
||||
Returns:
|
||||
(str): path to downloaded file
|
||||
"""
|
||||
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
if not write_object:
|
||||
write_object = str(uuid.uuid4())
|
||||
file_path = f"{ASSETS_DIR}/{write_object}"
|
||||
|
@ -49,7 +50,7 @@ def get_object(wallet: str, cid: str, oid: str, bearer_token: Optional[str] = No
|
|||
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
cli.object.get(rpc_endpoint=endpoint, wallet=wallet, cid=cid, oid=oid, file=file_path,
|
||||
bearer=bearer_token, no_progress=no_progress, **options or {})
|
||||
bearer=bearer_token, no_progress=no_progress, xhdr=xhdr)
|
||||
|
||||
return file_path
|
||||
|
||||
|
@ -57,7 +58,7 @@ def get_object(wallet: str, cid: str, oid: str, bearer_token: Optional[str] = No
|
|||
# TODO: make `bearer_token` optional
|
||||
@keyword('Get Range Hash')
|
||||
def get_range_hash(wallet: str, cid: str, oid: str, bearer_token: str, range_cut: str,
|
||||
wallet_config: str = WALLET_CONFIG, options: Optional[dict] = None):
|
||||
wallet_config: Optional[str] = None, xhdr: Optional[dict] = None):
|
||||
"""
|
||||
GETRANGEHASH of given Object.
|
||||
|
||||
|
@ -69,23 +70,24 @@ def get_range_hash(wallet: str, cid: str, oid: str, bearer_token: str, range_cut
|
|||
value to pass to the `--range` parameter
|
||||
bearer_token (optional, str): path to Bearer Token file, appends to `--bearer` key
|
||||
wallet_config(optional, str): path to the wallet config
|
||||
options (optional, str): any options which `neofs-cli object hash` accepts
|
||||
xhdr (optional, dict): Request X-Headers in form of Key=Value
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
output = cli.object.hash(rpc_endpoint=NEOFS_ENDPOINT, wallet=wallet, cid=cid, oid=oid, range=range_cut,
|
||||
bearer=bearer_token, **options or {})
|
||||
bearer=bearer_token, xhdr=xhdr)
|
||||
|
||||
# cutting off output about range offset and length
|
||||
return output.split(':')[1].strip()
|
||||
|
||||
|
||||
@keyword('Put object')
|
||||
def put_object(wallet: str, path: str, cid: str, bearer: str = "", user_headers: Optional[dict] = None,
|
||||
endpoint: str = "", wallet_config: str = WALLET_CONFIG, expire_at: Optional[int] = None,
|
||||
no_progress: bool = True, options: Optional[dict] = None):
|
||||
def put_object(wallet: str, path: str, cid: str, bearer: str = "", attributes: Optional[dict] = None,
|
||||
xhdr: Optional[dict] = None, endpoint: str = "", wallet_config: Optional[str] = None,
|
||||
expire_at: Optional[int] = None, no_progress: bool = True):
|
||||
"""
|
||||
PUT of given file.
|
||||
|
||||
|
@ -94,24 +96,24 @@ def put_object(wallet: str, path: str, cid: str, bearer: str = "", user_headers:
|
|||
path (str): path to file to be PUT
|
||||
cid (str): ID of Container where we get the Object from
|
||||
bearer (optional, str): path to Bearer Token file, appends to `--bearer` key
|
||||
user_headers (optional, dict): Object attributes, append to `--attributes` key
|
||||
attributes (optional, str): User attributes in form of Key1=Value1,Key2=Value2
|
||||
endpoint(optional, str): NeoFS endpoint to send request to
|
||||
wallet_config(optional, str): path to the wallet config
|
||||
no_progress(optional, bool): do not show progress bar
|
||||
options (optional, str): any options which `neofs-cli object put` accepts
|
||||
expire_at (optional, int): Last epoch in the life of the object
|
||||
xhdr (optional, dict): Request X-Headers in form of Key=Value
|
||||
Returns:
|
||||
(str): ID of uploaded Object
|
||||
"""
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
if not endpoint:
|
||||
endpoint = random.sample(NEOFS_NETMAP, 1)[0]
|
||||
if not endpoint:
|
||||
logger.info(f'---DEB:\n{NEOFS_NETMAP}')
|
||||
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
output = cli.object.put(rpc_endpoint=endpoint, wallet=wallet, file=path, cid=cid, bearer=bearer,
|
||||
expire_at=expire_at, no_progress=no_progress,
|
||||
attributes=user_headers or {}, **options or {})
|
||||
output = cli.object.put(rpc_endpoint=endpoint, wallet=wallet, file=path, cid=cid, attributes=attributes,
|
||||
bearer=bearer, expire_at=expire_at, no_progress=no_progress, xhdr=xhdr)
|
||||
|
||||
# splitting CLI output to lines and taking the penultimate line
|
||||
id_str = output.strip().split('\n')[-2]
|
||||
|
@ -120,8 +122,8 @@ def put_object(wallet: str, path: str, cid: str, bearer: str = "", user_headers:
|
|||
|
||||
|
||||
@keyword('Delete object')
|
||||
def delete_object(wallet: str, cid: str, oid: str, bearer: str = "", wallet_config: str = WALLET_CONFIG,
|
||||
options: Optional[dict] = None):
|
||||
def delete_object(wallet: str, cid: str, oid: str, bearer: str = "", wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None):
|
||||
"""
|
||||
DELETE an Object.
|
||||
|
||||
|
@ -131,14 +133,15 @@ def delete_object(wallet: str, cid: str, oid: str, bearer: str = "", wallet_conf
|
|||
oid (str): ID of Object we are going to delete
|
||||
bearer (optional, str): path to Bearer Token file, appends to `--bearer` key
|
||||
wallet_config(optional, str): path to the wallet config
|
||||
options (optional, dict): any options which `neofs-cli object delete` accepts
|
||||
xhdr (optional, dict): Request X-Headers in form of Key=Value
|
||||
Returns:
|
||||
(str): Tombstone ID
|
||||
"""
|
||||
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
output = cli.object.delete(rpc_endpoint=NEOFS_ENDPOINT, wallet=wallet, cid=cid, oid=oid, bearer=bearer,
|
||||
**options or {})
|
||||
xhdr=xhdr)
|
||||
|
||||
id_str = output.split('\n')[1]
|
||||
tombstone = id_str.split(':')[1]
|
||||
|
@ -146,8 +149,8 @@ def delete_object(wallet: str, cid: str, oid: str, bearer: str = "", wallet_conf
|
|||
|
||||
|
||||
@keyword('Get Range')
|
||||
def get_range(wallet: str, cid: str, oid: str, range_cut: str, wallet_config: str = WALLET_CONFIG,
|
||||
bearer: str = "", options: Optional[dict] = None):
|
||||
def get_range(wallet: str, cid: str, oid: str, range_cut: str, wallet_config: Optional[str] = None,
|
||||
bearer: str = "", xhdr: Optional[dict] = None):
|
||||
"""
|
||||
GETRANGE an Object.
|
||||
|
||||
|
@ -158,15 +161,16 @@ def get_range(wallet: str, cid: str, oid: str, range_cut: str, wallet_config: st
|
|||
range_cut (str): range to take data from in the form offset:length
|
||||
bearer (optional, str): path to Bearer Token file, appends to `--bearer` key
|
||||
wallet_config(optional, str): path to the wallet config
|
||||
options (optional, dict): any options which `neofs-cli object range` accepts
|
||||
xhdr (optional, dict): Request X-Headers in form of Key=Value
|
||||
Returns:
|
||||
(str, bytes) - path to the file with range content and content of this file as bytes
|
||||
"""
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
range_file = f"{ASSETS_DIR}/{uuid.uuid4()}"
|
||||
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
cli.object.range(rpc_endpoint=NEOFS_ENDPOINT, wallet=wallet, cid=cid, oid=oid, range=range_cut, file=range_file,
|
||||
bearer=bearer, **options or {})
|
||||
bearer=bearer, xhdr=xhdr)
|
||||
|
||||
with open(range_file, 'rb') as fout:
|
||||
content = fout.read()
|
||||
|
@ -175,8 +179,8 @@ def get_range(wallet: str, cid: str, oid: str, range_cut: str, wallet_config: st
|
|||
|
||||
@keyword('Search object')
|
||||
def search_object(wallet: str, cid: str, bearer: str = "", filters: Optional[dict] = None,
|
||||
expected_objects_list: Optional[list] = None, wallet_config: str = WALLET_CONFIG,
|
||||
options: Optional[dict] = None):
|
||||
expected_objects_list: Optional[list] = None, wallet_config: Optional[str] = None,
|
||||
xhdr: Optional[dict] = None) -> list:
|
||||
"""
|
||||
SEARCH an Object.
|
||||
|
||||
|
@ -187,16 +191,16 @@ def search_object(wallet: str, cid: str, bearer: str = "", filters: Optional[dic
|
|||
filters (optional, dict): key=value pairs to filter Objects
|
||||
expected_objects_list (optional, list): a list of ObjectIDs to compare found Objects with
|
||||
wallet_config(optional, str): path to the wallet config
|
||||
options(optional, str): any other options which `neofs-cli object search` might accept
|
||||
xhdr (optional, dict): Request X-Headers in form of Key=Value
|
||||
Returns:
|
||||
(list): list of found ObjectIDs
|
||||
"""
|
||||
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
output = cli.object.search(
|
||||
rpc_endpoint=NEOFS_ENDPOINT, wallet=wallet, cid=cid, bearer=bearer,
|
||||
filters=[f'{filter_key} EQ {filter_val}' for filter_key, filter_val in filters.items()] if filters else None,
|
||||
**options or {})
|
||||
rpc_endpoint=NEOFS_ENDPOINT, wallet=wallet, cid=cid, bearer=bearer, xhdr=xhdr,
|
||||
filters=[f'{filter_key} EQ {filter_val}' for filter_key, filter_val in filters.items()] if filters else None)
|
||||
|
||||
found_objects = re.findall(r'(\w{43,44})', output)
|
||||
|
||||
|
@ -213,8 +217,8 @@ def search_object(wallet: str, cid: str, bearer: str = "", filters: Optional[dic
|
|||
|
||||
@keyword('Head object')
|
||||
def head_object(wallet: str, cid: str, oid: str, bearer_token: str = "",
|
||||
options: Optional[dict] = None, endpoint: str = None, json_output: bool = True,
|
||||
is_raw: bool = False, is_direct: bool = False, wallet_config: str = WALLET_CONFIG):
|
||||
xhdr: Optional[dict] = None, endpoint: str = None, json_output: bool = True,
|
||||
is_raw: bool = False, is_direct: bool = False, wallet_config: Optional[str] = None):
|
||||
"""
|
||||
HEAD an Object.
|
||||
|
||||
|
@ -223,7 +227,6 @@ def head_object(wallet: str, cid: str, oid: str, bearer_token: str = "",
|
|||
cid (str): ID of Container where we get the Object from
|
||||
oid (str): ObjectID to HEAD
|
||||
bearer_token (optional, str): path to Bearer Token file, appends to `--bearer` key
|
||||
options (optional, str): any options which `neofs-cli object head` accepts
|
||||
endpoint(optional, str): NeoFS endpoint to send request to
|
||||
json_output(optional, bool): return reponse in JSON format or not; this flag
|
||||
turns into `--json` key
|
||||
|
@ -232,6 +235,7 @@ def head_object(wallet: str, cid: str, oid: str, bearer_token: str = "",
|
|||
is_direct(optional, bool): send request directly to the node or not; this flag
|
||||
turns into `--ttl 1` key
|
||||
wallet_config(optional, str): path to the wallet config
|
||||
xhdr (optional, dict): Request X-Headers in form of Key=Value
|
||||
Returns:
|
||||
depending on the `json_output` parameter value, the function returns
|
||||
(dict): HEAD response in JSON format
|
||||
|
@ -239,10 +243,11 @@ def head_object(wallet: str, cid: str, oid: str, bearer_token: str = "",
|
|||
(str): HEAD response as a plain text
|
||||
"""
|
||||
|
||||
wallet_config = wallet_config or WALLET_CONFIG
|
||||
cli = NeofsCli(config=wallet_config)
|
||||
output = cli.object.head(rpc_endpoint=endpoint or NEOFS_ENDPOINT, wallet=wallet, cid=cid, oid=oid,
|
||||
bearer=bearer_token, json_mode=json_output, raw=is_raw,
|
||||
ttl=1 if is_direct else None, **options or {})
|
||||
ttl=1 if is_direct else None, xhdr=xhdr)
|
||||
|
||||
if not json_output:
|
||||
return output
|
||||
|
|
100
robot/resources/lib/python_keywords/object_access.py
Normal file
100
robot/resources/lib/python_keywords/object_access.py
Normal file
|
@ -0,0 +1,100 @@
|
|||
from typing import Optional
|
||||
|
||||
import allure
|
||||
|
||||
from grpc_responses import OBJECT_ACCESS_DENIED, error_matches_status
|
||||
from python_keywords.neofs_verbs import (delete_object, get_object, get_range, get_range_hash, head_object, put_object,
|
||||
search_object)
|
||||
from python_keywords.utility_keywords import get_file_hash
|
||||
|
||||
OPERATION_ERROR_TYPE = RuntimeError
|
||||
|
||||
|
||||
def can_get_object(wallet: str, cid: str, oid: str, file_name: str, bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None, xhdr: Optional[dict] = None
|
||||
) -> bool:
|
||||
with allure.step('Try get object from container'):
|
||||
try:
|
||||
got_file_path = get_object(wallet, cid, oid, bearer_token=bearer, wallet_config=wallet_config, xhdr=xhdr)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(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(wallet: str, cid: str, file_name: str, bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None, xhdr: Optional[dict] = None, attributes: Optional[dict] = None,
|
||||
) -> bool:
|
||||
with allure.step('Try put object to container'):
|
||||
try:
|
||||
put_object(wallet, file_name, cid, bearer=bearer, wallet_config=wallet_config, xhdr=xhdr,
|
||||
attributes=attributes)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(err, OBJECT_ACCESS_DENIED), f'Expected {err} to match {OBJECT_ACCESS_DENIED}'
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def can_delete_object(wallet: str, cid: str, oid: str, bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None, xhdr: Optional[dict] = None
|
||||
) -> bool:
|
||||
with allure.step('Try delete object from container'):
|
||||
try:
|
||||
delete_object(wallet, cid, oid, bearer=bearer, wallet_config=wallet_config, xhdr=xhdr)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(err, OBJECT_ACCESS_DENIED), f'Expected {err} to match {OBJECT_ACCESS_DENIED}'
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def can_get_head_object(wallet: str, cid: str, oid: str, bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None, xhdr: Optional[dict] = None
|
||||
) -> bool:
|
||||
with allure.step('Try get head of object'):
|
||||
try:
|
||||
head_object(wallet, cid, oid, bearer_token=bearer, wallet_config=wallet_config, xhdr=xhdr)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(err, OBJECT_ACCESS_DENIED), f'Expected {err} to match {OBJECT_ACCESS_DENIED}'
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def can_get_range_of_object(wallet: str, cid: str, oid: str, bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None, xhdr: Optional[dict] = None
|
||||
) -> bool:
|
||||
with allure.step('Try get range of object'):
|
||||
try:
|
||||
get_range(wallet, cid, oid, bearer=bearer, range_cut='0:10', wallet_config=wallet_config,
|
||||
xhdr=xhdr)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(err, OBJECT_ACCESS_DENIED), f'Expected {err} to match {OBJECT_ACCESS_DENIED}'
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def can_get_range_hash_of_object(wallet: str, cid: str, oid: str, bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None, xhdr: Optional[dict] = None
|
||||
) -> bool:
|
||||
with allure.step('Try get range hash of object'):
|
||||
try:
|
||||
get_range_hash(wallet, cid, oid, bearer_token=bearer, range_cut='0:10', wallet_config=wallet_config,
|
||||
xhdr=xhdr)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(err, OBJECT_ACCESS_DENIED), f'Expected {err} to match {OBJECT_ACCESS_DENIED}'
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def can_search_object(wallet: str, cid: str, oid: Optional[str] = None, bearer: Optional[str] = None,
|
||||
wallet_config: Optional[str] = None, xhdr: Optional[dict] = None
|
||||
) -> bool:
|
||||
with allure.step('Try search object in container'):
|
||||
try:
|
||||
oids = search_object(wallet, cid, bearer=bearer, wallet_config=wallet_config, xhdr=xhdr)
|
||||
except OPERATION_ERROR_TYPE as err:
|
||||
assert error_matches_status(err, OBJECT_ACCESS_DENIED), f'Expected {err} to match {OBJECT_ACCESS_DENIED}'
|
||||
return False
|
||||
if oid:
|
||||
return oid in oids
|
||||
return True
|
|
@ -11,11 +11,8 @@ ROBOT_AUTO_KEYWORDS = False
|
|||
|
||||
|
||||
@keyword('Verify Head Tombstone')
|
||||
def verify_head_tombstone(wallet_path: str, cid: str, oid_ts: str, oid: str,
|
||||
bearer: str = "", options: str = ""):
|
||||
header = neofs_verbs.head_object(wallet_path, cid, oid_ts,
|
||||
bearer_token=bearer,
|
||||
options=options)
|
||||
def verify_head_tombstone(wallet_path: str, cid: str, oid_ts: str, oid: str):
|
||||
header = neofs_verbs.head_object(wallet_path, cid, oid_ts)
|
||||
header = header['header']
|
||||
|
||||
BuiltIn().should_be_equal(header["containerID"], cid,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue