Exemple #1
0
    def invoke_action_rpc(self, root: InstanceNode, rpc: RpcInfo) -> JsonNodeT:
        ii = self.parse_ii(rpc.path, rpc.path_format)
        node_ii = ii[0:-1]
        n = root.goto(node_ii)

        # Evaluate NACM
        if self.nacm and not rpc.skip_nacm_check:
            nrpc = self.nacm.get_user_rules(rpc.username)
            if nrpc.check_data_node_permission(
                    root, node_ii, Permission.NACM_ACCESS_EXEC) == Action.DENY:
                raise NacmForbiddenError(
                    "Invocation of \"{}\" operation denied for user \"{}\"".
                    format(rpc.op_name, rpc.username))

        ii_an = ii[-1]
        node_sn = n.schema_node
        sn = node_sn.get_child(ii_an.name, ii_an.namespace)

        action_handler = self.handlers.action.get_handler(id(sn))
        if action_handler is None:
            raise NoHandlerForOpError(rpc.path)

        # Get operation input schema
        sn_input = sn.get_child("input")

        # Input arguments are expected, this will validate them
        op_input_args = sn_input.from_raw(
            rpc.op_input_args) if sn_input.children else None

        try:
            ret_data = action_handler(ii, op_input_args, rpc.username)
        except Exception as e:
            raise OpHandlerFailedError(epretty(e))

        return ret_data
Exemple #2
0
    def update_node_rpc(self, root: InstanceNode, rpc: RpcInfo, value: Any) -> InstanceNode:
        ii = self.parse_ii(rpc.path, rpc.path_format)
        n = root.goto(ii)

        # Deny any changes of NACM data for non-privileged users
        if (len(ii) > 0) and (isinstance(ii[0], MemberName)):
            # Not getting root
            ns_first = ii[0].namespace
            if (ns_first == "ietf-netconf-acm") and (rpc.username not in CONFIG_NACM["ALLOWED_USERS"]):
                raise NacmForbiddenError(rpc.username + " not allowed to modify NACM data")
        else:
            # Replacing root node
            # Check if NACM data are present in the datastore
            nacm_val = n.value.get("ietf-netconf-acm:nacm")
            if (nacm_val is not None) and (rpc.username not in CONFIG_NACM["ALLOWED_USERS"]):
                raise NacmForbiddenError(rpc.username + " not allowed to modify NACM data")

        # Evaluate NACM
        if self.nacm:
            nrpc = self.nacm.get_user_rules(rpc.username)
            if nrpc.check_data_node_permission(root, ii, Permission.NACM_ACCESS_UPDATE) == Action.DENY:
                raise NacmForbiddenError()

        new_n = n.update(value, raw=True)

        return new_n.top()
Exemple #3
0
                    def _fill_state_roots(node: InstanceNode) -> InstanceNode:
                        if isinstance(node.value, ObjectValue):
                            if node.schema_node is state_root_sn.parent:
                                ii_gen = DataHelpers.node_get_ii(node)
                                sdh = STATE_DATA_HANDLES.get_handler(state_root_sch_pth)
                                if sdh is not None:
                                    try:
                                        if isinstance(sdh, ContainerNodeHandlerBase):
                                            state_handler_val = sdh.generate_node(ii_gen, rpc.username, staging)
                                        elif isinstance(sdh, ListNodeHandlerBase):
                                            state_handler_val = sdh.generate_list(ii_gen, rpc.username, staging)
                                    except Exception as e:
                                        error("Error occured in state data generator (sn: {})".format(state_root_sch_pth))
                                        error(epretty(e))
                                        error("This state node will be omitted.")
                                    else:
                                        if state_root_sn.ns == state_root_sn.parent.ns:
                                            nm_name = state_root_sn.qual_name[0]
                                        else:
                                            nm_name = state_root_sn.qual_name[1] + ":" + state_root_sn.qual_name[0]

                                        # print("nm={}".format(nm_name))
                                        node = node.put_member(nm_name, state_handler_val, raw=True).up()
                            else:
                                for key in node:
                                    member = node[key]
                                    node = _fill_state_roots(member).up()
                        elif isinstance(node.value, ArrayValue):
                            i = 0
                            arr_len = len(node.value)
                            while i < arr_len:
                                node = _fill_state_roots(node[i]).up()
                                i += 1

                        return node
