Adapted to yangson 1.1, improvements in NACM module

parent 65d51760
......@@ -15,7 +15,7 @@ HTTP_SERVER:
SERVER_SSL_CERT: "jetconf/server.crt"
SERVER_SSL_PRIVKEY: "jetconf/server.key"
CA_CERT: "jetconf/ca.pem"
DBG_DISABLE_CERTS: true
DBG_DISABLE_CERTS: false
NACM:
ALLOWED_USERS: ["lojza@mail.cz"]
......
This diff is collapsed.
......@@ -2,7 +2,7 @@ import logging
from colorlog import debug, getLogger
from enum import Enum
from typing import Dict, Any, Iterable
from typing import List, Dict, Union, Any, Iterable
from datetime import datetime
from pytz import timezone
from yangson.instance import InstanceRoute, MemberName, EntryKeys, InstanceIdParser, ResourceIdParser
......@@ -10,6 +10,7 @@ from yangson.datamodel import DataModel
from .config import CONFIG_GLOBAL, CONFIG_HTTP
JsonNodeT = Union[Dict[str, Any], List]
SSLCertT = Dict[str, Any]
......@@ -34,19 +35,14 @@ class CertHelpers:
class DataHelpers:
# Create parent data nodes to JSON subtree up to top level
@staticmethod
def node2doc(id: InstanceRoute, val: Any) -> Dict[str, Any]:
def node2doc(ii: InstanceRoute, val: Any) -> Dict[str, Any]:
n = val
for isel in reversed(id):
for isel in reversed(ii):
if isinstance(isel, MemberName):
new_node = {}
new_node[isel.name] = n
n = new_node
n = {isel.key: n}
if isinstance(isel, EntryKeys):
new_node = []
for k in isel.keys:
n[k] = isel.keys[k]
new_node.append(n)
n = new_node
n.update(isel.keys)
n = [n]
return n
@staticmethod
......
......@@ -228,6 +228,7 @@ def _post(ds: BaseDatastore, pth: str, username: str, data: str) -> HttpResponse
rpc1 = RpcInfo()
rpc1.username = username
rpc1.path = url_path
rpc1.qs = query_string
try:
json_data = json.loads(data) if len(data) > 0 else {}
......@@ -237,9 +238,7 @@ def _post(ds: BaseDatastore, pth: str, username: str, data: str) -> HttpResponse
try:
ds.lock_data(username)
ins_pos = (query_string.get("insert") or [None])[0]
point = (query_string.get("point") or [None])[0]
new_root = ds.create_node_rpc(ds.get_data_root_staging(rpc1.username), rpc1, json_data, insert=ins_pos, point=point)
new_root = ds.create_node_rpc(ds.get_data_root_staging(rpc1.username), rpc1, json_data)
ds.add_to_journal_rpc(ChangeType.CREATE, rpc1, json_data, new_root)
http_resp = HttpResponse.empty(HttpStatus.Created)
except DataLockError as e:
......
......@@ -186,7 +186,7 @@ class NacmConfig:
self._user_nacm_rpc = {}
try:
nacm_json = self.nacm_ds.get_data_root().member("ietf-netconf-acm:nacm").value
nacm_json = self.nacm_ds.get_data_root()["ietf-netconf-acm:nacm"].value
except NonexistentInstance:
raise ValueError("Data does not contain \"ietf-netconf-acm:nacm\" root element")
......@@ -303,7 +303,7 @@ class UserNacm:
self.rule_tree = DataRuleTree(self.rule_lists)
debug_nacm("Rule tree for user \"{}\":\n{}".format(username, self.rule_tree.print_rule_tree()))
def check_data_node_path(self, root: InstanceNode, ii: InstanceRoute, access: Permission) -> Action:
def check_data_node_permission(self, root: InstanceNode, ii: InstanceRoute, access: Permission) -> Action:
if not self.nacm_enabled:
return Action.PERMIT
......@@ -341,22 +341,22 @@ class UserNacm:
# debug_nacm("check_data_node_path, result = {}".format(retval.name))
return retval
def _check_data_read_path(self, node: InstanceNode, root: InstanceNode, ii: InstanceRoute) -> InstanceNode:
def _prune_data_tree(self, node: InstanceNode, root: InstanceNode, ii: InstanceRoute, access: Permission) -> InstanceNode:
if isinstance(node.value, ObjectValue):
# print("obj: {}".format(node.value))
nsel = MemberName("")
mii = ii + [nsel]
for child_key in node.value.keys():
nsel.name = child_key
nsel.key = child_key
m = nsel.goto_step(node)
# debug_nacm("checking mii {}".format(mii))
if self.check_data_node_path(root, mii, Permission.NACM_ACCESS_READ) == Action.DENY:
if self.check_data_node_permission(root, mii, access) == Action.DENY:
# debug_nacm("Pruning node {} {}".format(id(node.value[child_key]), node.value[child_key]))
debug_nacm("Pruning node {}".format(DataHelpers.ii2str(mii)))
node = node.delete_member(child_key)
node = node.delete_item(child_key)
else:
node = self._check_data_read_path(m, root, mii).up()
node = self._prune_data_tree(m, root, mii, access).up()
elif isinstance(node.value, ArrayValue):
# print("array: {}".format(node.value))
nsel = EntryIndex(0)
......@@ -368,22 +368,22 @@ class UserNacm:
e = nsel.goto_step(node)
# debug_nacm("checking eii {}".format(eii))
if self.check_data_node_path(root, eii, Permission.NACM_ACCESS_READ) == Action.DENY:
if self.check_data_node_permission(root, eii, access) == Action.DENY:
# debug_nacm("Pruning node {} {}".format(id(node.value[i]), node.value[i]))
debug_nacm("Pruning node {}".format(DataHelpers.ii2str(eii)))
node = node.delete_entry(i)
node = node.delete_item(i)
arr_len -= 1
else:
i += 1
node = self._check_data_read_path(e, root, eii).up()
node = self._prune_data_tree(e, root, eii, access).up()
return node
def check_data_read_path(self, node: InstanceNode, root: InstanceNode, ii: InstanceRoute) -> InstanceNode:
def prune_data_tree(self, node: InstanceNode, root: InstanceNode, ii: InstanceRoute, access: Permission) -> InstanceNode:
if not self.nacm_enabled:
return node
else:
return self._check_data_read_path(node, root, ii)
return self._prune_data_tree(node, root, ii, access)
def check_rpc_name(self, rpc_name: str) -> Action:
if not self.nacm_enabled:
......
......@@ -3,8 +3,8 @@ from typing import List, Dict, Union, Any
from yangson.instance import InstanceRoute, ObjectValue, EntryKeys, MemberName
from . import knot_api
from .data import BaseDataListener, SchemaNode, PathFormat, ChangeType, DataChange, ConfHandlerResult
from .helpers import ErrorHelpers, LogHelpers
from .data import BaseDataListener, SchemaNode, ChangeType, DataChange, ConfHandlerResult
from .helpers import PathFormat, ErrorHelpers, LogHelpers, DataHelpers
from .knot_api import RRecordBase, SOARecord, ARecord, AAAARecord, NSRecord, MXRecord
JsonNodeT = Union[Dict[str, Any], List]
......@@ -17,7 +17,7 @@ class KnotConfServerListener(BaseDataListener):
debug_confh(self.__class__.__name__ + " triggered")
base_ii_str = self.schema_path
base_ii = self.ds.parse_ii(base_ii_str, PathFormat.URL)
base_ii = DataHelpers.parse_ii(base_ii_str, PathFormat.URL)
base_nv = self.ds.get_node(self.ds.get_data_root(), base_ii).value
knot_api.KNOT.begin()
......@@ -26,7 +26,7 @@ class KnotConfServerListener(BaseDataListener):
knot_api.KNOT.set_item(section="server", item="async-start", data=base_nv.get("knot-dns:async-start"))
knot_api.KNOT.set_item(section="server", item="nsid", data=base_nv.get("nsid-identity", {}).get("nsid"))
listen_endpoints = base_nv.get("listen-endpoint") or []
listen_endpoints = base_nv.get("listen-endpoint", [])
ep_str_list = []
for ep in listen_endpoints:
......@@ -51,7 +51,7 @@ class KnotConfLogListener(BaseDataListener):
debug_confh(self.__class__.__name__ + " triggered")
base_ii_str = self.schema_path
base_ii = self.ds.parse_ii(base_ii_str, PathFormat.URL)
base_ii = DataHelpers.parse_ii(base_ii_str, PathFormat.URL)
base_nv = self.ds.get_node(self.ds.get_data_root(), base_ii).value
knot_api.KNOT.begin()
......@@ -78,7 +78,7 @@ class KnotConfZoneListener(BaseDataListener):
# ii_str = "".join([str(seg) for seg in ii])
base_ii_str = self.schema_path
base_ii = self.ds.parse_ii(base_ii_str, PathFormat.URL)
base_ii = DataHelpers.parse_ii(base_ii_str, PathFormat.URL)
knot_api.KNOT.begin()
......@@ -131,7 +131,7 @@ class KnotConfControlListener(BaseDataListener):
debug_confh(self.__class__.__name__ + " triggered")
base_ii_str = self.schema_path
base_ii = self.ds.parse_ii(base_ii_str, PathFormat.URL)
base_ii = DataHelpers.parse_ii(base_ii_str, PathFormat.URL)
base_nv = self.ds.get_node(self.ds.get_data_root(), base_ii).value
knot_api.KNOT.begin()
......@@ -163,7 +163,7 @@ class KnotConfAclListener(BaseDataListener):
debug_confh(self.__class__.__name__ + " triggered")
base_ii_str = self.schema_path
base_ii = self.ds.parse_ii(base_ii_str, PathFormat.URL)
base_ii = DataHelpers.parse_ii(base_ii_str, PathFormat.URL)
base_nv = self.ds.get_node(self.ds.get_data_root(), base_ii).value
knot_api.KNOT.begin()
......@@ -218,7 +218,7 @@ class KnotZoneDataListener(BaseDataListener):
debug_confh(self.__class__.__name__ + " triggered")
base_ii_str = "/dns-zones:zone-data"
base_ii = self.ds.parse_ii(base_ii_str, PathFormat.URL)
base_ii = DataHelpers.parse_ii(base_ii_str, PathFormat.URL)
base_ii_len = len(base_ii)
ii_str = "".join([str(seg) for seg in ii])
......@@ -251,7 +251,7 @@ class KnotZoneDataListener(BaseDataListener):
# Add resource record to particular zone
elif (
len(ii) == (base_ii_len + 2)) \
and isinstance(ii[base_ii_len], MemberName) and (ii[base_ii_len].name == "zone") \
and isinstance(ii[base_ii_len], MemberName) and (ii[base_ii_len].key == "zone") \
and isinstance(ii[base_ii_len + 1], EntryKeys) \
and (ch.change_type == ChangeType.CREATE) \
and (ch.data.get("rrset") is not None):
......@@ -275,9 +275,9 @@ class KnotZoneDataListener(BaseDataListener):
# Add resource record to particular zone (only specific "rdata" item)
elif (
len(ii) == (base_ii_len + 4)) \
and isinstance(ii[base_ii_len], MemberName) and (ii[base_ii_len].name == "zone") \
and isinstance(ii[base_ii_len], MemberName) and (ii[base_ii_len].key == "zone") \
and isinstance(ii[base_ii_len + 1], EntryKeys) \
and isinstance(ii[base_ii_len + 2], MemberName) and (ii[base_ii_len + 2].name == "rrset") \
and isinstance(ii[base_ii_len + 2], MemberName) and (ii[base_ii_len + 2].key == "rrset") \
and isinstance(ii[base_ii_len + 3], EntryKeys) \
and (ch.change_type == ChangeType.CREATE) \
and (ch.data.get("rdata") is not None):
......@@ -304,9 +304,9 @@ class KnotZoneDataListener(BaseDataListener):
# Delete resource record from particular zone
elif (
len(ii) == (base_ii_len + 4)) \
and isinstance(ii[base_ii_len], MemberName) and (ii[base_ii_len].name == "zone") \
and isinstance(ii[base_ii_len], MemberName) and (ii[base_ii_len].key == "zone") \
and isinstance(ii[base_ii_len + 1], EntryKeys) \
and isinstance(ii[base_ii_len + 2], MemberName) and (ii[base_ii_len + 2].name == "rrset") \
and isinstance(ii[base_ii_len + 2], MemberName) and (ii[base_ii_len + 2].key == "rrset") \
and isinstance(ii[base_ii_len + 3], EntryKeys) \
and (ch.change_type == ChangeType.DELETE):
domain_name = ii[base_ii_len + 1].keys["name"]
......@@ -322,11 +322,11 @@ class KnotZoneDataListener(BaseDataListener):
# Delete resource record from particular zone (only specific "rdata" item)
elif (
len(ii) == (base_ii_len + 6)) \
and isinstance(ii[base_ii_len], MemberName) and (ii[base_ii_len].name == "zone") \
and isinstance(ii[base_ii_len], MemberName) and (ii[base_ii_len].key == "zone") \
and isinstance(ii[base_ii_len + 1], EntryKeys) \
and isinstance(ii[base_ii_len + 2], MemberName) and (ii[base_ii_len + 2].name == "rrset") \
and isinstance(ii[base_ii_len + 2], MemberName) and (ii[base_ii_len + 2].key == "rrset") \
and isinstance(ii[base_ii_len + 3], EntryKeys) \
and isinstance(ii[base_ii_len + 4], MemberName) and (ii[base_ii_len + 4].name == "rdata") \
and isinstance(ii[base_ii_len + 4], MemberName) and (ii[base_ii_len + 4].key == "rdata") \
and isinstance(ii[base_ii_len + 5], EntryKeys) \
and (ch.change_type == ChangeType.DELETE):
domain_name = ii[base_ii_len + 1].keys["name"]
......
......@@ -7,11 +7,9 @@ from yangson.instance import InstanceRoute, InstanceNode, Value, EntryKeys, None
from jetconf.knot_api import KnotInternalError
from .libknot.control import KnotCtl
from . import knot_api
from .helpers import DataHelpers
from .helpers import DataHelpers, JsonNodeT
from .handler_list import StateDataHandlerList
JsonNodeT = Union[Dict[str, Any], List]
class StateNonexistentInstance(NonexistentInstance):
def __init__(self, ii: InstanceRoute, text: str) -> None:
......
......@@ -99,8 +99,8 @@ def test_nacm(datastore_1, nacm_datastore_1):
# debug("Node contents: {}".format(datanode.value))
test_ii = data.parse_ii(test_path[0], PathFormat.XPATH)
rule = []
action = nacm_conf.get_user_nacm(test_user).check_data_node_path(data.get_data_root(), test_ii, test_path[1],
out_matching_rule=rule)
action = nacm_conf.get_user_nacm(test_user).check_data_node_permission(data.get_data_root(), test_ii, test_path[1],
out_matching_rule=rule)
assert action == test_path[2]
"""
if action == test_path[2]:
......@@ -114,7 +114,7 @@ def test_nacm(datastore_1, nacm_datastore_1):
test_ii2 = data.parse_ii("/dns-server:dns-server/zones/zone[domain='example.com']", PathFormat.XPATH)
# info("Reading: " + str(test_ii2))
res = nacm_conf.get_user_nacm(test_user).check_data_read_path(data.get_data_root(), test_ii2)
res = nacm_conf.get_user_nacm(test_user).prun_data_tree(data.get_data_root(), test_ii2)
res = json.dumps(res.value, indent=4, sort_keys=True)
res_expected = """
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment