2021-09-10 12:44:40 +00:00
|
|
|
#!/usr/bin/python3.8
|
|
|
|
|
2022-06-09 13:08:11 +00:00
|
|
|
import base64
|
2021-09-10 12:44:40 +00:00
|
|
|
import json
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import uuid
|
2022-06-09 13:08:11 +00:00
|
|
|
from enum import Enum, auto
|
2021-09-10 12:44:40 +00:00
|
|
|
|
|
|
|
import base58
|
|
|
|
from cli_helpers import _cmd_run
|
2022-06-13 20:33:09 +00:00
|
|
|
from common import ASSETS_DIR, NEOFS_ENDPOINT, WALLET_CONFIG
|
2022-07-22 07:04:26 +00:00
|
|
|
from data_formatters import pub_key_hex
|
2021-09-10 12:44:40 +00:00
|
|
|
from robot.api import logger
|
2022-06-09 13:08:11 +00:00
|
|
|
from robot.api.deco import keyword
|
2021-09-10 12:44:40 +00:00
|
|
|
|
|
|
|
"""
|
|
|
|
Robot Keywords and helper functions for work with NeoFS ACL.
|
|
|
|
"""
|
|
|
|
|
|
|
|
ROBOT_AUTO_KEYWORDS = False
|
|
|
|
|
|
|
|
# path to neofs-cli executable
|
|
|
|
NEOFS_CLI_EXEC = os.getenv('NEOFS_CLI_EXEC', 'neofs-cli')
|
|
|
|
EACL_LIFETIME = 100500
|
|
|
|
|
2022-06-09 13:08:11 +00:00
|
|
|
|
2021-09-10 12:44:40 +00:00
|
|
|
class AutoName(Enum):
|
|
|
|
def _generate_next_value_(name, start, count, last_values):
|
|
|
|
return name
|
|
|
|
|
2022-06-09 13:08:11 +00:00
|
|
|
|
2021-09-10 12:44:40 +00:00
|
|
|
class Role(AutoName):
|
|
|
|
USER = auto()
|
|
|
|
SYSTEM = auto()
|
|
|
|
OTHERS = auto()
|
|
|
|
|
|
|
|
|
|
|
|
@keyword('Get eACL')
|
2022-07-18 10:19:05 +00:00
|
|
|
def get_eacl(wallet_path: str, cid: str):
|
2021-09-10 12:44:40 +00:00
|
|
|
cmd = (
|
2022-07-18 10:19:05 +00:00
|
|
|
f'{NEOFS_CLI_EXEC} --rpc-endpoint {NEOFS_ENDPOINT} --wallet {wallet_path} '
|
2022-06-13 20:33:09 +00:00
|
|
|
f'container get-eacl --cid {cid} --config {WALLET_CONFIG}'
|
2021-09-10 12:44:40 +00:00
|
|
|
)
|
|
|
|
try:
|
|
|
|
output = _cmd_run(cmd)
|
|
|
|
if re.search(r'extended ACL table is not set for this container', output):
|
|
|
|
return None
|
|
|
|
return output
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
|
|
@keyword('Set eACL')
|
2022-07-18 10:19:05 +00:00
|
|
|
def set_eacl(wallet_path: str, cid: str, eacl_table_path: str):
|
2021-09-10 12:44:40 +00:00
|
|
|
cmd = (
|
2022-07-18 10:19:05 +00:00
|
|
|
f'{NEOFS_CLI_EXEC} --rpc-endpoint {NEOFS_ENDPOINT} --wallet {wallet_path} '
|
2022-06-13 20:33:09 +00:00
|
|
|
f'container set-eacl --cid {cid} --table {eacl_table_path} --config {WALLET_CONFIG} --await'
|
2021-09-10 12:44:40 +00:00
|
|
|
)
|
|
|
|
_cmd_run(cmd)
|
|
|
|
|
|
|
|
|
|
|
|
def _encode_cid_for_eacl(cid: str) -> str:
|
|
|
|
cid_base58 = base58.b58decode(cid)
|
|
|
|
return base64.b64encode(cid_base58).decode("utf-8")
|
|
|
|
|
2022-06-09 13:08:11 +00:00
|
|
|
|
2022-02-17 09:52:48 +00:00
|
|
|
@keyword('Create eACL')
|
|
|
|
def create_eacl(cid: str, rules_list: list):
|
|
|
|
table = f"{os.getcwd()}/{ASSETS_DIR}/eacl_table_{str(uuid.uuid4())}.json"
|
|
|
|
rules = ""
|
|
|
|
for rule in rules_list:
|
2022-06-09 13:08:11 +00:00
|
|
|
# TODO: check if $Object: is still necessary for filtering in the newest releases
|
2022-02-17 09:52:48 +00:00
|
|
|
rules += f"--rule '{rule}' "
|
|
|
|
cmd = (
|
|
|
|
f"{NEOFS_CLI_EXEC} acl extended create --cid {cid} "
|
|
|
|
f"{rules}--out {table}"
|
|
|
|
)
|
|
|
|
_cmd_run(cmd)
|
|
|
|
|
2022-08-03 12:20:29 +00:00
|
|
|
with open(table, 'r') as fout:
|
|
|
|
table_data = fout.read()
|
|
|
|
logger.info(f"Generated eACL:\n{table_data}")
|
|
|
|
|
2022-02-17 09:52:48 +00:00
|
|
|
return table
|
|
|
|
|
2021-09-10 12:44:40 +00:00
|
|
|
|
|
|
|
@keyword('Form BearerToken File')
|
|
|
|
def form_bearertoken_file(wif: str, cid: str, eacl_records: list) -> str:
|
|
|
|
"""
|
|
|
|
This function fetches eACL for given <cid> on behalf of <wif>,
|
|
|
|
then extends it with filters taken from <eacl_records>, signs
|
|
|
|
with bearer token and writes to file
|
|
|
|
"""
|
|
|
|
enc_cid = _encode_cid_for_eacl(cid)
|
|
|
|
file_path = f"{os.getcwd()}/{ASSETS_DIR}/{str(uuid.uuid4())}"
|
|
|
|
|
|
|
|
eacl = get_eacl(wif, cid)
|
|
|
|
json_eacl = dict()
|
|
|
|
if eacl:
|
|
|
|
eacl = eacl.replace('eACL: ', '')
|
|
|
|
eacl = eacl.split('Signature')[0]
|
|
|
|
json_eacl = json.loads(eacl)
|
|
|
|
logger.info(json_eacl)
|
|
|
|
eacl_result = {
|
2022-06-09 13:08:11 +00:00
|
|
|
"body":
|
|
|
|
{
|
|
|
|
"eaclTable":
|
2021-09-10 12:44:40 +00:00
|
|
|
{
|
2022-06-09 13:08:11 +00:00
|
|
|
"containerID":
|
2021-09-10 12:44:40 +00:00
|
|
|
{
|
|
|
|
"value": enc_cid
|
|
|
|
},
|
2022-06-09 13:08:11 +00:00
|
|
|
"records": []
|
|
|
|
},
|
|
|
|
"lifetime":
|
|
|
|
{
|
|
|
|
"exp": EACL_LIFETIME,
|
|
|
|
"nbf": "1",
|
|
|
|
"iat": "0"
|
2021-09-10 12:44:40 +00:00
|
|
|
}
|
2022-06-09 13:08:11 +00:00
|
|
|
}
|
|
|
|
}
|
2021-09-10 12:44:40 +00:00
|
|
|
|
|
|
|
if not eacl_records:
|
2022-06-09 13:08:11 +00:00
|
|
|
raise (f"Got empty eacl_records list: {eacl_records}")
|
2021-09-10 12:44:40 +00:00
|
|
|
for record in eacl_records:
|
|
|
|
op_data = {
|
2022-06-09 13:08:11 +00:00
|
|
|
"operation": record['Operation'],
|
|
|
|
"action": record['Access'],
|
|
|
|
"filters": [],
|
|
|
|
"targets": []
|
|
|
|
}
|
2021-09-10 12:44:40 +00:00
|
|
|
|
|
|
|
if Role(record['Role']):
|
|
|
|
op_data['targets'] = [
|
2022-06-09 13:08:11 +00:00
|
|
|
{
|
|
|
|
"role": record['Role']
|
|
|
|
}
|
|
|
|
]
|
2021-09-10 12:44:40 +00:00
|
|
|
else:
|
|
|
|
op_data['targets'] = [
|
2022-06-09 13:08:11 +00:00
|
|
|
{
|
|
|
|
"keys": [record['Role']]
|
|
|
|
}
|
|
|
|
]
|
2021-09-10 12:44:40 +00:00
|
|
|
|
|
|
|
if 'Filters' in record.keys():
|
|
|
|
op_data["filters"].append(record['Filters'])
|
|
|
|
|
|
|
|
eacl_result["body"]["eaclTable"]["records"].append(op_data)
|
|
|
|
|
|
|
|
# Add records from current eACL
|
|
|
|
if "records" in json_eacl.keys():
|
|
|
|
for record in json_eacl["records"]:
|
|
|
|
eacl_result["body"]["eaclTable"]["records"].append(record)
|
|
|
|
|
|
|
|
with open(file_path, 'w', encoding='utf-8') as eacl_file:
|
|
|
|
json.dump(eacl_result, eacl_file, ensure_ascii=False, indent=4)
|
|
|
|
|
|
|
|
logger.info(f"Got these extended ACL records: {eacl_result}")
|
|
|
|
sign_bearer_token(wif, file_path)
|
|
|
|
return file_path
|
|
|
|
|
2022-07-18 10:19:05 +00:00
|
|
|
@keyword('EACL Rules')
|
|
|
|
def eacl_rules(access: str, verbs: list, user: str):
|
|
|
|
"""
|
|
|
|
This function creates a list of eACL rules.
|
|
|
|
Args:
|
|
|
|
access (str): identifies if the following operation(s)
|
|
|
|
is allowed or denied
|
|
|
|
verbs (list): a list of operations to set rules for
|
|
|
|
user (str): a group of users (user/others) or a wallet of
|
|
|
|
a certain user for whom rules are set
|
|
|
|
Returns:
|
|
|
|
(list): a list of eACL rules
|
|
|
|
"""
|
|
|
|
if user not in ('others', 'user'):
|
2022-07-22 07:04:26 +00:00
|
|
|
pubkey = pub_key_hex(user)
|
|
|
|
user = f"pubkey:{pubkey}"
|
2022-07-18 10:19:05 +00:00
|
|
|
|
|
|
|
rules = []
|
|
|
|
for verb in verbs:
|
|
|
|
elements = [access, verb, user]
|
|
|
|
rules.append(' '.join(elements))
|
|
|
|
return rules
|
|
|
|
|
|
|
|
|
|
|
|
def sign_bearer_token(wallet_path: str, eacl_rules_file: str):
|
2021-09-10 12:44:40 +00:00
|
|
|
cmd = (
|
|
|
|
f'{NEOFS_CLI_EXEC} util sign bearer-token --from {eacl_rules_file} '
|
2022-07-18 10:19:05 +00:00
|
|
|
f'--to {eacl_rules_file} --wallet {wallet_path} --config {WALLET_CONFIG} --json'
|
2021-09-10 12:44:40 +00:00
|
|
|
)
|
|
|
|
_cmd_run(cmd)
|