Exemple #4
0
    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(name="", ns=None)
            mii = ii + [nsel]
            for child_key in node.value.keys():
                key_splitted = child_key.split(":", maxsplit=1)
                if len(key_splitted) > 1:
                    nsel.namespace, nsel.name = key_splitted
                else:
                    nsel.namespace, nsel.name = (None, key_splitted[0])
                m = nsel.goto_step(node)

                # debug_nacm("checking mii {}".format(mii))
                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_item(child_key)
                else:
                    node = self._prune_data_tree(m, root, mii, access).up()
        elif isinstance(node.value, ArrayValue):
            # print("array: {}".format(node.value))
            nsel = EntryIndex(0)
            eii = ii + [nsel]
            i = 0
            arr_len = len(node.value)
            while i < arr_len:
                nsel.index = i
                e = nsel.goto_step(node)

                # debug_nacm("checking eii {}".format(eii))
                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_item(i)
                    arr_len -= 1
                else:
                    i += 1
                    node = self._prune_data_tree(e, root, eii, access).up()

        return node
Exemple #5
0
            def _tree_limit_depth(node: InstanceNode, depth: int) -> InstanceNode:
                if isinstance(node.value, ObjectValue):
                    if depth > max_depth:
                        node.value = ObjectValue({})
                    else:
                        for child_key in sorted(node.value.keys()):
                            m = node[child_key]
                            node = _tree_limit_depth(m, depth + 1).up()
                elif isinstance(node.value, ArrayValue):
                    if depth > max_depth:
                        node.value = ArrayValue([])
                    else:
                        for i in range(len(node.value)):
                            e = node[i]
                            node = _tree_limit_depth(e, depth + 1).up()

                return node
Exemple #6
0
    def delete_node_rpc(self, root: InstanceNode,
                        rpc: RpcInfo) -> Tuple[InstanceNode, bool]:
        ii = self.parse_ii(rpc.path, rpc.path_format)
        n = root.goto(ii)

        # Deny any changes of NACM data for non-privileged users
        nacm_changed = False
        if (len(ii) > 0) and (isinstance(ii[0], MemberName)):
            # Not getting root
            ns_first = ii[0].namespace
            if ns_first == "ietf-netconf-acm":
                nacm_changed = True
                if rpc.username not in CONFIG_NACM["ALLOWED_USERS"]:
                    raise NacmForbiddenError(
                        rpc.username + " not allowed to modify NACM data")
        else:
            # Deleting root node
            # Check if NACM data are present in the datastore
            nacm_val = n.value.get("ietf-netconf-acm:nacm")
            if nacm_val is not None:
                nacm_changed = True
                if rpc.username not in CONFIG_NACM["ALLOWED_USERS"]:
                    raise NacmForbiddenError(
                        rpc.username + " not allowed to modify NACM data")

        # Evaluate NACM
        if self.nacm and not rpc.skip_nacm_check:
            nrpc = self.nacm.get_user_rules(rpc.username)
            if nrpc.check_data_node_permission(
                    root, ii, Permission.NACM_ACCESS_DELETE) == Action.DENY:
                raise NacmForbiddenError()

        if len(ii) == 0:
            # Deleting entire datastore
            new_n = RootNode(ObjectValue({}), root.schema_node, datetime.now())
        else:
            n_parent = n.up()
            last_isel = ii[-1]
            if isinstance(n_parent.value, ArrayValue):
                if isinstance(last_isel, EntryIndex):
                    new_n = n_parent.delete_item(last_isel.index)
                elif isinstance(last_isel, EntryKeys):
                    new_n = n_parent.delete_item(n.index)
                else:
                    raise ValueError("Unknown node selector")
            elif isinstance(n_parent.value, ObjectValue):
                new_n = n_parent.delete_item(
                    last_isel.namespace + ":" +
                    last_isel.name if last_isel.namespace else last_isel.name)
            else:
                raise InstanceValueError(rpc.path, "Invalid target node type")

        return new_n.top(), nacm_changed
