NACM RFC341 datanode paths, read operation

This commit is contained in:
Olof hagsand 2020-04-14 11:38:29 +02:00
parent 58c36decef
commit ba59e22fd7
26 changed files with 689 additions and 325 deletions

View file

@ -24,6 +24,12 @@
## 4.5.0
Expected: May 2020
### Major New features
* NACM RFC341 datanode paths
* Read operations now implemented
* Create/Write/Update not yet
### API changes on existing protocol/config features (You may have have to change how you use Clixon)
* Stricter incoming RPC sanity checking, error messages may have changed.
@ -36,6 +42,7 @@ Expected: May 2020
### Minor changes
* Sanity check of duplicates prefixes in Yang modules and submodules as defined in RFC 7950 Sec 7.1.4
## 4.4.0
5 April 2020
@ -49,7 +56,7 @@ features include optimized search functions and a repair callback.
* New "general-purpose" datastore upgrade/repair callback called once on startup, intended for low-level general upgrades and as a complement to module-specific upgrade.
* Called on startup after initial XML parsing, but before module-specific upgrades
* Enabled by definign the `.ca_datastore_upgrade`
* Enabled by defining the `.ca_datastore_upgrade`
* [General-purpose upgrade documentation](https://clixon-docs.readthedocs.io/en/latest/backend.html#general-purpose)
* New and updated search functions using xpath, api-path and instance-id, and explicit indexes
* New search functions using api-path and instance_id:

View file

@ -474,7 +474,7 @@ client_get_config_only(clicon_handle h,
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* NACM datanode/module read validation */
if (nacm_datanode_read(xret, xvec, xlen, username, xnacm) < 0)
if (nacm_datanode_read(h, xret, xvec, xlen, username, xnacm) < 0)
goto done;
}
cprintf(cbret, "<rpc-reply>");
@ -1174,7 +1174,7 @@ from_client_get(clicon_handle h,
if (xpath_vec(xret, nsc, "%s", &xvec, &xlen, xpath?xpath:"/") < 0)
goto done;
/* NACM datanode/module read validation */
if (nacm_datanode_read(xret, xvec, xlen, username, xnacm) < 0)
if (nacm_datanode_read(h, xret, xvec, xlen, username, xnacm) < 0)
goto done;
}
cprintf(cbret, "<rpc-reply>"); /* OK */

View file

@ -130,6 +130,7 @@ clixon_plugin_statedata(clicon_handle h,
if (debug)
clicon_log_xml(LOG_DEBUG, x, "%s STATE:", __FUNCTION__);
#endif
/* XXX: ret == 0 invalid yang binding should be handled as internal error */
if (xml_bind_yang(x, YB_MODULE, yspec, NULL) < 0)
goto done;
if (xml_apply(x, CX_ELMNT, xml_sort, h) < 0)

View file

@ -95,7 +95,7 @@ leafstring(cxobj *x)
return xml_value(c);
}
/*! Internal recursive part where configuration xml tree is pruned frim filter
/*! Internal recursive part where configuration xml tree is pruned from filter
* assume parent has been selected and filter match (same name) as parent
* parent is pruned according to selection.
* @param[in] xfilter Filter xml

View file

@ -738,9 +738,11 @@ restconf_insert_attributes(cxobj *xdata,
}
/* Add prefix/namespaces used in attributes */
cv = NULL;
while ((cv = cvec_each(nsc, cv)) != NULL)
if (xmlns_set(xdata, cv_name_get(cv), cv_string_get(cv)) < 0)
while ((cv = cvec_each(nsc, cv)) != NULL){
char *ns = cv_string_get(cv);
if (xmlns_set(xdata, cv_name_get(cv), ns) < 0)
goto done;
}
if (nsc)
xml_sort(xdata, NULL); /* Ensure attr is first */
cprintf(cb, "/>");

View file

@ -57,8 +57,9 @@ enum nacm_access{
* Prototypes
*/
int nacm_rpc(char *rpc, char *module, char *username, cxobj *xnacm, cbuf *cbret);
int nacm_datanode_read(cxobj *xt, cxobj **xvec, size_t xlen, char *username, cxobj *nacm_xtree);
int nacm_datanode_write(cxobj *xt, cxobj *xr, enum nacm_access access,
int nacm_datanode_read(clicon_handle h, cxobj *xt, cxobj **xvec, size_t xlen, char *username,
cxobj *nacm_xtree);
int nacm_datanode_write(clicon_handle h, cxobj *xt, cxobj *xr, enum nacm_access access,
char *username, cxobj *xnacm, cbuf *cbret);
int nacm_access_pre(clicon_handle h, char *username, cxobj **xnacmp);

View file

@ -64,7 +64,8 @@ int xml_sanity(cxobj *x, void *arg);
int xml_non_config_data(cxobj *xt, void *arg);
int xml2xpath(cxobj *x, char **xpath);
int assign_namespaces(cxobj *x0, cxobj *x1, cxobj *x1p);
int assign_namespace_element(cxobj *x0, cxobj *x1, cxobj *x1p);
int assign_namespace_body(cxobj *x0, char *x0bstr, cxobj *x1);
int xml_merge(cxobj *x0, cxobj *x1, yang_stmt *yspec, char **reason);
int yang_enum_int_value(cxobj *node, int32_t *val);

View file

@ -57,8 +57,8 @@ int xml_nsctx_node(cxobj *x, cvec **ncp);
int xml_nsctx_yang(yang_stmt *yn, cvec **ncp);
int xml_nsctx_yangspec(yang_stmt *yspec, cvec **ncp);
int xml2ns(cxobj *x, char *localname, char **namespace);
int xml2prefix(cxobj *xn, char *namespace, char **prefixp);
int xml_localname_check(cxobj *xn, void *arg);
int xml2ns(cxobj *x, char *localname, char **namespace);
int xml2prefix(cxobj *xn, char *namespace, char **prefixp);
int xml_localname_check(cxobj *xn, void *arg);
#endif /* _CLIXON_XML_NSCTX_H */

View file

@ -131,15 +131,19 @@ attr_ns_value(cxobj *x,
goto done;
}
/*! When new body is added, some needs type lookup is made and namespace checked
* This includes identityrefs, paths
* This code identifies x0 as an identityref, looks at the _body_ string and ensures the right
* namespace is inserted in x1.
*/
static int
check_identityref(cxobj *x0,
cxobj *x1,
cxobj *x1p,
char *x1bstr,
yang_stmt *y)
check_body_namespace(cxobj *x0,
cxobj *x1,
cxobj *x1p,
char *x1bstr,
yang_stmt *y)
{
int retval = -1;
yang_stmt *yrestype = NULL;
char *prefix = NULL;
char *ns0 = NULL;
char *ns1 = NULL;
@ -151,11 +155,6 @@ check_identityref(cxobj *x0,
isroot = xml_parent(x1p)==NULL &&
strcmp(xml_name(x1p), "config") == 0 &&
xml_prefix(x1p)==NULL;
if (yang_type_get(y, NULL, &yrestype,
NULL, NULL, NULL, NULL, NULL) < 0)
goto done;
if (strcmp(yang_argument_get(yrestype), "identityref") != 0)
goto ok; /* skip */
if (nodeid_split(x1bstr, &prefix, NULL) < 0)
goto done;
if (prefix == NULL)
@ -240,12 +239,12 @@ text_modify(clicon_handle h,
char *opstr = NULL;
char *x1name;
char *x1cname; /* child name */
cxobj *x0c; /* base child */
cxobj *x0b; /* base body */
cxobj *x1c; /* mod child */
char *x0bstr; /* mod body string */
char *x1bstr; /* mod body string */
yang_stmt *yc; /* yang child */
cxobj *x0c; /* base child */
cxobj *x0b; /* base body */
cxobj *x1c; /* mod child */
char *x0bstr; /* mod body string */
char *x1bstr; /* mod body string */
yang_stmt *yc; /* yang child */
cxobj **x0vec = NULL;
int i;
int ret;
@ -318,7 +317,7 @@ text_modify(clicon_handle h,
* of ordered-by user and (changed) insert attribute.
*/
if (!permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x1, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
if ((ret = nacm_datanode_write(h, NULL, x1, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -335,7 +334,7 @@ text_modify(clicon_handle h,
case OP_NONE: /* fall thru */
if (x0==NULL){
if ((op != OP_NONE) && !permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x1, NACM_CREATE, username, xnacm, cbret)) < 0)
if ((ret = nacm_datanode_write(h, NULL, x1, NACM_CREATE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -350,8 +349,9 @@ text_modify(clicon_handle h,
/* Get namespace from x1
* Check if namespace exists in x0 parent
* if not add new binding and replace in x0.
* See also xmlns copying of attributes in the body section below
*/
if (assign_namespaces(x1, x0, x0p) < 0)
if (assign_namespace_element(x1, x0, x0p) < 0)
goto done;
changed++;
if (op==OP_NONE)
@ -362,17 +362,46 @@ text_modify(clicon_handle h,
}
}
if (x1bstr){
/* XXX: Do the type lookup here inline instead, there are more
* cases where we check for types, eg where we now call clixon_trim2
* AND in nacm path where we need proper namespace contexts.
/* Some bodies (eg identityref) requires proper namespace setup, so a type lookup is
* necessary.
*/
if (check_identityref(x1, x0, x0p, x1bstr, y0) < 0)
yang_stmt *yrestype = NULL;
char *restype;
if (yang_type_get(y0, NULL, &yrestype, NULL, NULL, NULL, NULL, NULL) < 0)
goto done;
if (yrestype == NULL){
clicon_err(OE_CFG, EFAULT, "No restype (internal error)");
goto done;
}
restype = yang_argument_get(yrestype);
if (strcmp(restype, "identityref") == 0){
x1bstr = clixon_trim2(x1bstr, " \t\n");
if (check_body_namespace(x1, x0, x0p, x1bstr, y0) < 0)
goto done;
}
else{
/* Some bodies strip pretty-printed here, unsure where to do this,.. */
if (strcmp(restype, "enumeration") == 0 ||
strcmp(restype, "bits") == 0)
x1bstr = clixon_trim2(x1bstr, " \t\n");
/* If origin body has namespace definitions, copy them. The reason is that
* some bodies rely on namespace prefixes, such as NACM path, but there is
* no way we can now this here.
* However, this may lead to namespace collisions if these prefixes are not
* canonical, and may collide with the assign_namespace_element() above (but that
* is for element sysmbols)
* Oh well.
*/
if (assign_namespace_body(x1, x1bstr, x0) < 0)
goto done;
}
if ((x0b = xml_body_get(x0)) != NULL){
x0bstr = xml_value(x0b);
if (x0bstr==NULL || strcmp(x0bstr, x1bstr)){
if ((op != OP_NONE) && !permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x1,
if ((ret = nacm_datanode_write(h, NULL, x1,
x0bstr==NULL?NACM_CREATE:NACM_UPDATE,
username, xnacm, cbret)) < 0)
goto done;
@ -398,7 +427,7 @@ text_modify(clicon_handle h,
case OP_REMOVE: /* fall thru */
if (x0){
if ((op != OP_NONE) && !permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
if ((ret = nacm_datanode_write(h, NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -460,7 +489,7 @@ text_modify(clicon_handle h,
* of ordered-by user and (changed) insert attribute.
*/
if (!permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x1, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
if ((ret = nacm_datanode_write(h, NULL, x1, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -486,7 +515,7 @@ text_modify(clicon_handle h,
if (op == OP_NONE)
break;
if (op==OP_MERGE && !permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x0, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
if ((ret = nacm_datanode_write(h, NULL, x0, x0?NACM_UPDATE:NACM_CREATE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -503,7 +532,7 @@ text_modify(clicon_handle h,
} /* anyxml, anydata */
if (x0==NULL){
if (op==OP_MERGE && !permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x1, NACM_CREATE, username, xnacm, cbret)) < 0)
if ((ret = nacm_datanode_write(h, NULL, x1, NACM_CREATE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -522,7 +551,7 @@ text_modify(clicon_handle h,
* Check if namespace exists in x0 parent
* if not add new binding and replace in x0.
*/
if (assign_namespaces(x1, x0, x0p) < 0)
if (assign_namespace_element(x1, x0, x0p) < 0)
goto done;
if (op==OP_NONE)
xml_flag_set(x0, XML_FLAG_NONE); /* Mark for potential deletion */
@ -595,7 +624,7 @@ text_modify(clicon_handle h,
case OP_REMOVE: /* fall thru */
if (x0){
if (!permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
if ((ret = nacm_datanode_write(h, NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -676,7 +705,7 @@ text_modify_top(clicon_handle h,
case OP_REMOVE:
case OP_REPLACE:
if (!permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
if ((ret = nacm_datanode_write(h, NULL, x0, NACM_DELETE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;
@ -710,7 +739,7 @@ text_modify_top(clicon_handle h,
/* Special case top-level replace */
else if (op == OP_REPLACE || op == OP_DELETE){
if (!permit && xnacm){
if ((ret = nacm_datanode_write(NULL, x1, NACM_UPDATE, username, xnacm, cbret)) < 0)
if ((ret = nacm_datanode_write(h, NULL, x1, NACM_UPDATE, username, xnacm, cbret)) < 0)
goto done;
if (ret == 0)
goto fail;

View file

@ -69,6 +69,8 @@
#include "clixon_yang_module.h"
#include "clixon_datastore.h"
#include "clixon_xml_nsctx.h"
#include "clixon_xml_map.h"
#include "clixon_path.h"
#include "clixon_nacm.h"
/* NACM namespace for use with xml namespace contexts and xpath */
@ -311,38 +313,131 @@ nacm_rpc(char *rpc,
* Datanode/module read and write
*/
/*! We have a rule matching user group. Now match proper write operation and module
/*! There is a data-node rule with a path. Match it.
* @param[in] h Clixon handle
* @param[in] xt XML root tree with "config" label.
* @param[in] xr XML requestor node (part of xt)
* @param[in] pathobj XML nacm path node
* @retval -1 Error
* @retval 0 No Match
* @retval 1 Match
* @see RFC8341 3.4.5. Data Node Access Validation point (6)
* @retval 1 SubMatch
o For HEAD and GET requests, any data nodes that are ancestor nodes
of the target resource are considered to be part of the retrieval
request for access control purposes.
Data nodes to which the client does not have read access are silently
omitted, along with any descendants,
the selection criteria are applied to the subset of nodes that the user
is authorized to read, not the entire datastore.
* Assume tree:
* tree
* |
* a
* / \
* b c
* access: /
* rule1: /a/b deny
* rule2: / permit
* returned tree:
* tree
* |
* a
* \
* c
* Algorithm:
* Traverse all rules and access the tree:
* rule1: purge b
* rule2: accept rest.
*/
static int
nacm_rule_datanode(cxobj *xt,
cxobj *xr,
nacm_rule_datanode_path(clicon_handle h,
cxobj *xt,
cxobj *xreq,
cxobj *pathobj)
{
int retval = -1;
cvec *nsc0 = NULL; /* Non-canonical namespace context */
cxobj *xp; /* Node specified by path */
cxobj *xa; /* Ancestor */
char *path0; /* Non-canonical path */
char *path; /* Canonical path */
yang_stmt *yspec;
cxobj **xvec = NULL;
size_t xlen = 0;
int ret;
yspec = clicon_dbspec_yang(h);
path0 = clixon_trim2(xml_body(pathobj), " \t\n");
/* Create namespace context for with nacm namespace as default */
if (xml_nsctx_node(pathobj, &nsc0) < 0)
goto done;
/* instance-id requires canonical paths */
if (xpath2canonical(path0, nsc0, yspec, &path, NULL) < 0)
goto done;
if ((ret = clixon_xml_find_instance_id(xt, yspec, &xvec, &xlen, "%s", path)) < 0)
goto nomatch;
if (ret == 0)
goto nomatch;
assert(xlen == 1); /* XXX: This may be rich */
/* xp is the node specified by the path */
xp = xvec[0];
/* The requested node (xreq) is the node specified by the path (xp) or is a
* descendant node of the path xn.
* By checking if xreq is equal to xp or if any of its ancestors is =
* xmatch is one of xvec[] or an ancestor of the xvec[] nodes.
*/
xa = xreq;
do {
if (xp == xa)
goto match;
} while ((xa = xml_parent(xa)) != NULL);
match:
retval = 1;
done:
if (xvec)
free(xvec);
return retval;
nomatch:
retval = 0;
goto done;
}
/*! We have a rule matching user group. Now match proper write operation and module
* @param[in] h Clixon handle
* @param[in] xt XML root tree with "config" label.
* @param[in] xreq XML requestor node (part of xt)
* @param[in] xrule XML rule node
* @param[in] access read/write/create/update/delete/exec
* @retval -1 Error
* @retval 0 No Match
* @retval 1 Match
* @see RFC8341 3.4.5. Data Node Access Validation point (6)
* @note read access is not handled in this function, see nacm_data_read_xrule
*/
static int
nacm_rule_datanode(clicon_handle h,
cxobj *xt,
cxobj *xreq,
cxobj *xrule,
enum nacm_access access)
{
int retval = -1;
char *path;
char *access_operations;
char *module_rule; /* rule module name */
yang_stmt *ys;
yang_stmt *ymod;
char *module;
cxobj *xpath; /* xpath match */
cxobj *xp; /* parent */
cvec *nsc = NULL;
cxobj *pathobj;
int ret;
/* Create namespace context for with nacm namespace as default */
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
goto done;
/* 6a) The rule's "module-name" leaf is "*" or equals the name of
* the YANG module where the requested data node is defined. */
if ((module_rule = xml_find_body(xrule, "module-name")) == NULL)
goto nomatch;
if (strcmp(module_rule,"*")!=0){
if (xr==NULL || (ys = xml_spec(xr)) == NULL)
if (xreq==NULL || (ys = xml_spec(xreq)) == NULL)
goto nomatch;
ymod = ys_module(ys);
module = yang_argument_get(ymod);
@ -353,15 +448,13 @@ nacm_rule_datanode(cxobj *xt,
/* 6b) Either (1) the rule does not have a "rule-type" defined or
(2) the "rule-type" is "data-node" and the "path" matches the
requested data node, action node, or notification node. */
if ((path = xml_find_body(xrule, "path")) == NULL){
if ((pathobj = xml_find_type(xrule, NULL, "path", CX_ELMNT)) == NULL){
if (xml_find_body(xrule, "rpc-name") ||xml_find_body(xrule, "notification-name"))
goto nomatch;
}
else
path = clixon_trim2(path, " \t\n");
access_operations = xml_find_body(xrule, "access-operations");
switch (access){
case NACM_READ:
case NACM_READ: /* Not used, see nacm_data_read_xrule */
/* 6c) For a "read" access operation, the rule's "access-operations"
leaf has the "read" bit set or has the special value "*" */
if (!match_access(access_operations, "read", NULL))
@ -388,108 +481,166 @@ nacm_rule_datanode(cxobj *xt,
default:
break;
}
/* Here module is matched, now check for path if any NYI
* A path is considered to match if the requested node is the node
/* Here module is matched, now check for path if any
* A path is considered to match if the requested node (xreq) is the node
* specified by the path or is a descendant node of the path */
if (path){
#if 0
if ((ret = clixon_xml_find_instance_id(xt, yt, xvec, xlen, "%s", path)) == NULL)
if (pathobj){
if ((ret = nacm_rule_datanode_path(h, xt, xreq, pathobj)) < 0)
goto done;
if (ret == 0)
goto nomatch;
#else
if ((xpath = xpath_first(xt, nsc, "%s", path)) == NULL)
goto nomatch;
#endif
/* The requested node xr is the node specified by the path or is a
* descendant node of the path:
* xmatch is one of xvec[] or an ancestor of the xvec[] nodes.
*/
xp = xr;
do {
if (xpath == xp)
goto match;
} while ((xp = xml_parent(xp)) != NULL);
if (ret == 2)
goto submatch;
}
match:
// match:
retval = 1;
done:
if (nsc)
xml_nsctx_free(nsc);
return retval;
nomatch:
retval = 0;
goto done;
submatch:
retval = 2;
goto done;
}
/*! Go through all rules for a requested node
* @param[in] xt XML root tree with "config" label
* @param[in] xr Requested node (node in xt)
* @param[in] gvec NACM groups where user is member
* @param[in] glen Length of gvec
* @param[in] rlistvec NACM rule-list entries
* @param[in] rlistlen Length of rlistvec
* @param[in] nsc NACM namespace context for xpaths
* @param[out] xrulep If set, then points to matching rule
*/
static int
nacm_data_read_xr(cxobj *xt,
cxobj *xr,
cxobj **gvec,
size_t glen,
cxobj **rlistvec,
size_t rlistlen,
cvec *nsc,
cxobj **xrulep)
nacm_data_read_action(cxobj *xrule,
cxobj *xp,
int default_permit)
{
int retval = -1;
int i, j;
cxobj *rlist;
char *gname;
cxobj **rvec = NULL; /* rules */
size_t rlen;
cxobj *xrule = NULL;
int match = 0;
int retval = -1;
char *action;
for (i=0; i<rlistlen; i++){ /* Loop through rule list */
rlist = rlistvec[i];
/* Loop through user's group to find match in this rule-list */
for (j=0; j<glen; j++){
gname = xml_find_body(gvec[j], "name");
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
break; /* found */
}
if (j==glen) /* not found */
continue;
/* 6. For each rule-list entry found, process all rules, in order,
until a rule that matches the requested access operation is
found. (see 6 sub rules in nacm_rule_datanode
*/
if (xpath_vec(rlist, nsc, "rule", &rvec, &rlen) < 0)
goto done;
for (j=0; j<rlen; j++){ /* Loop through rules */
xrule = rvec[j];
if ((match = nacm_rule_datanode(xt, xr, xrule, NACM_READ)) < 0)
if ((action = xml_find_body(xrule, "action")) != NULL){
if (strcmp(action, "deny")==0){
if (xml_purge(xp) < 0)
goto done;
if (match) /* xrule match */
break;
}
if (rvec){
free(rvec);
rvec=NULL;
else if (strcmp(action, "permit")==0){
if (default_permit)
;
else
xml_flag_set(xp, XML_FLAG_MARK);
}
if (match) /* xrule match */
break;
}
if (match)
*xrulep = xrule;
else
*xrulep = NULL;
retval = 0;
done:
if (rvec)
free(rvec);
return retval;
}
/*
* @retval -1 Error
* @retval 0 OK and rule does not match
* @retval 1 OK and rule matches
* Two distinct cases:
* (1) read_default is permit
* mark all deny rules and remove them
* (2) read_default is deny:
* mark all permit rules and ancestors, remove everything else
*/
// xml_apply_ancestor(xn, (xml_applyfn_t*)xml_flag_set, (void*)XML_FLAG_CHANGE);
static int
nacm_data_read_xrule(clicon_handle h,
cxobj *xt,
cxobj *xrule,
int default_permit)
{
int retval = -1;
cxobj *xp;
int ret;
cxobj *pathobj;
char *access_operations;
cvec *nsc0 = NULL; /* Non-canonical namespace context */
char *path0; /* Non-canonical path */
char *path; /* Canonical path */
yang_stmt *yspec;
yang_stmt *ymod;
cxobj **xvec = NULL;
size_t xlen = 0;
char *module_rule; /* rule module name */
int i;
yspec = clicon_dbspec_yang(h);
if ((module_rule = xml_find_body(xrule, "module-name")) == NULL)
goto nomatch;
/* 6b) Either (1) the rule does not have a "rule-type" defined or
(2) the "rule-type" is "data-node" and the "path" matches the
requested data node, action node, or notification node. */
if ((pathobj = xml_find_type(xrule, NULL, "path", CX_ELMNT)) == NULL){
if (xml_find_body(xrule, "rpc-name") || xml_find_body(xrule, "notification-name"))
goto nomatch;
}
access_operations = xml_find_body(xrule, "access-operations");
/* 6c) For a "read" access operation, the rule's "access-operations"
leaf has the "read" bit set or has the special value "*" */
if (!match_access(access_operations, "read", NULL))
goto nomatch;
if (pathobj == NULL){
/* 6a) The rule's "module-name" leaf is "*" or equals the name of
* the YANG module where the requested data node is defined.
* Go thru all children of xt,
*/
i = 0;
xp = NULL;
while ((xp = xml_child_each(xt, xp, CX_ELMNT)) != NULL) {
if (strcmp(module_rule, "*") != 0){
if (ys_module_by_xml(yspec, xp, &ymod) < 0)
goto done;
if (strcmp(yang_argument_get(ymod), module_rule) != 0)
continue;
}
i++;
if (nacm_data_read_action(xrule, xp, default_permit) < 0)
goto done;
}
if (i==0)
goto nomatch;
goto match;
}
path0 = clixon_trim2(xml_body(pathobj), " \t\n");
/* Create namespace context for with nacm namespace as default */
if (xml_nsctx_node(pathobj, &nsc0) < 0)
goto done;
/* instance-id requires canonical paths */
if (xpath2canonical(path0, nsc0, yspec, &path, NULL) < 0)
goto done;
/* XXX path may not be yang-consistent gives an error,... but retval =0 back,... */
if ((ret = clixon_xml_find_instance_id(xt, yspec, &xvec, &xlen, "%s", path)) < 0)
goto done;
switch (ret){
case 0: /* Actually some kind of failure, eg yang mismatch */
goto nomatch;
break;
case 1:
if (xlen == 0)
goto nomatch;
assert(xlen == 1);
xp = xvec[0]; /* XXX: loop? */
/* 6a) The rule's "module-name" leaf is "*" or equals the name of
* the YANG module where the requested data node is defined. */
if (strcmp(module_rule, "*") != 0){
if (ys_module_by_xml(yspec, xp, &ymod) < 0)
goto done;
if (strcmp(yang_argument_get(ymod), module_rule) != 0)
goto nomatch;
}
if (nacm_data_read_action(xrule, xp, default_permit) < 0)
goto done;
break;
} /* switch */
match:
retval = 1; /* match */
done:
if (xvec)
free(xvec);
return retval;
nomatch:
retval = 0;
goto done;
}
/*! Make nacm datanode and module rule read access validation
* Just purge nodes that fail validation (dont send netconf error message)
* @param[in] xt XML root tree with "config" label
@ -566,23 +717,26 @@ nacm_data_read_xr(cxobj *xt,
* @see nacm_rpc
*/
int
nacm_datanode_read(cxobj *xt,
cxobj **xrvec,
size_t xrlen,
char *username,
cxobj *xnacm)
nacm_datanode_read(clicon_handle h,
cxobj *xt,
cxobj **xrvec,
size_t xrlen,
char *username,
cxobj *xnacm)
{
int retval = -1;
cxobj **gvec = NULL; /* groups */
size_t glen;
cxobj *xr;
char *gname;
cxobj **rlistvec = NULL; /* rule-list */
size_t rlistlen;
int i;
int j;
char *read_default = NULL;
cxobj *xrule;
char *action;
int matches = 0;
cvec *nsc = NULL;
cxobj *xrule;
int ret;
/* Create namespace context for with nacm namespace as default */
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
@ -610,37 +764,53 @@ nacm_datanode_read(cxobj *xt,
clicon_err(OE_XML, EINVAL, "No nacm read-default rule");
goto done;
}
for (i=0; i<xrlen; i++){ /* Loop through requested nodes */
xr = xrvec[i]; /* requested node XR */
/* Loop through rule-list (steps 5,6,7) to find match of requested node
*/
xrule = NULL;
/* Skip if no groups */
if (glen && nacm_data_read_xr(xt, xr, gvec, glen, rlistvec, rlistlen,
nsc, &xrule) < 0)
matches = 0;
for (i=0; i<rlistlen; i++){ /* Loop through rule list */
cxobj *rlist;
cxobj **rvec = NULL; /* rules */
size_t rlen;
rlist = rlistvec[i];
/* Loop through user's group to find match in this rule-list */
for (j=0; j<glen; j++){
gname = xml_find_body(gvec[j], "name");
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
break; /* found */
}
if (j==glen) /* not found */
continue;
/* 6. For each rule-list entry found, process all rules, in order,
until a rule that matches the requested access operation is
found. (see 6 sub rules in nacm_rule_datanode
*/
if (xpath_vec(rlist, nsc, "rule", &rvec, &rlen) < 0)
goto done;
if (xrule){ /* xrule match requested node xr */
if ((action = xml_find_body(xrule, "action")) == NULL)
continue;
if (strcmp(action, "deny")==0){
if (xml_purge(xr) < 0)
goto done;
}
else if (strcmp(action, "permit")==0)
;/* XXX recursively find denies in xr and purge them
* ie call nacm_data_read_xr recursively?
*/
for (j=0; j<rlen; j++){ /* Loop through rules */
xrule = rvec[j];
if ((ret = nacm_data_read_xrule(h, xt, xrule, strcmp(read_default,"permit")==0)) < 0)
goto done;
if (ret > 0)
matches++;
}
else{ /* no rule matching xr, apply default */
/*11. For a "read" access operation, if the "read-default" leaf is set
to "permit", then include the requested data node in the reply;
otherwise, do not include the requested data node or any of its
descendants in the reply.*/
if (strcmp(read_default, "deny")==0)
if (xml_purge(xr) < 0)
goto done;
if (rvec){
free(rvec);
rvec=NULL;
}
} /* xr */
}
/* If there were accpet rules in some matches, remove everything else */
if (matches && strcmp(read_default, "deny") == 0)
if (xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL) < 0)
goto done;
if (strcmp(read_default, "deny") == 0){
if (matches == 0)
goto step9; /* delete all */
else /* Accept rules that override default delete: delete everything not marked) */
if (xml_tree_prune_flagged_sub(xt, XML_FLAG_MARK, 1, NULL) < 0)
goto done;
}
/* reset flag */
if (xml_apply(xt, CX_ELMNT, (xml_applyfn_t*)xml_flag_reset, (void*)XML_FLAG_MARK) < 0)
goto done;
goto ok;
/* 8. At this point, no matching rule was found in any rule-list
entry. */
@ -670,9 +840,10 @@ nacm_datanode_read(cxobj *xt,
/*! Make nacm datanode and module rule write access validation
* The operations of NACM are: create, read, update, delete, exec
* where write is short-hand for create+delete+update
* @param[in] xt XML root tree with "config" label. XXX?
* @param[in] xr XML requestor node (part of xt)
* @param[in] op NACM access of xr
* @param[in] h Clixon handle
* @param[in] xt XML root tree with "config" label.
* @param[in] xreq XML requestor node (part of xt)
* @param[in] op NACM access of xreq
* @param[in] username User making access
* @param[in] xnacm NACM xml tree
* @param[out] cbret Cligen buffer result. Set to an error msg if retval=0.
@ -684,14 +855,15 @@ nacm_datanode_read(cxobj *xt,
* @see nacm_rpc
*/
int
nacm_datanode_write(cxobj *xt,
cxobj *xr,
nacm_datanode_write(clicon_handle h,
cxobj *xt,
cxobj *xreq,
enum nacm_access access,
char *username,
cxobj *xnacm,
cbuf *cbret)
{
int retval = -1;
int retval = -1;
cxobj **gvec = NULL; /* groups */
size_t glen;
cxobj **rlistvec = NULL; /* rule-list */
@ -749,7 +921,7 @@ nacm_datanode_write(cxobj *xt,
*/
for (j=0; j<rlen; j++){ /* Loop through rules */
xrule = rvec[j];
if ((match = nacm_rule_datanode(xt, xr, xrule, access)) < 0)
if ((match = nacm_rule_datanode(h, xt, xreq, xrule, access)) < 0)
goto done;
if (match) /* match */
break;

View file

@ -686,7 +686,10 @@ api_path2xpath_cvv(cvec *api_path,
if (xml_nsctx_get_prefix(nsc, namespace, &xprefix) == 0){
xprefix = yang_find_myprefix(y);
clicon_debug(1, "%s prefix not found add it %s", __FUNCTION__, xprefix);
/* not found, add it to nsc */
/* not found, add it to nsc
* XXX: do we always have to add it? It could be default?
*/
// if (xml2prefix(x1, namespace, &pexisting));
if (xml_nsctx_add(nsc, xprefix, namespace) < 0)
goto done;
}
@ -1358,7 +1361,8 @@ instance_id_resolve(clixon_path *cplist,
}
}
if ((yc = yang_find_datanode(yt, cp->cp_id)) == NULL){
clicon_err(OE_YANG, ENOENT, "Corresponding yang node for id not found");
clicon_err(OE_YANG, ENOENT, "Corresponding yang node for id:%s not found",
cp->cp_id);
goto fail;
}
cp->cp_yang = yc;
@ -1411,9 +1415,9 @@ instance_id_resolve(clixon_path *cplist,
* @retval 1 OK with found xml nodes in xvec (if any)
*/
static int
clixon_path_search(cxobj *xt,
yang_stmt *yt,
clixon_path *cplist,
clixon_path_search(cxobj *xt,
yang_stmt *yt,
clixon_path *cplist,
clixon_xvec **xvec0)
{
int retval = -1;
@ -1480,8 +1484,8 @@ clixon_path_search(cxobj *xt,
* @param[out] xvec Vector of xml-trees. Vector must be free():d after use
* @param[in] format Format string for api-path syntax
* @retval -1 Error
* @retval 0 Nomatch
* @retval 1 OK with found xml nodes in xvec (if any)
* @retval 0 Non-fatal failure, yang bind failures, etc,
* @retval 1 OK with found xml nodes in xvec (if any) (xvec contains matches)
* Reasons for nomatch (retval = 0) are:
* - Modulename in api-path does not correspond to existing module
* - Modulename not defined for top-level id.
@ -1572,7 +1576,7 @@ clixon_xml_find_api_path(cxobj *xt,
* @param[out] xlen Returns length of vector in return value
* @param[in] format Format string for api-path syntax
* @retval -1 Error
* @retval 0 Nomatch
* @retval 0 Non-fatal failure, yang bind failures, etc,
* @retval 1 OK with found xml nodes in xvec (if any)
* Reasons for nomatch (retval = 0) are:
* - Modulename in api-path does not correspond to existing module
@ -1589,7 +1593,7 @@ clixon_xml_find_api_path(cxobj *xt,
* }
* clixon_xvec_free(xvec);
* @endcode
* @note canonical namespace contexts are used, seexpath2canonical
* @note canonical namespace contexts are used, see xpath2canonical
* @see clixon_xml_find_api_path for RESTCONF api-paths
* @see RFC7950 Sec 9.13
*/
@ -1639,7 +1643,7 @@ clixon_xml_find_instance_id(cxobj *xt,
if (ret == 0)
goto fail;
/* Convert to api xvec format */
if (clixon_xvec_extract(xv, xvec, xlen) < 0)
if (xv && clixon_xvec_extract(xv, xvec, xlen) < 0)
goto done;
retval = 1;
done:

View file

@ -270,9 +270,7 @@ populate_self_top(cxobj *xt,
/*! Find yang spec association of tree of XML nodes
*
* Populate xt:s children as top-level symbols
* This may be unnecessary if yspec is set on manual creation. Also note that for incoming or
* outgoing RPC need specialized function. maybe it can be built into the same function, but
* you dont know whether it is input or output rpc.
* This may be unnecessary if yspec is set on manual creation: x=xml_new(); xml_spec_set(x,y)
* @param[in] xt XML tree node
* @param[in] yspec Yang spec
* @param[out] xerr Reason for failure, or NULL

View file

@ -763,6 +763,7 @@ xml_tree_prune_flagged_sub(cxobj *xt,
return retval;
}
#endif
/*! Prune everything that passes test
* @param[in] xt XML tree with some node marked
* @param[in] flag Which flag to test for
@ -799,7 +800,7 @@ xml_tree_prune_flagged(cxobj *xt,
return retval;
}
/*! Add prefix:namespace pair to xml node, set cache, prefix, etc
/*! Add prefix:namespace pair to xml node, set cache, etc
* @param[in] x XML node whose namespace should change
* @param[in] xp XML node where namespace attribute should be declared (can be same)
* @param[in] prefix1 Use this prefix
@ -835,9 +836,6 @@ add_namespace(cxobj *x,
goto done;
xml_sort(xp, NULL); /* Ensure attr is first / XXX xml_insert? */
/* 5. Add prefix to x, if any */
if (prefix && xml_prefix_set(x, prefix) < 0)
goto done;
retval = 0;
done:
return retval;
@ -882,6 +880,10 @@ xml_namespace_change(cxobj *x,
xp = xml_parent(x);
if (add_namespace(x, xp, prefix, namespace) < 0)
goto done;
/* Add prefix to x, if any */
if (prefix && xml_prefix_set(x, prefix) < 0)
goto done;
}
ok:
retval = 0;
@ -946,6 +948,9 @@ xml_default(cxobj *xt,
}
if (add_namespace(xc, xc, prefix, namespace) < 0)
goto done;
/* Add prefix to x, if any */
if (prefix && xml_prefix_set(xc, prefix) < 0)
goto done;
}
}
@ -1114,7 +1119,6 @@ xml2xpath(cxobj *x,
return retval;
}
/*! Check if the module tree x is in is assigned right XML namespace, assign if not
* @param[in] x XML node
*(0. You should probably find the XML root and apply this function to that.)
@ -1160,50 +1164,24 @@ xmlns_assign(cxobj *x)
return retval;
}
/*! Given a src node x0 and a target node x1, assign (optional) prefix and namespace
* @param[in] x0 Source XML tree
* @param[in] x1 Target XML tree
* 1. Find N=namespace(x0)
* 2. Detect if N is declared in x1 parent
* 3. If yes, assign prefix to x1
* 4. If no, create new prefix/namespace binding and assign that to x1p (x1 if x1p is root)
* 5. Add prefix to x1, if any
* 6. Ensure x1 cache is updated
* @note switch use of x0 and x1 compared to datastore text_modify
* @see xml2ns
* XXX: fail handling: if (netconf_data_missing(cbret, NULL, "Data does not exist; cannot delete resource") < 0)
goto done;
/*! Given a src element node x0 and a target node x1, assign (optional) prefix and namespace
* @see assign_namespace_element this is a subroutine
*/
int
assign_namespaces(cxobj *x0, /* source */
cxobj *x1, /* target */
cxobj *x1p)
static int
assign_namespace(cxobj *x0, /* source */
cxobj *x1, /* target */
cxobj *x1p,
int isroot,
char *namespace,
char *prefix0)
{
int retval = -1;
char *namespace = NULL;
char *prefix0 = NULL;;
char *prefix1 = NULL;
char *prefixb = NULL; /* identityref body prefix */
char *pexist = NULL;
cvec *nsc0 = NULL;
cvec *nsc = NULL;
int isroot;
char *pexist = NULL;
yang_stmt *y;
/* XXX: need to identify root better than hiereustics and strcmp,... */
isroot = xml_parent(x1p)==NULL &&
(strcmp(xml_name(x1p), "config") == 0 || strcmp(xml_name(x1p), "top") == 0)&&
xml_prefix(x1p)==NULL;
/* 1. Find N=namespace(x0) */
prefix0 = xml_prefix(x0);
if (xml2ns(x0, prefix0, &namespace) < 0)
goto done;
if (namespace == NULL){
clicon_err(OE_XML, ENOENT, "No namespace found for prefix:%s",
prefix0?prefix0:"NULL");
goto done;
}
/* 2a. Detect if namespace is declared in x1 target parent */
if (xml2prefix(x1p, namespace, &pexist) == 1){
/* Yes, and it has prefix pexist */
@ -1235,7 +1213,7 @@ assign_namespaces(cxobj *x0, /* source */
if (xml2prefix(x1, namespace, &pexist) == 1){
/* Yes it exists, but is it equal? */
if (clicon_strcmp(pexist, prefix0) == 0)
; /* Equal, reuse */
; /* Equal, reuse */
else{ /* namespace exist, but not equal, use existing */
/* Add prefix to x1, if any */
if (pexist && xml_prefix_set(x1, pexist) < 0)
@ -1253,31 +1231,133 @@ assign_namespaces(cxobj *x0, /* source */
}
}
else{
char *ptmp;
if ((y = xml_spec(x0)) == NULL){
clicon_err(OE_YANG, ENOENT, "XML %s does not have yang spec",
xml_name(x0));
goto done;
}
if ((prefix1 = strdup(yang_find_myprefix(y))) == NULL){
/* Find local (imported) prefix for that module namespace */
if (yang_find_prefix_by_namespace(y, namespace, &ptmp) < 0)
goto done;
if ((prefix1 = strdup(ptmp)) == NULL){
clicon_err(OE_UNIX, errno, "strdup");
goto done;
}
}
}
if (add_namespace(x1, x1, prefix1, namespace) < 0)
goto done;
if (prefix1 && xml_prefix_set(x1, prefix1) < 0)
goto done;
}
ok:
/* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
retval = 0;
done:
if (prefixb)
free(prefixb);
if (prefix1)
free(prefix1);
return retval;
}
/*! Given a src element node x0 and a target node x1, assign (optional) prefix and namespace
* @param[in] x0 Source XML tree
* @param[in] x1 Target XML tree
* @retval 0 OK
* @retval -1 OK
* 1. Find N=namespace(x0)
* 2. Detect if N is declared in x1 parent
* 3. If yes, assign prefix to x1
* 4. If no, create new prefix/namespace binding and assign that to x1p (x1 if x1p is root)
* 5. Add prefix to x1, if any
* 6. Ensure x1 cache is updated
* @note switch use of x0 and x1 compared to datastore text_modify
* @see xml2ns
*/
int
assign_namespace_element(cxobj *x0, /* source */
cxobj *x1, /* target */
cxobj *x1p)
{
int retval = -1;
char *namespace = NULL;
char *prefix0 = NULL;;
int isroot;
/* XXX: need to identify root better than hiereustics and strcmp,... */
isroot = xml_parent(x1p)==NULL &&
(strcmp(xml_name(x1p), "config") == 0 || strcmp(xml_name(x1p), "top") == 0)&&
xml_prefix(x1p)==NULL;
/* 1. Find N=namespace(x0) in x0 element */
prefix0 = xml_prefix(x0);
if (xml2ns(x0, prefix0, &namespace) < 0)
goto done;
if (namespace == NULL){
clicon_err(OE_XML, ENOENT, "No namespace found for prefix:%s",
prefix0?prefix0:"NULL");
goto done;
}
if (assign_namespace(x0, x1, x1p, isroot, namespace, prefix0) < 0)
goto done;
/* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
retval = 0;
done:
return retval;
}
/* If origin body has namespace definitions, copy them. The reason is that
* some bodies rely on namespace prefixes, such as NACM path, but there is
* no way we can now this here.
* However, this may lead to namespace collisions if these prefixes are not
* canonical, and may collide with the assign_namespace_element() above (but that
* is for element sysmbols)
*/
int
assign_namespace_body(cxobj *x0, /* source */
char *x0bstr,
cxobj *x1) /* target */
{
int retval = -1;
char *namespace = NULL;
char *name;
char *prefix;
char *prefix0 = NULL;;
char *pexisting = NULL;;
cxobj *xa;
xa = NULL;
while ((xa = xml_child_each(x0, xa, CX_ATTR)) != NULL) {
prefix = xml_prefix(xa);
name = xml_name(xa);
namespace = xml_value(xa);
if ((strcmp(name, "xmlns")==0 && prefix==NULL) ||
(prefix != NULL && strcmp(prefix, "xmlns")==0)){
if (prefix == NULL)
prefix0 = NULL;
else
prefix0 = name;
if (strcmp(namespace, NETCONF_BASE_NAMESPACE) ==0 ||
strcmp(namespace, YANG_XML_NAMESPACE) ==0)
continue;
/* Detect if prefix:namespace is declared already? */
if (xml2prefix(x1, namespace, &pexisting) == 1){
/* Yes, and it has prefix pexist */
if (clicon_strcmp(pexisting, prefix0) ==0)
continue;
}
if (add_namespace(x1, x1, prefix0, namespace) < 0)
goto done;
}
}
/* 6. Ensure x1 cache is updated (I think it is done w xmlns_set above) */
retval = 0;
done:
return retval;
}
/*! Merge a base tree x0 with x1 with yang spec y
* @param[in] x0 Base xml tree (can be NULL in add scenarios)
* @param[in] y0 Yang spec corresponding to xml-node x0. NULL if x0 is NULL
@ -1334,7 +1414,7 @@ xml_merge1(cxobj *x0, /* the target */
if (xml_value_set(x0b, x1bstr) < 0)
goto done;
}
if (assign_namespaces(x1, x0, x0p) < 0)
if (assign_namespace_element(x1, x0, x0p) < 0)
goto done;
} /* if LEAF|LEAF_LIST */
else { /* eg Y_CONTAINER, Y_LIST */
@ -1343,7 +1423,7 @@ xml_merge1(cxobj *x0, /* the target */
goto done;
xml_spec_set(x0, y0);
}
if (assign_namespaces(x1, x0, x0p) < 0)
if (assign_namespace_element(x1, x0, x0p) < 0)
goto done;
if ((second_wave = calloc(xml_child_nr(x1), sizeof(*second_wave))) == NULL){
clicon_err(OE_UNIX, errno, "calloc");

View file

@ -152,11 +152,13 @@ xml_nsctx_get_prefix(cvec *cvv,
while ((cv = cvec_each(cvv, cv)) != NULL){
if ((ns = cv_string_get(cv)) != NULL &&
strcmp(ns, namespace) == 0){
*prefix = cv_name_get(cv); /* can be NULL */
if (prefix)
*prefix = cv_name_get(cv); /* can be NULL */
return 1;
}
}
*prefix = NULL;
if (prefix)
*prefix = NULL;
return 0;
}
@ -493,7 +495,6 @@ xmlns_set(cxobj *x,
goto done;
if (xml_prefix_set(xa, "xmlns") < 0)
goto done;
}
else{ /* xmlns="<uri>" */
if ((xa = xml_new("xmlns", x, CX_ATTR)) == NULL)

View file

@ -755,12 +755,18 @@ xml_search_yang(cxobj *xp,
char *indexvar,
clixon_xvec *xvec)
{
int retval = -1;
cxobj *xa;
int low = 0;
int upper = xml_child_nr(xp);
int sorted = 1;
int yangi;
if (xp == NULL){
clicon_err(OE_XML, EINVAL, "xp is NULL");
goto done;
}
upper = xml_child_nr(xp);
/* Assume if there are any attributes, they are first in the list, mask
them by raising low to skip them */
for (low=0; low<upper; low++)
@ -775,7 +781,12 @@ xml_search_yang(cxobj *xp,
if (yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST)
sorted = (yang_find(yc, Y_ORDERED_BY, "user") == NULL);
yangi = yang_order(yc);
return xml_search_binary(xp, x1, sorted, yangi, low, upper, skip1, indexvar, xvec);
if (xml_search_binary(xp, x1, sorted, yangi, low, upper, skip1, indexvar, xvec) < 0)
goto done;
retval = 0;
done:
return retval;
}
/*! Insert xn in xp:s sorted child list (special case of ordered-by user)
@ -1281,6 +1292,10 @@ xml_find_index_yang(cxobj *xp,
int revert = 0;
char *indexvar = NULL;
if (xp == NULL){
clicon_err(OE_XML, EINVAL, "xp is NULL");
goto done;
}
name = yang_argument_get(yc);
if ((cb = cbuf_new()) == NULL){
clicon_err(OE_UNIX, errno, "cbuf_new");

View file

@ -203,13 +203,20 @@ clixon_xvec_i(clixon_xvec *xv,
* Used in glue code between clixon_xvec code and cxobj **, size_t code, may go AWAY?
* @param[in] xv XML tree vector
* @param[out] xvec XML object vector
* @retval 0
* @retval 0 OK
* @retval -1 Error
*/
int
clixon_xvec_extract(clixon_xvec *xv,
cxobj ***xvec,
size_t *xlen)
{
int retval = -1;
if (xv == NULL){
clicon_err(OE_XML, EINVAL, "xv is NULL");
goto done;
}
*xvec = xv->xv_vec;
*xlen = xv->xv_len;
if (xv->xv_vec != NULL){
@ -217,7 +224,9 @@ clixon_xvec_extract(clixon_xvec *xv,
xv->xv_max = 0;
xv->xv_vec = NULL;
}
return 0;
retval = 0;
done:
return retval;
}
/*! Append a new xml tree to an existing xml vector last in the list

View file

@ -873,7 +873,9 @@ yang_find_mynamespace(yang_stmt *ys)
* @retval -1 found
* @note prefix NULL is not returned, if own module, then return its prefix
* @code
* char *pfx = yang_find_prefix_by_namespace(ys, "urn:example:clixon", &prefix);
* char *prefix = NULL;
* if (yang_find_prefix_by_namespace(ys, "urn:example:clixon", &prefix) < 0)
* err;
* @endcode
*/
int
@ -1103,7 +1105,7 @@ yang_key2str(int keyword)
}
/*! Find top data node among all modules by namespace in xml tree
* @param[in] ysp Yang specification
* @param[in] yspec Yang specification
* @param[in] xt XML node
* @param[out] ymod Yang module (NULL if not found)
* @retval 0 OK
@ -1112,7 +1114,7 @@ yang_key2str(int keyword)
* Note that xt xml symbol may belong to submodule of ymod
*/
int
ys_module_by_xml(yang_stmt *ysp,
ys_module_by_xml(yang_stmt *yspec,
cxobj *xt,
yang_stmt **ymodp)
{
@ -1130,7 +1132,7 @@ ys_module_by_xml(yang_stmt *ysp,
if (namespace == NULL)
goto ok;
/* We got the namespace, now get the module */
ym = yang_find_module_by_namespace(ysp, namespace);
ym = yang_find_module_by_namespace(yspec, namespace);
/* Set result param */
if (ymodp && ym)
*ymodp = ym;
@ -1892,6 +1894,58 @@ ys_populate_unknown(clicon_handle h,
return retval;
}
/*! Populate modules / submodules
* @param[in] h Clicon handle
* @param[in] ys The yang statement (module/submodule) to populate.
*
* Check RFC 7950: 7.1.4: All prefixes, including the prefix for the module itself,
* MUST be unique within the module or submodule.
*/
static int
ys_populate_module_submodule(clicon_handle h,
yang_stmt *ym)
{
int retval = -1;
yang_stmt *yp;
yang_stmt *yi;
yang_stmt *yi2; /* remaining */
char *p0 = NULL;
char *pi;
char *pi2; /* remaining */
/* Modules but not submodules have prefixes */
if ((yp = yang_find(ym, Y_PREFIX, NULL)) != NULL)
p0 = yang_argument_get(yp);
yi = NULL;
while ((yi = yn_each(ym, yi)) != NULL) {
if (yang_keyword_get(yi) != Y_IMPORT)
continue;
yp = yang_find(yi, Y_PREFIX, NULL);
pi = yang_argument_get(yp);
if (p0 && strcmp(p0, pi) == 0){ /* Check top-level */
clicon_err(OE_YANG, EFAULT, "Prefix %s in module %s is not unique but should be (see RFC 7950 7.1.4)",
pi, yang_argument_get(ym));
goto done;
}
/* Check rest of imports */
yi2 = yi;
while ((yi2 = yn_each(ym, yi2)) != NULL) {
if (yang_keyword_get(yi2) != Y_IMPORT)
continue;
yp = yang_find(yi2, Y_PREFIX, NULL);
pi2 = yang_argument_get(yp);
if (strcmp(pi2, pi) == 0){
clicon_err(OE_YANG, EFAULT, "Prefix %s in module %s is not unique but should be (see RFC 7950 7.1.4)",
pi, yang_argument_get(ym));
goto done;
}
}
}
retval = 0;
done:
return retval;
}
/*! Populate with cligen-variables, default values, etc. Sanity checks on complete tree.
*
* @param[in] ys Yang statement
@ -1913,24 +1967,29 @@ ys_populate(yang_stmt *ys,
clicon_handle h = (clicon_handle)arg;
switch(ys->ys_keyword){
case Y_LIST:
if (ys_populate_list(h, ys) < 0)
goto done;
break;
case Y_RANGE:
if (ys_populate_range(h, ys) < 0)
case Y_IDENTITY:
if (ys_populate_identity(h, ys, NULL) < 0)
goto done;
break;
case Y_LENGTH:
if (ys_populate_length(h, ys) < 0)
goto done;
break;
case Y_TYPE:
if (ys_populate_type(h, ys) < 0)
case Y_LIST:
if (ys_populate_list(h, ys) < 0)
goto done;
break;
case Y_IDENTITY:
if (ys_populate_identity(h, ys, NULL) < 0)
case Y_MODULE:
case Y_SUBMODULE:
if (ys_populate_module_submodule(h, ys) < 0)
goto done;
break;
case Y_RANGE:
if (ys_populate_range(h, ys) < 0)
goto done;
break;
case Y_TYPE:
if (ys_populate_type(h, ys) < 0)
goto done;
break;
case Y_UNIQUE:
@ -1949,7 +2008,6 @@ ys_populate(yang_stmt *ys,
return retval;
}
/*! Run after grouping expand and augment
* @see ys_populate run before grouping expand and augment
*/

View file

@ -978,7 +978,7 @@ ys_list_check(clicon_handle h,
/*! Parse top yang module including all its sub-modules. Expand and populate yang tree
*
* Perform secondary actions after yang parsing. These actions cannot be made at
* compile-time for various reasons.
* parse-time for various reasons.
* These includes:
* - Detect imported yang specs that are not loaded and load and parse them too
* - Check cardinality of yang (that nr of children match)
@ -1015,14 +1015,17 @@ yang_parse_post(clicon_handle h,
goto done;
/* 3: Check features: check if enabled and remove disabled features */
for (i=modnr; i<yang_len_get(yspec); i++) /* XXX */
for (i=modnr; i<yang_len_get(yspec); i++)
if (yang_features(h, yspec->ys_stmt[i]) < 0)
goto done;
/* 4: Go through parse tree and populate it with cv types */
for (i=modnr; i<yang_len_get(yspec); i++)
for (i=modnr; i<yang_len_get(yspec); i++){
if (ys_populate(yspec->ys_stmt[i], h) < 0) /* Alt: make a yang_apply0 */
goto done;
if (yang_apply(yspec->ys_stmt[i], -1, ys_populate, (void*)h) < 0)
goto done;
}
/* 5: Resolve all types: populate type caches. Requires eg length/range cvecs
* from ys_populate step.

View file

@ -526,9 +526,9 @@ outoflength(uint64_t u64,
* @param[in] regexps Vector of compiled regexps
* @param[out] reason If given, and return value is 0, contains malloced str
* @retval -1 Error (fatal), with errno set to indicate error
* @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
* @retval 1 Validation OK
* @retval -1 Error (fatal), with errno set to indicate error
* @retval 0 Validation not OK, malloced reason is returned. Free reason with free()
* @retval 1 Validation OK
* @note reason if given must be freed by caller
* @see cv_validate Corresponding type check in cligen
*/
@ -661,7 +661,7 @@ cv_validate1(clicon_handle h,
found = 0;
yi = NULL;
if (str != NULL) {
str = clixon_trim2(str, " \t\n"); /* May be misplaced, strip earlier? */
// str = clixon_trim2(str, " \t\n"); /* May be misplaced, strip earlier? */
while ((yi = yn_each(yrestype, yi)) != NULL){
if (yang_keyword_get(yi) != Y_ENUM)
continue;

View file

@ -39,7 +39,7 @@ cat <<EOF > $fyang
module nacm-example{
yang-version 1.1;
namespace "urn:example:nacm";
prefix nacm;
prefix nex;
import clixon-example {
prefix ex;
}

View file

@ -25,7 +25,7 @@ cat <<EOF > $fyang
module nacm-example{
yang-version 1.1;
namespace "urn:example:nacm";
prefix nacm;
prefix nex;
import clixon-example {
prefix ex;
}

View file

@ -39,7 +39,7 @@ cat <<EOF > $fyang
module nacm-example{
yang-version 1.1;
namespace "urn:example:nacm";
prefix nacm;
prefix nex;
import clixon-example {
prefix ex;
}

View file

@ -42,7 +42,7 @@ cat <<EOF > $fyang
module nacm-example{
yang-version 1.1;
namespace "urn:example:nacm";
prefix nacm;
prefix nex;
import clixon-example {
prefix ex;
}
@ -112,7 +112,7 @@ RULES=$(cat <<EOF
<translate xmlns="urn:example:clixon"><translate><k>key43</k><value>val43</value></translate></translate>
EOF
)
# NOTE use of translate^ has nothing to do with the CLI translate semantics
# NOTE use of translate^ has nothing to do with the CLI translate semantics CONFUSING!
new "test params: -f $cfg -- -s"
@ -147,30 +147,26 @@ new "commit it"
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
new "enable nacm"
expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 ""
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:enable-nacm": true}' http://localhost/restconf/data/ietf-netconf-acm:nacm/enable-nacm)" 0 "HTTP/1.1 204 No Content"
#--------------- nacm enabled
#----READ access
#user:admin
new "admin read ok"
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]}}
'
expectpart "$(curl -u andy:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 'HTTP/1.1 200 OK' '{"clixon-example:translate":{"translate":\[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}\]}}'
new "admin read netconf ok"
expecteof "$clixon_netconf -U andy -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon" /></get-config></rpc>]]>]]>' '^<rpc-reply><data><translate xmlns="urn:example:clixon"><translate><k>key42</k><value>val42</value></translate><translate><k>key43</k><value>val43</value></translate></translate></data></rpc-reply>]]>]]>$'
new "admin read element ok"
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 '{"clixon-example:value":"val42"}
'
expectpart "$(curl -u andy:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 'HTTP/1.1 200 OK' '{"clixon-example:value":"val42"}'
new "admin read other module OK"
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"nacm-example:x":42}
'
expectpart "$(curl -u andy:bar -siS -X GET http://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 200 OK' '{"nacm-example:x":42}'
new "admin read state OK"
expecteq "$(curl -u andy:bar -sS -X GET http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state":{"op":["41","42","43"]}}
'
expectpart "$(curl -u andy:bar -siS -X GET http://localhost/restconf/data/clixon-example:state)" 0 'HTTP/1.1 200 OK' '{"clixon-example:state":{"op":\["41","42","43"\]}}'
new "admin read top ok (all)"
ret=$(curl -u andy:bar -sS -X GET http://localhost/restconf/data)
@ -183,65 +179,59 @@ fi
#user:limit
new "limit read ok"
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]}}
'
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 'HTTP/1.1 200 OK' '{"clixon-example:translate":{"translate":\[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}\]}}'
new "limit read netconf ok"
expecteof "$clixon_netconf -U wilma -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon"/></get-config></rpc>]]>]]>' '^<rpc-reply><data><translate xmlns="urn:example:clixon"><translate><k>key42</k><value>val42</value></translate><translate><k>key43</k><value>val43</value></translate></translate></data></rpc-reply>]]>]]>$'
new "limit read element ok"
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 '{"clixon-example:value":"val42"}
'
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 'HTTP/1.1 200 OK' '{"clixon-example:value":"val42"}'
new "limit read other module fail"
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}} '
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 404 Not Found' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"invalid-value","error-severity":"error","error-message":"Instance does not exist"}}}'
new "limit read state OK"
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:state)" 0 '{"clixon-example:state":{"op":["41","42","43"]}}
'
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/clixon-example:state)" 0 'HTTP/1.1 200 OK' '{"clixon-example:state":{"op":\["41","42","43"\]}}'
new "limit read top ok (part)"
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data)" 0 '{"data":{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]},"clixon-example:state":{"op":["41","42","43"]}}}
'
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data)" 0 'HTTP/1.1 200 OK' '{"data":{"clixon-example:translate":{"translate":\[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}\]},"clixon-example:state":{"op":\["41","42","43"\]}}}'
#user:guest
new "guest read fail"
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} '
new "guest read forbidden"
expectpart "$(curl -u guest:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new "guest read netconf fail"
expecteof "$clixon_netconf -U guest -qf $cfg" 0 '<rpc><get-config><source><candidate/></source><filter type="xpath" select="/t:translate" xmlns:t="urn:example:clixon"/></get-config></rpc>]]>]]>' '^<rpc-reply><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>default deny</error-message></rpc-error></rpc-reply>]]>]]>$'
new "guest read element fail"
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} '
new "guest read element forbidden"
expectpart "$(curl -u guest:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate/translate=key42/value)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new "guest read other module fail"
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} '
expectpart "$(curl -u guest:bar -siS -X GET http://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new "guest read state fail"
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:state)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} '
expectpart "$(curl -u guest:bar -siS -X GET http://localhost/restconf/data/clixon-example:state)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
new "guest read top ok (part)"
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} '
expectpart "$(curl -u guest:bar -siS -X GET http://localhost/restconf/data)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
#------- RPC operation
new "admin rpc ok"
expecteq "$(curl -u andy:bar -s -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"78"}}' http://localhost/restconf/operations/clixon-example:example)" 0 '{"clixon-example:output":{"x":"78","y":"42"}}
'
expectpart "$(curl -u andy:bar -si -X POST -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"78"}}' http://localhost/restconf/operations/clixon-example:example)" 0 'HTTP/1.1 200 OK' '{"clixon-example:output":{"x":"78","y":"42"}}'
new "admin rpc netconf ok"
expecteof "$clixon_netconf -U andy -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><example xmlns="urn:example:clixon"><x>0</x></example></rpc>]]>]]>' 0 '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><x xmlns="urn:example:clixon">0</x><y xmlns="urn:example:clixon">42</y></rpc-reply>]]>]]>$'
new "limit rpc ok"
expecteq "$(curl -u wilma:bar -s -X POST http://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' )" 0 '{"clixon-example:output":{"x":"42","y":"42"}}
'
expectpart "$(curl -u wilma:bar -si -X POST http://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' )" 0 'HTTP/1.1 200 OK' '{"clixon-example:output":{"x":"42","y":"42"}}'
new "limit rpc netconf ok"
expecteof "$clixon_netconf -U wilma -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><example xmlns="urn:example:clixon"><x>0</x></example></rpc>]]>]]>' 0 '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><x xmlns="urn:example:clixon">0</x><y xmlns="urn:example:clixon">42</y></rpc-reply>]]>]]>$'
new "guest rpc fail"
expecteq "$(curl -u guest:bar -s -X POST http://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' )" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}} '
expectpart "$(curl -u guest:bar -si -X POST http://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":42}}' )" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
new "guest rpc netconf fail"
expecteof "$clixon_netconf -U guest -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><example xmlns="urn:example:clixon"><x>0</x></example></rpc>]]>]]>' 0 '^<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><rpc-error><error-type>application</error-type><error-tag>access-denied</error-tag><error-severity>error</error-severity><error-message>access denied</error-message></rpc-error></rpc-reply>]]>]]>$'
@ -249,19 +239,16 @@ expecteof "$clixon_netconf -U guest -qf $cfg" 0 '<rpc xmlns="urn:ietf:params:xml
#------------------ Set read-default permit
new "admin set read-default permit"
expecteq "$(curl -u andy:bar -sS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:read-default":"permit"}' http://localhost/restconf/data/ietf-netconf-acm:nacm/read-default)" 0 ""
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" -d '{"ietf-netconf-acm:read-default":"permit"}' http://localhost/restconf/data/ietf-netconf-acm:nacm/read-default)" 0 'HTTP/1.1 204 No Content'
new "limit read ok"
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 '{"clixon-example:translate":{"translate":[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}]}}
'
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/clixon-example:translate)" 0 'HTTP/1.1 200 OK' '{"clixon-example:translate":{"translate":\[{"k":"key42","value":"val42"},{"k":"key43","value":"val43"}\]}}'
new "limit read other module ok"
expecteq "$(curl -u wilma:bar -sS -X GET http://localhost/restconf/data/nacm-example:x)" 0 '{"nacm-example:x":42}
'
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/nacm-example:x)" 0 'HTTP/1.1 200 OK' '{"nacm-example:x":42}'
new "guest read state fail"
expecteq "$(curl -u guest:bar -sS -X GET http://localhost/restconf/data/clixon-example:state)" 0 '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}} '
expectpart "$(curl -u guest:bar -siS -X GET http://localhost/restconf/data/clixon-example:state)" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"default deny"}}}'
if [ $RC -ne 0 ]; then
new "Kill restconf daemon"

View file

@ -56,7 +56,7 @@ cat <<EOF > $fyang
module nacm-example{
yang-version 1.1;
namespace "urn:example:nacm";
prefix nacm;
prefix nex;
import clixon-example {
prefix ex;
}
@ -104,7 +104,7 @@ RULES=$(cat <<EOF
<rule>
<name>deny-update</name>
<module-name>nacm-example</module-name>
<access-operations>read update</access-operations>
<access-operations>update</access-operations>
<action>deny</action>
</rule>
</rule-list>
@ -121,7 +121,7 @@ RULES=$(cat <<EOF
<rule>
<name>deny-create-delete</name>
<module-name>nacm-example</module-name>
<access-operations>read create delete</access-operations>
<access-operations>create delete</access-operations>
<action>deny</action>
</rule>

View file

@ -59,7 +59,7 @@ cat <<EOF > $fyang
module nacm-example{
yang-version 1.1;
namespace "urn:example:nacm";
prefix nacm;
prefix nex;
import clixon-example {
prefix ex;
}

View file

@ -242,9 +242,7 @@ new 'B.3.5. "point" Parameter leaf-list 4 before 3'
expectpart "$(curl -si -X POST -H 'Content-Type: application/yang-data+json' -d '{"example-jukebox:extra":"4"}' http://localhost/restconf/data?insert=before\&point=%2Fexample-jukebox%3Aextra%3D3 )" 0 "HTTP/1.1 201 Created" 'Location: http://localhost/restconf/data/example-jukebox:extra=4'
new 'B.3.5. "insert/point" leaf-list check order (2,4,3,1)'
expectpart "$(curl -si -X GET http://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<extra xmlns="http://example.com/ns/example-jukebox">2</extra><extra xmlns="http://example.com/ns/example-jukebox">4</extra><extra xmlns="http://example.com/ns/example-jukebox">3</extra><extra xmlns="http://example.com/ns/example-jukebox">1</extra>'
if false; then # NYI
expectpart "$(curl -si -X GET http://localhost/restconf/data/example-jukebox:extra -H 'Accept: application/yang-data+xml')" 0 'HTTP/1.1 200 OK' '<extra xmlns="http://example.com/ns/example-jukebox">2</extra><extra xmlns="http://example.com/ns/example-jukebox" xmlns:jbox="http://example.com/ns/example-jukebox">4</extra><extra xmlns="http://example.com/ns/example-jukebox">3</extra><extra xmlns="http://example.com/ns/example-jukebox">1</extra>'
new "B.2.2. Detect Datastore Resource Entity-Tag Change" # XXX done except entity-changed
new 'B.3.3. "fields" Parameter'
@ -253,8 +251,6 @@ new 'B.3.7. "start-time" Parameter'
new 'B.3.8. "stop-time" Parameter'
new 'B.3.9. "with-defaults" Parameter'
fi # NYI
if [ $RC -ne 0 ]; then
new "Kill restconf daemon"
stop_restconf