Exemple #7
0
    def update_node_rpc(self, root: InstanceNode, rpc: RpcInfo,
                        value: Any) -> Tuple[InstanceNode, bool]:
        ii = self.parse_ii(rpc.path, rpc.path_format)

        # Get target member name
        input_member_keys = tuple(value.keys())
        if len(input_member_keys) != 1:
            raise ValueError(
                "Received json object must contain exactly one member")

        input_member_name_fq = input_member_keys[0]
        try:
            input_member_ns, input_member_name = input_member_name_fq.split(
                ":", maxsplit=1)
        except ValueError:
            raise ValueError(
                "Input object name must me in fully-qualified format")
        input_member_value = value[input_member_name_fq]

        n = root.goto(ii)

        # Deny any changes of NACM data for non-privileged users
        nacm_changed = False
        if (len(ii) > 0) and (isinstance(ii[0], MemberName)):
            # Not getting root
            ns_first = ii[0].namespace
            if ns_first == "ietf-netconf-acm":
                nacm_changed = True
                if rpc.username not in CONFIG_NACM["ALLOWED_USERS"]:
                    raise NacmForbiddenError(
                        rpc.username + " not allowed to modify NACM data")
        else:
            # Replacing root node
            # Check if NACM data are present in the datastore
            nacm_val = n.value.get("ietf-netconf-acm:nacm")
            if nacm_val is not None:
                nacm_changed = True
                if rpc.username not in CONFIG_NACM["ALLOWED_USERS"]:
                    raise NacmForbiddenError(
                        rpc.username + " not allowed to modify NACM data")

        # Evaluate NACM
        if self.nacm and not rpc.skip_nacm_check:
            nrpc = self.nacm.get_user_rules(rpc.username)
            if nrpc.check_data_node_permission(
                    root, ii, Permission.NACM_ACCESS_UPDATE) == Action.DENY:
                raise NacmForbiddenError()

        new_n = n.update(input_member_value, raw=True)
        new_n.validate(ValidationScope.syntax)

        return new_n.top(), nacm_changed
Exemple #8
0
    def create_node_rpc(self, root: InstanceNode, rpc: RpcInfo,
                        value: Any) -> Tuple[InstanceNode, bool]:
        ii = self.parse_ii(rpc.path, rpc.path_format)

        # Get target member name
        input_member_keys = tuple(value.keys())
        if len(input_member_keys) != 1:
            raise ValueError(
                "Received json object must contain exactly one member")

        input_member_name_fq = input_member_keys[0]
        try:
            input_member_ns, input_member_name = input_member_name_fq.split(
                ":", maxsplit=1)
        except ValueError:
            raise ValueError(
                "Input object name must me in fully-qualified format")
        input_member_value = value[input_member_name_fq]

        # Deny any changes of NACM data for non-privileged users
        nacm_changed = False
        if (len(ii) > 0) and (isinstance(ii[0], MemberName)):
            # Not getting root
            ns_first = ii[0].namespace
            if ns_first == "ietf-netconf-acm":
                nacm_changed = True
                if rpc.username not in CONFIG_NACM["ALLOWED_USERS"]:
                    raise NacmForbiddenError(
                        rpc.username + " not allowed to modify NACM data")
        else:
            # Editing root node
            if input_member_ns == "ietf-netconf-acm":
                nacm_changed = True
                if rpc.username not in CONFIG_NACM["ALLOWED_USERS"]:
                    raise NacmForbiddenError(
                        rpc.username + " not allowed to modify NACM data")

        # Evaluate NACM
        if self.nacm and not rpc.skip_nacm_check:
            nrpc = self.nacm.get_user_rules(rpc.username)
            if nrpc.check_data_node_permission(
                    root, ii, Permission.NACM_ACCESS_CREATE) == Action.DENY:
                raise NacmForbiddenError()

        n = root.goto(ii)

        # Get target schema node
        sn = n.schema_node  # type: InternalNode
        member_sn = sn.get_child(input_member_name, input_member_ns)

        if member_sn is None:
            raise ValueError("Received json object contains unknown member")

        # Check if target member already exists
        if sn.ns == member_sn.ns:
            try:
                existing_member = n[input_member_name]
            except NonexistentInstance:
                existing_member = None
        else:
            try:
                existing_member = n[input_member_name_fq]
            except NonexistentInstance:
                existing_member = None

        # Get query parameters
        insert = rpc.qs.get("insert", [None])[0]
        point = rpc.qs.get("point", [None])[0]

        if isinstance(member_sn, ListNode):
            # Append received node to list

            # Create list if necessary
            if existing_member is None:
                new_member_name = input_member_name if n.namespace == input_member_ns else input_member_name_fq
                existing_member = n.put_member(new_member_name, ArrayValue([]))

            # Get ListNode key names
            list_node_keys = member_sn.keys  # Key names in the form [(key, ns), ]

            if insert == "first":
                # Optimization
                if len(existing_member.value) > 0:
                    list_entry_first = existing_member[0]  # type: ArrayEntry
                    new_member = list_entry_first.insert_before(
                        input_member_value, raw=True).up()
                else:
                    new_member = existing_member.update([input_member_value],
                                                        raw=True)
            elif (insert == "last") or (insert is None):
                # Optimization
                if len(existing_member.value) > 0:
                    list_entry_last = existing_member[-1]  # type: ArrayEntry
                    new_member = list_entry_last.insert_after(
                        input_member_value, raw=True).up()
                else:
                    new_member = existing_member.update([input_member_value],
                                                        raw=True)
            elif (insert == "before") and (point is not None):
                point_keys_val = point.split(
                    ","
                )  # List key values passed in the "point" query argument
                if len(list_node_keys) != len(point_keys_val):
                    raise ValueError(
                        "Invalid number of keys passed in 'point' query: {} ({} expected)"
                        .format(len(point_keys_val), len(list_node_keys)))
                entry_keys = dict(
                    map(lambda i: (list_node_keys[i], point_keys_val[i]),
                        range(len(list_node_keys))))
                entry_sel = EntryKeys(entry_keys)
                point_list_entry = entry_sel.goto_step(
                    existing_member)  # type: ArrayEntry
                new_member = point_list_entry.insert_before(input_member_value,
                                                            raw=True).up()
            elif (insert == "after") and (point is not None):
                point_keys_val = point.split(
                    ","
                )  # List key values passed in the "point" query argument
                if len(list_node_keys) != len(point_keys_val):
                    raise ValueError(
                        "Invalid number of keys passed in 'point' query: {} ({} expected)"
                        .format(len(point_keys_val), len(list_node_keys)))
                entry_keys = dict(
                    map(lambda i: (list_node_keys[i], point_keys_val[i]),
                        range(len(list_node_keys))))
                entry_sel = EntryKeys(entry_keys)
                point_list_entry = entry_sel.goto_step(
                    existing_member)  # type: ArrayEntry
                new_member = point_list_entry.insert_after(input_member_value,
                                                           raw=True).up()
            else:
                raise ValueError("Invalid 'insert'/'point' query values")
        elif isinstance(member_sn, LeafListNode):
            # Append received node to leaf list

            # Create leaf list if necessary
            if existing_member is None:
                new_member_name = input_member_name if n.namespace == input_member_ns else input_member_name_fq
                existing_member = n.put_member(new_member_name, ArrayValue([]))

            # Convert input data from List/Dict to ArrayValue/ObjectValue
            new_value_item = member_sn.entry_from_raw(input_member_value)

            if insert == "first":
                new_member = existing_member.update(
                    ArrayValue([new_value_item] + existing_member.value))
            elif (insert == "last") or (insert is None):
                new_member = existing_member.update(
                    ArrayValue(existing_member.value + [new_value_item]))
            else:
                raise ValueError("Invalid 'insert' query value")
        else:
            # Create new container member

            if existing_member is None:
                # Create new node (object member)
                new_member_name = input_member_name if n.namespace == input_member_ns else input_member_name_fq
                new_member = n.put_member(new_member_name,
                                          input_member_value,
                                          raw=True)
            else:
                # Data node already exists
                raise InstanceAlreadyPresent(
                    "Member \"{}\" already present in \"{}\"".format(
                        input_member_name, ii))

        return new_member.top(), nacm_changed