* Fixed instance-id multiple results
* Fixed last NACM write paths
This commit is contained in:
parent
04017c97ba
commit
dc36282874
15 changed files with 806 additions and 211 deletions
|
|
@ -50,7 +50,7 @@ Expected: May 2020
|
|||
|
||||
### Corrected Bugs
|
||||
|
||||
* Fixed: Insertion of subtree leaf nodes were not made in the crrect place, always ended up last regardless of yang spec (if ordered-by system).
|
||||
* Fixed: Insertion of subtree leaf nodes were not made in the correct place, always ended up last regardless of yang spec (if ordered-by system).
|
||||
|
||||
## 4.4.0
|
||||
5 April 2020
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||
Copyright (C) 2017-2019 Olof Hagsand
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC
|
||||
Copyright (C) 2020 Olof Hagsand and Rubicon Communications, LLC (Netgate)
|
||||
|
||||
|
||||
CLIXON is dual license.
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ typedef struct {
|
|||
char csm_prompt[CLI_PROMPT_LEN]; /* Prompt for mode */
|
||||
int csm_nsyntax; /* Num syntax specs registered by plugin */
|
||||
parse_tree csm_pt; /* CLIgen parse tree */
|
||||
|
||||
} cli_syntaxmode_t;
|
||||
|
||||
/* Plugin group object. Just a single object, not list. part of cli_handle */
|
||||
|
|
|
|||
|
|
@ -273,11 +273,11 @@ expand_dbvar(void *h,
|
|||
/*! List files in a directory
|
||||
*/
|
||||
int
|
||||
expand_dir(char *dir,
|
||||
int *nr,
|
||||
expand_dir(char *dir,
|
||||
int *nr,
|
||||
char ***commands,
|
||||
mode_t flags,
|
||||
int detail)
|
||||
mode_t flags,
|
||||
int detail)
|
||||
{
|
||||
DIR *dirp;
|
||||
struct dirent *dp;
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ int xml_search_indexvar_binary_pos(cxobj *xp, char *indexvar, clixon_xvec *xvec,
|
|||
#endif
|
||||
int match_base_child(cxobj *x0, cxobj *x1c, yang_stmt *yc, cxobj **x0cp);
|
||||
int clixon_xml_find_index(cxobj *xp, yang_stmt *yp, char *namespace, char *name,
|
||||
cvec *cvk, clixon_xvec **xvec);
|
||||
int clixon_xml_find_pos(cxobj *xp, yang_stmt *yc, uint32_t pos, clixon_xvec **xvec);
|
||||
cvec *cvk, clixon_xvec *xvec);
|
||||
int clixon_xml_find_pos(cxobj *xp, yang_stmt *yc, uint32_t pos, clixon_xvec *xvec);
|
||||
|
||||
#endif /* _CLIXON_XML_SORT_H */
|
||||
|
|
|
|||
|
|
@ -310,6 +310,48 @@ nacm_rpc(char *rpc,
|
|||
goto done;
|
||||
}
|
||||
|
||||
/* Local struct for keeping preparation/compiled data in NACM data path code */
|
||||
struct prepvec{
|
||||
qelem_t pv_q;
|
||||
cxobj *pv_xrule;
|
||||
clixon_xvec *pv_xpathvec;
|
||||
};
|
||||
typedef struct prepvec prepvec;
|
||||
|
||||
/*! Delete all Upgrade callbacks
|
||||
*/
|
||||
int
|
||||
prepvec_free(prepvec *pv_list)
|
||||
{
|
||||
prepvec *pv;
|
||||
|
||||
while((pv = pv_list) != NULL) {
|
||||
DELQ(pv, pv_list, prepvec *);
|
||||
if (pv->pv_xpathvec)
|
||||
clixon_xvec_free(pv->pv_xpathvec);
|
||||
free(pv);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
prepvec *
|
||||
prepvec_add(prepvec **pv_listp,
|
||||
cxobj *xrule)
|
||||
{
|
||||
prepvec *pv;
|
||||
|
||||
if ((pv = malloc(sizeof(*pv))) == NULL){
|
||||
clicon_err(OE_UNIX, errno, "malloc");
|
||||
return NULL;
|
||||
}
|
||||
memset(pv, 0, sizeof(*pv));
|
||||
ADDQ(pv, *pv_listp);
|
||||
pv->pv_xrule = xrule;
|
||||
if ((pv->pv_xpathvec = clixon_xvec_new()) == NULL)
|
||||
return NULL;
|
||||
return pv;
|
||||
}
|
||||
|
||||
/*! Prepare datastructures before running through XML tree
|
||||
* Save rules in a "cache"
|
||||
* These rules match:
|
||||
|
|
@ -318,21 +360,21 @@ nacm_rpc(char *rpc,
|
|||
* Also make instance-id lookups on top object for each rule. Assume at most one result
|
||||
*/
|
||||
static int
|
||||
nacm_datanode_prepare(clicon_handle h,
|
||||
cxobj *xt,
|
||||
enum nacm_access access,
|
||||
cxobj **gvec,
|
||||
size_t glen,
|
||||
cxobj **rlistvec,
|
||||
size_t rlistlen,
|
||||
cvec *nsc,
|
||||
clixon_xvec *rulevec,
|
||||
clixon_xvec *xpathvec)
|
||||
nacm_datanode_prepare(clicon_handle h,
|
||||
cxobj *xt,
|
||||
enum nacm_access access,
|
||||
cxobj **gvec,
|
||||
size_t glen,
|
||||
cxobj **rlistvec,
|
||||
size_t rlistlen,
|
||||
cvec *nsc,
|
||||
prepvec **pv_listp)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *rlist;
|
||||
int i;
|
||||
int j;
|
||||
int k;
|
||||
char *gname;
|
||||
cxobj **rvec = NULL; /* rules */
|
||||
size_t rlen;
|
||||
|
|
@ -346,6 +388,7 @@ nacm_datanode_prepare(clicon_handle h,
|
|||
cxobj **xvec = NULL;
|
||||
int xlen = 0;
|
||||
int ret;
|
||||
prepvec *pv;
|
||||
|
||||
yspec = clicon_dbspec_yang(h);
|
||||
for (i=0; i<rlistlen; i++){ /* Loop through rule list */
|
||||
|
|
@ -368,7 +411,6 @@ nacm_datanode_prepare(clicon_handle h,
|
|||
xrule = rvec[j];
|
||||
/* 6c) For a "read" access operation, the rule's "access-operations"
|
||||
leaf has the "read" bit set or has the special value "*" */
|
||||
|
||||
access_operations = xml_find_body(xrule, "access-operations");
|
||||
switch (access){
|
||||
case NACM_READ:
|
||||
|
|
@ -406,7 +448,8 @@ nacm_datanode_prepare(clicon_handle h,
|
|||
if ((pathobj = xml_find_type(xrule, NULL, "path", CX_ELMNT)) == NULL){
|
||||
if (xml_find_body(xrule, "rpc-name") || xml_find_body(xrule, "notification-name"))
|
||||
continue;
|
||||
if (clixon_xvec_append(xpathvec, NULL) < 0) /* XXX: vector of vectors? */
|
||||
/* Here a new xrule is found, add it */
|
||||
if (prepvec_add(pv_listp, xrule) == NULL)
|
||||
goto done;
|
||||
}
|
||||
else{
|
||||
|
|
@ -421,10 +464,13 @@ nacm_datanode_prepare(clicon_handle h,
|
|||
goto done;
|
||||
if (ret == 0)
|
||||
continue;
|
||||
if (xlen > 1)
|
||||
clicon_log(LOG_WARNING, "%s path:%s Clixon only supports single returns, this had: %d", __FUNCTION__, path, xlen);
|
||||
if (clixon_xvec_append(xpathvec, xvec?xvec[0]:NULL) < 0) /* XXX: vector of vectors? */
|
||||
/* Here a new xrule is found, add it */
|
||||
if ((pv = prepvec_add(pv_listp, xrule)) == NULL)
|
||||
goto done;
|
||||
for (k=0; k<xlen; k++){
|
||||
if (clixon_xvec_append(pv->pv_xpathvec, xvec[k]) < 0)
|
||||
goto done;
|
||||
}
|
||||
if (xvec){
|
||||
free(xvec);
|
||||
xvec = NULL;
|
||||
|
|
@ -438,8 +484,6 @@ nacm_datanode_prepare(clicon_handle h,
|
|||
path = NULL;
|
||||
}
|
||||
}
|
||||
if (clixon_xvec_append(rulevec, xrule) < 0) /* save it */
|
||||
goto done;
|
||||
}
|
||||
if (rvec){
|
||||
free(rvec);
|
||||
|
|
@ -472,15 +516,17 @@ nacm_datanode_prepare(clicon_handle h,
|
|||
* @retval 2 OK and rule matches permit
|
||||
*/
|
||||
static int
|
||||
nacm_data_write_xrule_xml(cxobj *xn,
|
||||
cxobj *xrule,
|
||||
cxobj *xp,
|
||||
yang_stmt *yspec)
|
||||
nacm_data_write_xrule_xml(cxobj *xn,
|
||||
cxobj *xrule,
|
||||
clixon_xvec *xpathvec,
|
||||
yang_stmt *yspec)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ymod;
|
||||
char *module_pattern; /* rule module name */
|
||||
char *action;
|
||||
cxobj *xp;
|
||||
int i;
|
||||
|
||||
if ((module_pattern = xml_find_body(xrule, "module-name")) == NULL)
|
||||
goto nomatch;
|
||||
|
|
@ -502,11 +548,16 @@ nacm_data_write_xrule_xml(cxobj *xn,
|
|||
goto deny;
|
||||
goto permit;
|
||||
}
|
||||
/* Check if ancestor is xp */
|
||||
if (xn != xp && !xml_isancestor(xn, xp))
|
||||
goto nomatch;
|
||||
if (strcmp(action, "deny")==0)
|
||||
goto deny;
|
||||
for (i=0; i<clixon_xvec_len(xpathvec); i++){
|
||||
xp = clixon_xvec_i(xpathvec, i);
|
||||
/* Check if ancestor is xp (for every xpathvec?) */
|
||||
if (xn == xp || xml_isancestor(xn, xp)){
|
||||
if (strcmp(action, "deny")==0)
|
||||
goto deny;
|
||||
goto permit;
|
||||
}
|
||||
}
|
||||
goto nomatch;
|
||||
permit:
|
||||
retval = 2; /* rule match and permit */
|
||||
done:
|
||||
|
|
@ -536,35 +587,40 @@ nacm_data_write_xrule_xml(cxobj *xn,
|
|||
* deny: Send error message
|
||||
*/
|
||||
static int
|
||||
nacm_datanode_write_recurse(clicon_handle h,
|
||||
cxobj *xn,
|
||||
clixon_xvec *rulevec,
|
||||
clixon_xvec *xpathvec,
|
||||
int defpermit,
|
||||
yang_stmt *yspec,
|
||||
cbuf *cbret)
|
||||
nacm_datanode_write_recurse(clicon_handle h,
|
||||
cxobj *xn,
|
||||
prepvec *pv_list,
|
||||
int defpermit,
|
||||
yang_stmt *yspec,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval=-1;
|
||||
cxobj *x;
|
||||
int i;
|
||||
int ret = 0;
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
int ret = 0;
|
||||
prepvec *pv;
|
||||
|
||||
for (i=0; i<clixon_xvec_len(rulevec); i++){ /* Loop through rule list */
|
||||
if ((ret = nacm_data_write_xrule_xml(xn,
|
||||
clixon_xvec_i(rulevec, i),
|
||||
clixon_xvec_i(xpathvec, i),
|
||||
yspec)) < 0)
|
||||
goto done;
|
||||
if (ret == 0) /* No match, continue with next rule */
|
||||
;
|
||||
else if (ret == 1){ /* Match and deny: break all traversal and send error back to client */
|
||||
if (netconf_access_denied(cbret, "application", "access denied") < 0)
|
||||
pv = pv_list;
|
||||
if (pv){
|
||||
do {
|
||||
/* return values: -1:Error /0:no match /1: deny /2: permit
|
||||
*/
|
||||
if ((ret = nacm_data_write_xrule_xml(xn, pv->pv_xrule, pv->pv_xpathvec, yspec)) < 0)
|
||||
goto done;
|
||||
goto deny;
|
||||
break;
|
||||
}
|
||||
else if (ret == 2) /* Match and accpt: break rule processing but continue recursion */
|
||||
break;
|
||||
switch(ret){
|
||||
case 0: /* No match, continue with next rule */
|
||||
break;
|
||||
case 1: /* Match and deny: break all traversal and send error back to client */
|
||||
if (netconf_access_denied(cbret, "application", "access denied") < 0)
|
||||
goto done;
|
||||
goto deny;
|
||||
break;
|
||||
case 2: /* Match and permit: break rule processing but continue recursion */
|
||||
break;
|
||||
}
|
||||
if (ret == 2)
|
||||
break;
|
||||
pv = NEXTQ(prepvec *, pv);
|
||||
} while (pv && pv != pv_list);
|
||||
}
|
||||
/* If no rule match, check default rule: if deny then break traversal and send error */
|
||||
if (ret == 0 && !defpermit){
|
||||
|
|
@ -575,8 +631,7 @@ nacm_datanode_write_recurse(clicon_handle h,
|
|||
/* If node should be purged, dont recurse and defer removal to caller */
|
||||
x = NULL; /* Recursively check XML */
|
||||
while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL) {
|
||||
if ((ret = nacm_datanode_write_recurse(h, x,
|
||||
rulevec, xpathvec,
|
||||
if ((ret = nacm_datanode_write_recurse(h, x, pv_list,
|
||||
defpermit, yspec, cbret)) < 0)
|
||||
goto done;
|
||||
if (ret == 0)
|
||||
|
|
@ -616,17 +671,16 @@ nacm_datanode_write(clicon_handle h,
|
|||
cxobj *xnacm,
|
||||
cbuf *cbret)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj **gvec = NULL; /* groups */
|
||||
size_t glen;
|
||||
cxobj **rlistvec = NULL; /* rule-list */
|
||||
size_t rlistlen;
|
||||
cxobj **rvec = NULL; /* rules */
|
||||
char *write_default = NULL;
|
||||
cvec *nsc = NULL;
|
||||
clixon_xvec *rulevec = NULL;
|
||||
clixon_xvec *xpathvec = NULL;
|
||||
int ret;
|
||||
int retval = -1;
|
||||
cxobj **gvec = NULL; /* groups */
|
||||
size_t glen;
|
||||
cxobj **rlistvec = NULL; /* rule-list */
|
||||
size_t rlistlen;
|
||||
cxobj **rvec = NULL; /* rules */
|
||||
char *write_default = NULL;
|
||||
cvec *nsc = NULL;
|
||||
int ret;
|
||||
prepvec *pv_list = NULL;
|
||||
|
||||
/* Create namespace context for with nacm namespace as default */
|
||||
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
|
||||
|
|
@ -638,7 +692,6 @@ nacm_datanode_write(clicon_handle h,
|
|||
clicon_err(OE_XML, EINVAL, "No nacm write-default rule");
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* 3. Check all the "group" entries to see if any of them contain a
|
||||
"user-name" entry that equals the username for the session
|
||||
making the request. (If the "enable-external-groups" leaf is
|
||||
|
|
@ -660,16 +713,10 @@ nacm_datanode_write(clicon_handle h,
|
|||
goto done;
|
||||
/* First run through rules and cache rules as well as lookup objects in xt.
|
||||
*/
|
||||
if ((rulevec = clixon_xvec_new()) == NULL)
|
||||
goto done;
|
||||
if ((xpathvec = clixon_xvec_new()) == NULL)
|
||||
goto done;
|
||||
if (nacm_datanode_prepare(h, xt, access, gvec, glen, rlistvec, rlistlen, nsc,
|
||||
rulevec, xpathvec) < 0)
|
||||
if (nacm_datanode_prepare(h, xt, access, gvec, glen, rlistvec, rlistlen, nsc, &pv_list) < 0)
|
||||
goto done;
|
||||
/* Then recursivelyy traverse all requested nodes */
|
||||
if ((ret = nacm_datanode_write_recurse(h, xreq,
|
||||
rulevec, xpathvec,
|
||||
if ((ret = nacm_datanode_write_recurse(h, xreq, pv_list,
|
||||
strcmp(write_default, "deny"),
|
||||
clicon_dbspec_yang(h),
|
||||
cbret)) < 0)
|
||||
|
|
@ -701,10 +748,8 @@ nacm_datanode_write(clicon_handle h,
|
|||
retval = 1;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
|
||||
if (xpathvec)
|
||||
clixon_xvec_free(xpathvec);
|
||||
if (rulevec)
|
||||
clixon_xvec_free(rulevec);
|
||||
if (pv_list)
|
||||
prepvec_free(pv_list);
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (gvec)
|
||||
|
|
@ -764,13 +809,15 @@ nacm_data_read_action(cxobj *xrule,
|
|||
static int
|
||||
nacm_data_read_xrule_xml(cxobj *xn,
|
||||
cxobj *xrule,
|
||||
cxobj *xp,
|
||||
clixon_xvec *xpathvec,
|
||||
yang_stmt *yspec)
|
||||
{
|
||||
int retval = -1;
|
||||
yang_stmt *ymod;
|
||||
char *module_pattern; /* rule module name */
|
||||
|
||||
cxobj *xp;
|
||||
int i;
|
||||
|
||||
if ((module_pattern = xml_find_body(xrule, "module-name")) == NULL)
|
||||
goto nomatch;
|
||||
/* 6a) The rule's "module-name" leaf is "*" or equals the name of
|
||||
|
|
@ -790,17 +837,21 @@ nacm_data_read_xrule_xml(cxobj *xn,
|
|||
goto done;
|
||||
goto match;
|
||||
}
|
||||
/* Check if ancestor is xp */
|
||||
if (xn != xp && !xml_isancestor(xn, xp))
|
||||
goto nomatch;
|
||||
if (nacm_data_read_action(xrule, xn) < 0)
|
||||
goto done;
|
||||
match:
|
||||
retval = 1; /* match */
|
||||
done:
|
||||
return retval;
|
||||
for (i=0; i<clixon_xvec_len(xpathvec); i++){
|
||||
xp = clixon_xvec_i(xpathvec, i);
|
||||
/* Check if ancestor is xp (for every xpathvec?) */
|
||||
if (xn == xp || xml_isancestor(xn, xp)){
|
||||
if (nacm_data_read_action(xrule, xn) < 0)
|
||||
goto done;
|
||||
goto match;
|
||||
}
|
||||
}
|
||||
nomatch:
|
||||
retval = 0;
|
||||
done:
|
||||
return retval;
|
||||
match:
|
||||
retval = 1; /* match */
|
||||
goto done;
|
||||
}
|
||||
|
||||
|
|
@ -816,26 +867,30 @@ nacm_data_read_xrule_xml(cxobj *xn,
|
|||
static int
|
||||
nacm_datanode_read_recurse(clicon_handle h,
|
||||
cxobj *xn,
|
||||
clixon_xvec *rulevec,
|
||||
clixon_xvec *xpathvec,
|
||||
prepvec *pv_list,
|
||||
yang_stmt *yspec)
|
||||
{
|
||||
int retval=-1;
|
||||
cxobj *x;
|
||||
int i;
|
||||
cxobj *xprev;
|
||||
int ret;
|
||||
int retval = -1;
|
||||
cxobj *x;
|
||||
cxobj *xprev;
|
||||
int ret;
|
||||
prepvec *pv;
|
||||
|
||||
if (xml_spec(xn)){ /* Check this node */
|
||||
for (i=0; i<clixon_xvec_len(rulevec); i++){ /* Loop through rule list */
|
||||
if ((ret = nacm_data_read_xrule_xml(xn,
|
||||
clixon_xvec_i(rulevec, i),
|
||||
clixon_xvec_i(xpathvec, i),
|
||||
yspec)) < 0)
|
||||
goto done;
|
||||
if (ret == 1)
|
||||
break; /* stop at first match */
|
||||
pv = pv_list;
|
||||
if (pv){
|
||||
do {
|
||||
if ((ret = nacm_data_read_xrule_xml(xn,
|
||||
pv->pv_xrule,
|
||||
pv->pv_xpathvec,
|
||||
yspec)) < 0)
|
||||
goto done;
|
||||
if (ret == 1)
|
||||
break; /* stop at first match */
|
||||
pv = NEXTQ(prepvec *, pv);
|
||||
} while (pv && pv != pv_list);
|
||||
}
|
||||
|
||||
#if 0 /* 6(A) in algorithm
|
||||
* If N did not match any rule R, and default rule is deny, remove that subtree */
|
||||
if (strcmp(read_default, "deny") == 0)
|
||||
|
|
@ -849,7 +904,7 @@ nacm_datanode_read_recurse(clicon_handle h,
|
|||
x = NULL; /* Recursively check XML */
|
||||
xprev = NULL;
|
||||
while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL) {
|
||||
if (nacm_datanode_read_recurse(h, x, rulevec, xpathvec, yspec) < 0)
|
||||
if (nacm_datanode_read_recurse(h, x, pv_list, yspec) < 0)
|
||||
goto done;
|
||||
/* check for delayed remove */
|
||||
if (xml_flag(x, XML_FLAG_DEL)){
|
||||
|
|
@ -920,16 +975,15 @@ nacm_datanode_read(clicon_handle h,
|
|||
char *username,
|
||||
cxobj *xnacm)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj **gvec = NULL; /* groups */
|
||||
size_t glen;
|
||||
cxobj **rlistvec = NULL; /* rule-list */
|
||||
size_t rlistlen;
|
||||
int i;
|
||||
char *read_default = NULL;
|
||||
cvec *nsc = NULL;
|
||||
clixon_xvec *rulevec = NULL;
|
||||
clixon_xvec *xpathvec = NULL;
|
||||
int retval = -1;
|
||||
cxobj **gvec = NULL; /* groups */
|
||||
size_t glen;
|
||||
cxobj **rlistvec = NULL; /* rule-list */
|
||||
size_t rlistlen;
|
||||
int i;
|
||||
char *read_default = NULL;
|
||||
cvec *nsc = NULL;
|
||||
prepvec *pv_list = NULL;
|
||||
|
||||
/* Create namespace context for with nacm namespace as default */
|
||||
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
|
||||
|
|
@ -960,15 +1014,10 @@ nacm_datanode_read(clicon_handle h,
|
|||
/* First run through rules and cache rules as well as lookup objects in xt.
|
||||
* DANGER: objects could be stale if they are removed?
|
||||
*/
|
||||
if ((rulevec = clixon_xvec_new()) == NULL)
|
||||
goto done;
|
||||
if ((xpathvec = clixon_xvec_new()) == NULL)
|
||||
goto done;
|
||||
if (nacm_datanode_prepare(h, xt, NACM_READ, gvec, glen, rlistvec, rlistlen, nsc,
|
||||
rulevec, xpathvec) < 0)
|
||||
if (nacm_datanode_prepare(h, xt, NACM_READ, gvec, glen, rlistvec, rlistlen, nsc, &pv_list) < 0)
|
||||
goto done;
|
||||
/* Then recursivelyy traverse all nodes */
|
||||
if (nacm_datanode_read_recurse(h, xt, rulevec, xpathvec, clicon_dbspec_yang(h)) < 0)
|
||||
if (nacm_datanode_read_recurse(h, xt, pv_list, clicon_dbspec_yang(h)) < 0)
|
||||
goto done;
|
||||
#if 1
|
||||
/* Step 8(B) above:
|
||||
|
|
@ -999,10 +1048,8 @@ nacm_datanode_read(clicon_handle h,
|
|||
retval = 0;
|
||||
done:
|
||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||
if (xpathvec)
|
||||
clixon_xvec_free(xpathvec);
|
||||
if (rulevec)
|
||||
clixon_xvec_free(rulevec);
|
||||
if (pv_list)
|
||||
prepvec_free(pv_list);
|
||||
if (nsc)
|
||||
xml_nsctx_free(nsc);
|
||||
if (gvec)
|
||||
|
|
|
|||
|
|
@ -37,10 +37,16 @@
|
|||
* - instance-identifier as defined by YANG
|
||||
* - clixon-path is an internal format which both ^ use as internal representation
|
||||
*
|
||||
* 1. Instance-identifier
|
||||
* "Instance-identifier" is a subset of XML Xpaths and defined in Yang, used in NACM for example.
|
||||
* and defined in RF7950 Sections 9.13 and 14.
|
||||
* To note: prefixes depend on the XML context in which the value occurs,
|
||||
* In RFC8341 node-instance-identifiers are defined as:
|
||||
* All the same rules as an instance-identifier apply, except that predicates for keys are optional.
|
||||
* If a key predicate is missing, then the node-instance-identifier represents all possible server
|
||||
* instances for that key.
|
||||
*
|
||||
* 2. Api-path
|
||||
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||
* BNF:
|
||||
* <api-path> := <root> ("/" (<api-identifier> | <list-instance>))*
|
||||
|
|
@ -1365,31 +1371,40 @@ instance_id_resolve(clixon_path *cplist,
|
|||
goto fail;
|
||||
}
|
||||
cp->cp_yang = yc;
|
||||
if (cp->cp_cvk){
|
||||
/* Iterate over yang list keys and assign as names in cvk */
|
||||
if (yang_keyword_get(yc) == Y_LEAF_LIST)
|
||||
;
|
||||
else if (yang_keyword_get(yc) == Y_LIST){
|
||||
#ifdef notused
|
||||
if (cvec_len(cp->cp_cvk) > cvec_len(yang_cvec_get(yc))){
|
||||
clicon_err(OE_YANG, ENOENT, "Number of keys in key-value list does not match Yang list ");
|
||||
goto fail;
|
||||
}
|
||||
switch (yang_keyword_get(yc)){
|
||||
case Y_LIST:
|
||||
if (cp->cp_cvk == NULL){
|
||||
#if 0 /* XXX why is this not enforced? */
|
||||
clicon_err(OE_YANG, ENOENT, "key-values mandatory for lists");
|
||||
goto fail;
|
||||
#endif
|
||||
i = 0;
|
||||
cva = NULL;
|
||||
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
|
||||
if (cv_name_get(cva) == NULL){
|
||||
cvy = cvec_i(yang_cvec_get(yc), i);
|
||||
cv_name_set(cva, cv_string_get(cvy));
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
#if 0 /* XXX why is this not enforced? */
|
||||
if (cvec_len(cp->cp_cvk) > cvec_len(yang_cvec_get(yc))){
|
||||
clicon_err(OE_YANG, ENOENT, "Number of keys in key-value list does not match Yang list ");
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
i = 0;
|
||||
cva = NULL;
|
||||
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
|
||||
if (cv_name_get(cva) == NULL){
|
||||
cvy = cvec_i(yang_cvec_get(yc), i);
|
||||
cv_name_set(cva, cv_string_get(cvy));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
else{
|
||||
break;
|
||||
case Y_LEAF_LIST:
|
||||
|
||||
break;
|
||||
default:
|
||||
if (cp->cp_cvk != NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "key-values only defined for list or leaf-list");
|
||||
goto fail;
|
||||
goto fail;
|
||||
}
|
||||
break;
|
||||
}
|
||||
yt = yc;
|
||||
cp = NEXTQ(clixon_path *, cp);
|
||||
|
|
@ -1439,22 +1454,26 @@ clixon_path_search(cxobj *xt,
|
|||
yc = cp->cp_yang;
|
||||
if ((modns = yang_find_mynamespace(yc)) == NULL)
|
||||
goto fail;
|
||||
if (xvecp)
|
||||
for (i=0; i<clixon_xvec_len(xvecp); i++){
|
||||
xp = clixon_xvec_i(xvecp, i); /* Iterate over parent set */
|
||||
xvecc = NULL;
|
||||
if (cp->cp_cvk && /* cornercase for instance-id [<pos>] special case */
|
||||
(yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST) &&
|
||||
cvec_len(cp->cp_cvk) == 1 && (cv = cvec_i(cp->cp_cvk,0)) &&
|
||||
(cv_type_get(cv) == CGV_UINT32)){
|
||||
if (clixon_xml_find_pos(xp, yc, cv_uint32_get(cv), &xvecc) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (clixon_xml_find_index(xp, yang_parent_get(yc),
|
||||
modns, yang_argument_get(yc),
|
||||
cp->cp_cvk, &xvecc) < 0)
|
||||
if (xvecp){
|
||||
if ((xvecc = clixon_xvec_new()) == NULL)
|
||||
goto done;
|
||||
} /* for */
|
||||
for (i=0; i<clixon_xvec_len(xvecp); i++){
|
||||
xp = clixon_xvec_i(xvecp, i); /* Iterate over parent set */
|
||||
|
||||
if (cp->cp_cvk && /* cornercase for instance-id [<pos>] special case */
|
||||
(yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST) &&
|
||||
cvec_len(cp->cp_cvk) == 1 && (cv = cvec_i(cp->cp_cvk,0)) &&
|
||||
(cv_type_get(cv) == CGV_UINT32)){
|
||||
if (clixon_xml_find_pos(xp, yc, cv_uint32_get(cv), xvecc) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (clixon_xml_find_index(xp, yang_parent_get(yc),
|
||||
modns, yang_argument_get(yc),
|
||||
cp->cp_cvk, xvecc) < 0)
|
||||
goto done;
|
||||
/* XXX: xvecc append! either here or in the functions */
|
||||
} /* for */
|
||||
}
|
||||
if (xvecp)
|
||||
clixon_xvec_free(xvecp);
|
||||
xvecp = xvecc;
|
||||
|
|
|
|||
|
|
@ -179,6 +179,8 @@ populate_self_parent(cxobj *xt,
|
|||
#endif
|
||||
retval = 1;
|
||||
done:
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
return retval;
|
||||
fail:
|
||||
retval = 0;
|
||||
|
|
|
|||
|
|
@ -735,7 +735,8 @@ xml_search_binary(cxobj *xp,
|
|||
/*! Search XML child under xp matching x1 using yang-based binary search for list/leaf-list keys
|
||||
*
|
||||
* Match is tried xp with x1 with either name only (container/leaf) or using keys (list/leaf-lists)
|
||||
* Any non-key leafs or other structure of x1 is not matched.
|
||||
* Any non-key leafs or other structure of x1 is not matched (unless indexvar is set).
|
||||
* If x1 is list or leaf-list, the function assumes key values / indexvar exists in x1.
|
||||
*
|
||||
* @param[in] xp Parent xml node.
|
||||
* @param[in] x1 Find this object among xp:s children
|
||||
|
|
@ -748,11 +749,11 @@ xml_search_binary(cxobj *xp,
|
|||
* @see xml_find_index for a generic search function
|
||||
*/
|
||||
static int
|
||||
xml_search_yang(cxobj *xp,
|
||||
cxobj *x1,
|
||||
yang_stmt *yc,
|
||||
int skip1,
|
||||
char *indexvar,
|
||||
xml_search_yang(cxobj *xp,
|
||||
cxobj *x1,
|
||||
yang_stmt *yc,
|
||||
int skip1,
|
||||
char *indexvar,
|
||||
clixon_xvec *xvec)
|
||||
{
|
||||
int retval = -1;
|
||||
|
|
@ -779,7 +780,7 @@ xml_search_yang(cxobj *xp,
|
|||
else
|
||||
#endif
|
||||
if (yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST)
|
||||
sorted = (yang_find(yc, Y_ORDERED_BY, "user") == NULL);
|
||||
sorted = (yang_find(yc, Y_ORDERED_BY, "user") == NULL);
|
||||
yangi = yang_order(yc);
|
||||
|
||||
if (xml_search_binary(xp, x1, sorted, yangi, low, upper, skip1, indexvar, xvec) < 0)
|
||||
|
|
@ -1258,7 +1259,7 @@ xml_find_noyang_name(cxobj *xp,
|
|||
/*! Try to find an XML child from parent with yang available using list keys and leaf-lists
|
||||
*
|
||||
* Must be populated with Yang specs, parent must be list or leaf-list, and (for list) search
|
||||
* index must be keys in the order they are declared.
|
||||
* index MUST be keys in the order they are declared.
|
||||
* First identify that this search qualifies for yang-based list/leaf-list optimized search,
|
||||
* - if no, revert (return 0) so that the overlying algorithm can try next or fallback to
|
||||
* linear seacrh
|
||||
|
|
@ -1306,6 +1307,10 @@ xml_find_index_yang(cxobj *xp,
|
|||
cprintf(cb, "<%s>", name);
|
||||
ycvk = yang_cvec_get(yc); /* Check that those are proper index keys */
|
||||
cvi = NULL;
|
||||
if (cvk == NULL){ /* If list and no keys, all should match */
|
||||
revert++;
|
||||
break;
|
||||
}
|
||||
i = 0;
|
||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||
if ((kname = cv_name_get(cvi)) == NULL){
|
||||
|
|
@ -1313,7 +1318,7 @@ xml_find_index_yang(cxobj *xp,
|
|||
goto done;
|
||||
}
|
||||
/* Parameter in cvk is not key or not in right key order, then we cannot call
|
||||
* xml_find_keys and we need to revert to noynag
|
||||
* xml_find_keys and we need to revert to noyang
|
||||
*/
|
||||
if ((ycv = cvec_i(ycvk, i)) == NULL || strcmp(kname, cv_string_get(ycv))){
|
||||
revert++;
|
||||
|
|
@ -1411,12 +1416,11 @@ xml_find_index_yang(cxobj *xp,
|
|||
* - Otherwise search is made using linear search
|
||||
*
|
||||
* @param[in] xp Parent xml node.
|
||||
* @param[in] yc Yang spec of list child (preferred) See rule (2) above
|
||||
* @param[in] yp Yang spec of parent node or yang-spec/yang (Alternative if yc not given).
|
||||
* @param[in] namespace Namespace (needed only if name is derived from top-symbol)
|
||||
* @param[in] name Name of child (not required if yc given)
|
||||
* @param[in] name Name of child
|
||||
* @param[in] cvk List of keys and values as CLIgen vector on the form k1=foo, k2=bar
|
||||
* @param[out] xvec Array of found nodes
|
||||
* @param[out] xvec Array of result nodes. Must be initialized on entry
|
||||
* @retval 0 OK, see xret
|
||||
* @retval -1 Error
|
||||
* @code
|
||||
|
|
@ -1424,10 +1428,12 @@ xml_find_index_yang(cxobj *xp,
|
|||
* cvec *cvk = NULL; vector of index keys
|
||||
* cxobj *x;
|
||||
* ... Populate cvk with key/values eg a:5 b:6
|
||||
* if (clixon_xml_find_index(xp, yp, NULL, "a", ns, cvk, &xv) < 0)
|
||||
* if ((xv = clixon_xvec_new()) == NULL)
|
||||
* err;
|
||||
* for (i=0; i<clixon_xvec_len(xvec); i++){
|
||||
* x = clixon_xpath_i(xvec, i);
|
||||
* if (clixon_xml_find_index(xp, yp, NULL, "a", ns, cvk, xv) < 0)
|
||||
* err;
|
||||
* for (i=0; i<clixon_xvec_len(xv); i++){
|
||||
* x = clixon_xpath_i(xv, i);
|
||||
* ...
|
||||
* }
|
||||
* clixon_xvec_free(xvec);
|
||||
|
|
@ -1443,14 +1449,18 @@ clixon_xml_find_index(cxobj *xp,
|
|||
char *namespace,
|
||||
char *name,
|
||||
cvec *cvk,
|
||||
clixon_xvec **xvec)
|
||||
clixon_xvec *xvec)
|
||||
{
|
||||
int retval = -1;
|
||||
int ret;
|
||||
yang_stmt *yc = NULL;
|
||||
|
||||
if (xvec == NULL){
|
||||
clicon_err(OE_YANG, EINVAL, "xvec");
|
||||
goto done;
|
||||
}
|
||||
if (name == NULL){
|
||||
clicon_err(OE_YANG, ENOENT, "name");
|
||||
clicon_err(OE_YANG, EINVAL, "name");
|
||||
goto done;
|
||||
}
|
||||
if (yp == NULL)
|
||||
|
|
@ -1461,27 +1471,21 @@ clixon_xml_find_index(cxobj *xp,
|
|||
if (yp)
|
||||
yc = yang_find_datanode(yp, name);
|
||||
}
|
||||
if ((*xvec = clixon_xvec_new()) == NULL)
|
||||
goto done;
|
||||
if (yc){
|
||||
if ((ret = xml_find_index_yang(xp, yc, cvk, *xvec)) < 0)
|
||||
if ((ret = xml_find_index_yang(xp, yc, cvk, xvec)) < 0)
|
||||
goto done;
|
||||
if (ret == 0){ /* This means yang method did not work for some reason
|
||||
* such as not being list key indexes in cvk, for example
|
||||
*/
|
||||
if (xml_find_noyang_name(xp, namespace, name, cvk, *xvec) < 0)
|
||||
if (xml_find_noyang_name(xp, namespace, name, cvk, xvec) < 0)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (xml_find_noyang_name(xp, namespace, name, cvk, *xvec) < 0)
|
||||
if (xml_find_noyang_name(xp, namespace, name, cvk, xvec) < 0)
|
||||
goto done;
|
||||
retval = 0;
|
||||
done:
|
||||
if (retval < 0){
|
||||
clixon_xvec_free(*xvec);
|
||||
*xvec = NULL;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -1499,7 +1503,7 @@ int
|
|||
clixon_xml_find_pos(cxobj *xp,
|
||||
yang_stmt *yc,
|
||||
uint32_t pos,
|
||||
clixon_xvec **xvec)
|
||||
clixon_xvec *xvec)
|
||||
{
|
||||
int retval = -1;
|
||||
cxobj *xc = NULL;
|
||||
|
|
@ -1513,22 +1517,16 @@ clixon_xml_find_pos(cxobj *xp,
|
|||
name = yang_argument_get(yc);
|
||||
u = 0;
|
||||
xc = NULL;
|
||||
if ((*xvec = clixon_xvec_new()) == NULL)
|
||||
goto done;
|
||||
while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL) {
|
||||
if (strcmp(name, xml_name(xc)))
|
||||
continue;
|
||||
if (pos == u++){ /* Found */
|
||||
if (clixon_xvec_append(*xvec, xc) < 0)
|
||||
if (clixon_xvec_append(xvec, xc) < 0)
|
||||
goto done;
|
||||
break;
|
||||
}
|
||||
}
|
||||
retval = 0;
|
||||
done:
|
||||
if (retval < 0){
|
||||
clixon_xvec_free(*xvec);
|
||||
*xvec = NULL;
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -227,9 +227,9 @@ loop_preds(xpath_tree *xt,
|
|||
* y[k=3] # corresponds to: <name>[<keyname>=<keyval>]
|
||||
*/
|
||||
static int
|
||||
xpath_list_optimize_fn(xpath_tree *xt,
|
||||
cxobj *xv,
|
||||
clixon_xvec **xvec)
|
||||
xpath_list_optimize_fn(xpath_tree *xt,
|
||||
cxobj *xv,
|
||||
clixon_xvec *xvec)
|
||||
{
|
||||
int retval = -1;
|
||||
xpath_tree *xm = NULL;
|
||||
|
|
@ -329,8 +329,10 @@ xpath_optimize_check(xpath_tree *xs,
|
|||
|
||||
if (!_optimize_enable)
|
||||
return 0; /* use regular code */
|
||||
if ((xvec = clixon_xvec_new()) == NULL)
|
||||
return -1;
|
||||
/* Glue code since xpath code uses (old) cxobj ** and search code uses (new) clixon_xvec */
|
||||
if ((ret = xpath_list_optimize_fn(xs, xv, &xvec)) < 0)
|
||||
if ((ret = xpath_list_optimize_fn(xs, xv, xvec)) < 0)
|
||||
return -1;
|
||||
if (ret == 1){
|
||||
if (clixon_xvec_extract(xvec, xvec0, xlen0) < 0)
|
||||
|
|
|
|||
|
|
@ -156,8 +156,10 @@ trigger_rpc(clicon_handle h, /* Clicon handle */
|
|||
goto done;
|
||||
cv_name_set(cv, "k");
|
||||
cv_string_set(cv, "5");
|
||||
if ((xv = clixon_xvec_new()) == NULL)
|
||||
goto done;
|
||||
/* Use form 2c use spec of xc + name */
|
||||
if (clixon_xml_find_index(xc, NULL, NULL, "y3", cvk, &xv) < 0)
|
||||
if (clixon_xml_find_index(xc, NULL, NULL, "y3", cvk, xv) < 0)
|
||||
goto done;
|
||||
if (clixon_xvec_len(xv))
|
||||
val = xml_find_body(clixon_xvec_i(xv,0), "val");
|
||||
|
|
|
|||
102
test/test_instance_id_multi.sh
Executable file
102
test/test_instance_id_multi.sh
Executable file
|
|
@ -0,0 +1,102 @@
|
|||
#!/usr/bin/env bash
|
||||
# Instance-id tests with multiple results
|
||||
# RFC 7950 Sections 9.13 and 14 says that:
|
||||
# Predicates are used only for specifying the values for the key nodes for list entries
|
||||
# but does not explicitly say that list nodes can skip them
|
||||
# And in RFC8341 node-instance-identifiers are defined as:
|
||||
# All the same rules as an instance-identifier apply, except that predicates for keys are optional.
|
||||
# If a key predicate is missing, then the node-instance-identifier represents all possible server
|
||||
# instances for that key.
|
||||
|
||||
# Magic line must be first in script (see README.md)
|
||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
: ${clixon_util_path:=clixon_util_path -D $DBG}
|
||||
|
||||
xml1=$dir/xml1.xml
|
||||
ydir=$dir/yang
|
||||
|
||||
if [ ! -d $ydir ]; then
|
||||
mkdir $ydir
|
||||
fi
|
||||
|
||||
# Nested lists
|
||||
cat <<EOF > $ydir/example.yang
|
||||
module example{
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:id";
|
||||
prefix ex;
|
||||
container table{
|
||||
list parameter{
|
||||
key name;
|
||||
leaf name{
|
||||
type string;
|
||||
}
|
||||
container next{
|
||||
list parameter{
|
||||
key name;
|
||||
leaf name{
|
||||
type string;
|
||||
}
|
||||
leaf value{
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
cat <<EOF > $xml1
|
||||
<table xmlns="urn:example:id">
|
||||
<parameter>
|
||||
<name>a</name>
|
||||
<next>
|
||||
<parameter>
|
||||
<name>a</name>
|
||||
<value>11</value>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>b</name>
|
||||
<value>22</value>
|
||||
</parameter>
|
||||
</next>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>b</name>
|
||||
<next>
|
||||
<parameter>
|
||||
<name>a</name>
|
||||
<value>33</value>
|
||||
</parameter>
|
||||
</next>
|
||||
</parameter>
|
||||
</table>
|
||||
EOF
|
||||
|
||||
new "instance-id top-level param"
|
||||
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter)" 0 "0: <parameter><name>a</name><next><parameter><name>a</name><value>11</value></parameter><parameter><name>b</name><value>22</value></parameter></next></parameter>" "1: <parameter><name>b</name><next><parameter><name>a</name><value>33</value></parameter></next></parameter>"
|
||||
|
||||
new "instance-id a/next"
|
||||
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter[ex:name=\"a\"]/ex:next)" 0 "0: <next><parameter><name>a</name><value>11</value></parameter><parameter><name>b</name><value>22</value></parameter></next>"
|
||||
|
||||
new "instance-id a/next/param"
|
||||
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter[ex:name=\"a\"]/ex:next/ex:parameter)" 0 "0: <parameter><name>a</name><value>11</value></parameter>" "1: <parameter><name>b</name><value>22</value></parameter>"
|
||||
|
||||
new "instance-id a/next/param/a"
|
||||
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter[ex:name=\"a\"]/ex:next/ex:parameter[ex:name=\"a\"])" 0 "0: <parameter><name>a</name><value>11</value></parameter>" "0: <parameter><name>a</name><value>11</value></parameter>"
|
||||
|
||||
new "instance-id a/next/param/a/value"
|
||||
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter[ex:name=\"a\"]/ex:next/ex:parameter[ex:name=\"a\"]/ex:value)" 0 "0: <value>11</value>"
|
||||
|
||||
new "instance-id top-level parameter/next"
|
||||
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter/ex:next)" 0 "0: <next><parameter><name>a</name><value>11</value></parameter><parameter><name>b</name><value>22</value></parameter></next>" "1: <next><parameter><name>a</name><value>33</value></parameter></next>"
|
||||
|
||||
new "instance-id top-level parameter/next/parameter"
|
||||
expectpart "$($clixon_util_path -f $xml1 -y $ydir -p /ex:table/ex:parameter/ex:next/ex:parameter)" 0 "0: <parameter><name>a</name><value>11</value></parameter>" "1: <parameter><name>b</name><value>22</value></parameter>" "2: <parameter><name>a</name><value>33</value></parameter>"
|
||||
|
||||
rm -rf $dir
|
||||
unset clixon_util_path # for other script reusing it
|
||||
|
||||
|
||||
296
test/test_nacm_datanode_write.sh
Executable file
296
test/test_nacm_datanode_write.sh
Executable file
|
|
@ -0,0 +1,296 @@
|
|||
#!/usr/bin/env bash
|
||||
# Authentication and authorization and IETF NACM
|
||||
# NACM data node rules
|
||||
# The RFC 8341 examples in the appendix are very limited.
|
||||
# Here focus on datanode paths from a write perspective.
|
||||
# Especially a list in a list to test vector rules
|
||||
# The test uses a nested list and makes CRUD operations on one object "b"
|
||||
|
||||
# Magic line must be first in script (see README.md)
|
||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
APPNAME=example
|
||||
|
||||
# Common NACM scripts
|
||||
. ./nacm.sh
|
||||
|
||||
cfg=$dir/conf_yang.xml
|
||||
fyang=$dir/nacm-example.yang
|
||||
|
||||
cat <<EOF > $cfg
|
||||
<clixon-config xmlns="http://clicon.org/config">
|
||||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$IETFRFC</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_MAIN_FILE>$fyang</CLICON_YANG_MAIN_FILE>
|
||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||
<CLICON_RESTCONF_DIR>/usr/local/lib/$APPNAME/restconf</CLICON_RESTCONF_DIR>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||
<CLICON_BACKEND_DIR>/usr/local/lib/$APPNAME/backend</CLICON_BACKEND_DIR>
|
||||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
<CLICON_XMLDB_DIR>/usr/local/var/$APPNAME</CLICON_XMLDB_DIR>
|
||||
<CLICON_RESTCONF_PRETTY>false</CLICON_RESTCONF_PRETTY>
|
||||
<CLICON_NACM_MODE>internal</CLICON_NACM_MODE>
|
||||
<CLICON_NACM_CREDENTIALS>none</CLICON_NACM_CREDENTIALS>
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
||||
cat <<EOF > $fyang
|
||||
module nacm-example{
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:nacm";
|
||||
prefix ex;
|
||||
import ietf-netconf-acm {
|
||||
prefix nacm;
|
||||
}
|
||||
container table{
|
||||
list parameter{
|
||||
key name;
|
||||
leaf name{
|
||||
type string;
|
||||
}
|
||||
container next{
|
||||
list parameter{
|
||||
key name;
|
||||
leaf name{
|
||||
type string;
|
||||
}
|
||||
leaf value{
|
||||
type string;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
RULES=$(cat <<EOF
|
||||
<nacm xmlns="urn:ietf:params:xml:ns:yang:ietf-netconf-acm">
|
||||
<enable-nacm>false</enable-nacm>
|
||||
<read-default>deny</read-default>
|
||||
<write-default>deny</write-default>
|
||||
<exec-default>permit</exec-default>
|
||||
|
||||
$NGROUPS
|
||||
|
||||
<rule-list>
|
||||
<name>limited-acl</name>
|
||||
<group>limited</group>
|
||||
|
||||
<rule>
|
||||
<name>value</name>
|
||||
<module-name>nacm-example</module-name>
|
||||
<access-operations>create delete update</access-operations>
|
||||
<path xmlns:ex="urn:example:nacm">/ex:table/ex:parameter/ex:next/ex:parameter/ex:value</path>
|
||||
<action>permit</action>
|
||||
</rule>
|
||||
<rule>
|
||||
<name>parameter</name>
|
||||
<module-name>nacm-example</module-name>
|
||||
<access-operations>read update</access-operations>
|
||||
<path xmlns:ex="urn:example:nacm">/ex:table/ex:parameter/ex:next/ex:parameter</path>
|
||||
<action>permit</action>
|
||||
</rule>
|
||||
|
||||
</rule-list>
|
||||
|
||||
$NADMIN
|
||||
|
||||
</nacm>
|
||||
EOF
|
||||
)
|
||||
|
||||
CONFIG=$(cat <<EOF
|
||||
<table xmlns="urn:example:nacm">
|
||||
<parameter>
|
||||
<name>a</name>
|
||||
<next>
|
||||
<parameter>
|
||||
<name>a</name>
|
||||
<value>72</value>
|
||||
</parameter>
|
||||
</next>
|
||||
</parameter>
|
||||
<parameter>
|
||||
<name>b</name>
|
||||
<next>
|
||||
<parameter>
|
||||
<name>a</name>
|
||||
<value>99</value>
|
||||
</parameter>
|
||||
</next>
|
||||
</parameter>
|
||||
</table>
|
||||
EOF
|
||||
)
|
||||
|
||||
#
|
||||
# Arguments, permit/deny on different levels:
|
||||
# Configs (permit/deny):
|
||||
# - write-default
|
||||
# - param access
|
||||
# - param action
|
||||
# - value access
|
||||
# - value action
|
||||
# Tests, epxect true/false:
|
||||
# - create
|
||||
# - read
|
||||
# - update
|
||||
# - delete
|
||||
testrun(){
|
||||
writedefault=$1
|
||||
paramaccess=$2
|
||||
paramaction=$3
|
||||
valueaccess=$4
|
||||
valueaction=$5
|
||||
testc=$6
|
||||
testr=$7
|
||||
testu=$8
|
||||
testd=$9
|
||||
|
||||
new "set write-default $writedefault"
|
||||
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/ietf-netconf-acm:nacm/write-default -d "{\"ietf-netconf-acm:write-default\":\"$writedefault\"}" )" 0 "HTTP/1.1 204 No Content"
|
||||
|
||||
new "set param rule access: $paramaccess"
|
||||
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=parameter/access-operations -d "{\"ietf-netconf-acm:access-operations\":\"$paramaccess\"}" )" 0 "HTTP/1.1 204 No Content"
|
||||
|
||||
new "set param rule access: $paramaction"
|
||||
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=parameter/action -d "{\"ietf-netconf-acm:action\":\"$paramaction\"}" )" 0 "HTTP/1.1 204 No Content"
|
||||
|
||||
new "set value rule access: $valueaccess"
|
||||
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=value/access-operations -d "{\"ietf-netconf-acm:access-operations\":\"$valueaccess\"}" )" 0 "HTTP/1.1 204 No Content"
|
||||
|
||||
new "set value rule access: $valueaction"
|
||||
expectpart "$(curl -u andy:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/ietf-netconf-acm:nacm/rule-list=limited-acl/rule=value/action -d "{\"ietf-netconf-acm:action\":\"$valueaction\"}" )" 0 "HTTP/1.1 204 No Content"
|
||||
|
||||
#--------------- Here tests: create/update/read/delete
|
||||
|
||||
new "create object b"
|
||||
if $testc; then
|
||||
expectpart "$(curl -u wilma:bar -siS -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/nacm-example:table/parameter=a/next -d '{"nacm-example:parameter":[{"name":"b","value":"17"}]}')" 0 'HTTP/1.1 201 Created'
|
||||
else
|
||||
expectpart "$(curl -u wilma:bar -siS -X POST -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/nacm-example:table/parameter=a/next -d '{"nacm-example:parameter":[{"name":"b","value":"17"}]}')" 0 'HTTP/1.1 403 Forbidden' '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
|
||||
fi
|
||||
new "read object b"
|
||||
if $testr; then
|
||||
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 'HTTP/1.1 200 OK' '{"nacm-example:parameter":\[{"name":"b","value":"17"}\]}'
|
||||
else
|
||||
expectpart "$(curl -u wilma:bar -siS -X GET http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 'HTTP/1.1 404 Not Found'
|
||||
fi
|
||||
|
||||
new "update object b"
|
||||
if $testu; then
|
||||
expectpart "$(curl -u wilma:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b -d '{"nacm-example:parameter":[{"name":"b","value":"92"}]}')" 0 'HTTP/1.1 204 No Content'
|
||||
else
|
||||
expectpart "$(curl -u wilma:bar -siS -X PUT -H "Content-Type: application/yang-data+json" http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b -d '{"nacm-example:parameter":[{"name":"b","value":"92"}]}')" 0 'HTTP/1.1 403 Forbidden'
|
||||
# '{"ietf-restconf:errors":{"error":{"error-type":"application","error-tag":"access-denied","error-severity":"error","error-message":"access denied"}}}'
|
||||
fi
|
||||
|
||||
new "delete object b"
|
||||
if $testd; then
|
||||
expectpart "$(curl -u wilma:bar -siS -X DELETE http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 'HTTP/1.1 204 No Content'
|
||||
else # XXX can vara olika
|
||||
ret=$(curl -u wilma:bar -siS -X DELETE http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)
|
||||
r=$?
|
||||
if [ $r != 0 ]; then
|
||||
err "retval: $r" "0"
|
||||
fi
|
||||
match1=$(echo "$ret" | grep --null -o 'HTTP/1.1 403 Forbidden')
|
||||
r1=$?
|
||||
match2=$(echo "$ret" | grep --null -o 'HTTP/1.1 409 Conflict')
|
||||
r2=$?
|
||||
if [ $r1 != 0 -a $r2 != 0 ]; then
|
||||
err "'HTTP/1.1 403 Forbidden' or 'HTTP/1.1 409 Conflict'" "$ret"
|
||||
fi
|
||||
# Ensure delete
|
||||
new "ensure delete object b"
|
||||
expectpart "$(curl -u andy:bar -siS -X DELETE http://localhost/restconf/data/nacm-example:table/parameter=a/next/parameter=b)" 0 'HTTP/1.1' # ignore error
|
||||
fi
|
||||
|
||||
} # testrun
|
||||
|
||||
new "test params: -f $cfg"
|
||||
|
||||
if [ $BE -ne 0 ]; then
|
||||
new "kill old backend"
|
||||
sudo clixon_backend -zf $cfg
|
||||
if [ $? -ne 0 ]; then
|
||||
err
|
||||
fi
|
||||
new "start backend -s init -f $cfg"
|
||||
start_backend -s init -f $cfg
|
||||
fi
|
||||
|
||||
new "waiting"
|
||||
wait_backend
|
||||
|
||||
if [ $RC -ne 0 ]; then
|
||||
new "kill old restconf daemon"
|
||||
sudo pkill -u $wwwuser -f clixon_restconf
|
||||
|
||||
new "start restconf daemon (-a is enable basic authentication)"
|
||||
start_restconf -f $cfg -- -a
|
||||
|
||||
new "waiting"
|
||||
wait_restconf
|
||||
fi
|
||||
|
||||
new "auth set authentication config"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$RULES</config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "set app config"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><edit-config><target><candidate/></target><config>$CONFIG</config></edit-config></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "commit it"
|
||||
expecteof "$clixon_netconf -qf $cfg" 0 "<rpc><commit/></rpc>]]>]]>" "^<rpc-reply><ok/></rpc-reply>]]>]]>$"
|
||||
|
||||
new "enable nacm"
|
||||
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
|
||||
# config: def param:access/action value:access/action
|
||||
# test: create read update delete
|
||||
# default deny
|
||||
testrun permit "*" permit "*" permit true true true true
|
||||
testrun permit "*" deny "*" deny false false false false
|
||||
testrun permit "*" permit "*" deny false false false false
|
||||
testrun permit "*" permit "update delete" deny true true false false
|
||||
testrun permit "*" permit "delete" deny true true true false
|
||||
testrun permit "delete" deny "*" permit true true true false
|
||||
testrun permit "update" deny "*" permit true true false true
|
||||
testrun permit "read" deny "*" permit true false true true
|
||||
|
||||
testrun deny "*" permit "*" permit true true true true
|
||||
testrun deny "*" permit "*" deny false false false false
|
||||
testrun deny "create" permit "*" permit true true false false
|
||||
# strange: a read permit on a sub-object while default read deny opens up all
|
||||
testrun deny "create read" permit "*" permit true true false false
|
||||
testrun deny "create" permit "create" permit true false false false
|
||||
testrun deny "create update" permit "create update" permit true false true false
|
||||
testrun deny "create update delete" permit "create update delete" permit true false true true
|
||||
testrun deny "create update delete" permit "update" deny true false false true
|
||||
testrun deny "create update delete" permit "delete" deny true false true false
|
||||
# OK but only gives sub-.object (not value) too complex to test
|
||||
# testrun deny "create update read" permit "read" deny true true true false
|
||||
testrun deny "*" deny "*" deny false false false false
|
||||
|
||||
if [ $RC -ne 0 ]; then
|
||||
new "Kill restconf daemon"
|
||||
stop_restconf
|
||||
fi
|
||||
if [ $BE -ne 0 ]; then # Bring your own backend
|
||||
new "Kill backend"
|
||||
# Check if premature kill
|
||||
pid=$(pgrep -u root -f clixon_backend)
|
||||
if [ -z "$pid" ]; then
|
||||
err "backend already dead"
|
||||
fi
|
||||
# kill backend
|
||||
stop_backend -f $cfg
|
||||
fi
|
||||
|
||||
rm -rf $dir
|
||||
128
test/test_xml_trees.sh
Executable file
128
test/test_xml_trees.sh
Executable file
|
|
@ -0,0 +1,128 @@
|
|||
#!/usr/bin/env bash
|
||||
# XML inserty and merge two trees
|
||||
# This is mainly a development API check
|
||||
|
||||
# Magic line must be first in script (see README.md)
|
||||
s="$_" ; . ./lib.sh || if [ "$s" = $0 ]; then exit 0; else return 0; fi
|
||||
|
||||
: ${clixon_util_xml_mod:=clixon_util_xml_mod}
|
||||
|
||||
OPTS="-D $DBG"
|
||||
|
||||
APPNAME=example
|
||||
|
||||
cfg=$dir/conf_yang.xml
|
||||
fyang=$dir/example.yang
|
||||
|
||||
cat <<EOF > $cfg
|
||||
<clixon-config xmlns="http://clicon.org/config">
|
||||
<CLICON_CONFIGFILE>$cfg</CLICON_CONFIGFILE>
|
||||
<CLICON_YANG_DIR>$dir</CLICON_YANG_DIR>
|
||||
<CLICON_YANG_DIR>/usr/local/share/clixon</CLICON_YANG_DIR>
|
||||
<CLICON_CLISPEC_DIR>/usr/local/lib/$APPNAME/clispec</CLICON_CLISPEC_DIR>
|
||||
<CLICON_CLI_DIR>/usr/local/lib/$APPNAME/cli</CLICON_CLI_DIR>
|
||||
<CLICON_CLI_MODE>$APPNAME</CLICON_CLI_MODE>
|
||||
<CLICON_SOCK>/usr/local/var/$APPNAME/$APPNAME.sock</CLICON_SOCK>
|
||||
<CLICON_BACKEND_PIDFILE>/usr/local/var/$APPNAME/$APPNAME.pidfile</CLICON_BACKEND_PIDFILE>
|
||||
<CLICON_XMLDB_DIR>$dir</CLICON_XMLDB_DIR>
|
||||
</clixon-config>
|
||||
EOF
|
||||
|
||||
cat <<EOF > $fyang
|
||||
module example {
|
||||
yang-version 1.1;
|
||||
namespace "urn:example:example";
|
||||
prefix ex;
|
||||
revision 2019-01-13;
|
||||
container c{
|
||||
leaf d{
|
||||
type int32;
|
||||
}
|
||||
list a{
|
||||
key x;
|
||||
leaf x{
|
||||
type int32;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# insert/merge a tree into a base tree
|
||||
# Args:
|
||||
# 1: operation
|
||||
# 2: base tree
|
||||
# 3: insert tree
|
||||
# 4: xpath
|
||||
# 5: retval
|
||||
# 6: result
|
||||
testrun(){
|
||||
op=$1
|
||||
x0=$2
|
||||
x1=$3
|
||||
xp=$4
|
||||
ret=$5
|
||||
res=$6
|
||||
|
||||
echo "$clixon_util_xml_mod -o $op -y $fyang -b "$x0" -x "$x1" -p $xp $OPTS"
|
||||
expectpart "$($clixon_util_xml_mod -o $op -y $fyang -b "$x0" -x "$x1" -p $xp $OPTS)" $ret "$res"
|
||||
}
|
||||
|
||||
new "test params: -y $fyang $OPTS"
|
||||
|
||||
# -------- insert
|
||||
# Empty element base list
|
||||
x0a='<c xmlns="urn:example:example">'
|
||||
x0b='</c>'
|
||||
p=c
|
||||
|
||||
new "insert 1st element"
|
||||
testrun insert "$x0a$x0b" "$x0a<a><x>1</x></a>$x0b" $p 0 '<c xmlns="urn:example:example"><a><x>1</x></a></c>'
|
||||
|
||||
new "insert 2nd element"
|
||||
testrun insert "$x0a<a><x>2</x></a>$x0b" "$x0a<a><x>1</x></a>$x0b" $p 0 '<c xmlns="urn:example:example"><a><x>1</x></a><a><x>2</x></a></c>'
|
||||
|
||||
new "insert container"
|
||||
testrun insert "$x0a<a><x>1</x></a>$x0b" "$x0a<d>42</d>$x0b" c 0 '<c xmlns="urn:example:example"><d>42</d><a><x>1</x></a></c>'
|
||||
|
||||
# -------- parse parent
|
||||
new "parent 1st element"
|
||||
testrun parent "$x0a$x0b" "<a><x>1</x></a>" $p 0 '<c xmlns="urn:example:example"><a><x>1</x></a></c>'
|
||||
|
||||
new "patrse parent element"
|
||||
testrun parent "$x0a<a><x>2</x></a>$x0b" '<a><x>1</x></a>' $p 0 '<c xmlns="urn:example:example"><a><x>1</x></a><a><x>2</x></a></c>'
|
||||
|
||||
|
||||
new "parse parent container"
|
||||
testrun parent "$x0a<a><x>1</x></a>$x0b" '<d>42</d>' c 0 '<c xmlns="urn:example:example"><d>42</d><a><x>1</x></a></c>'
|
||||
|
||||
# -------- merge
|
||||
|
||||
new "merge empty"
|
||||
testrun merge "$x0a$x0b" "$x0a$x0b" $p 0 '<c xmlns="urn:example:example"/>'
|
||||
|
||||
new "merge single w empty"
|
||||
testrun merge "$x0a<a><x>1</x></a>$x0b" "$x0a$x0b" . 0 '<c xmlns="urn:example:example"><a><x>1</x></a></c>'
|
||||
|
||||
new "merge empty w single"
|
||||
testrun merge "$x0a$x0b" "$x0a<a><x>1</x></a>$x0b" . 0 '<c xmlns="urn:example:example"><a><x>1</x></a></c>'
|
||||
|
||||
new "merge equal single"
|
||||
testrun merge "$x0a<a><x>1</x></a>$x0b" "$x0a<a><x>1</x></a>$x0b" . 0 '<c xmlns="urn:example:example"><a><x>1</x></a></c>'
|
||||
|
||||
new "merge overlap"
|
||||
testrun merge "$x0a<a><x>1</x></a><a><x>2</x></a>$x0b" "$x0a<a><x>2</x></a><a><x>3</x></a>$x0b" . 0 '<c xmlns="urn:example:example"><a><x>1</x></a><a><x>2</x></a><a><x>3</x></a></c>'
|
||||
|
||||
new "merge list and leaf"
|
||||
testrun merge "$x0a<a><x>1</x></a><a><x>2</x></a>$x0b" "$x0a<d>42</d>$x0b" . 0 '<c xmlns="urn:example:example"><d>42</d><a><x>1</x></a><a><x>2</x></a></c>'
|
||||
|
||||
new "merge leaf and list"
|
||||
testrun merge "$x0a<d>42</d>$x0b" "$x0a<a><x>1</x></a><a><x>2</x></a>$x0b" . 0 '<c xmlns="urn:example:example"><d>42</d><a><x>1</x></a><a><x>2</x></a></c>'
|
||||
|
||||
new "merge overlap with path fail, merge does not work w subtrees"
|
||||
testrun merge "$x0a<a><x>1</x></a><a><x>2</x></a>$x0b" "$x0a<a><x>2</x></a><a><x>3</x></a>$x0b" c 255 ''
|
||||
|
||||
rm -rf $dir
|
||||
|
||||
# unset conditional parameters
|
||||
unset clixon_util_xml_mod
|
||||
|
|
@ -70,10 +70,10 @@ struct curlbuf{
|
|||
* realloc. Therefore, we append new data to the userdata buffer.
|
||||
*/
|
||||
static size_t
|
||||
curl_get_cb(void *ptr,
|
||||
curl_get_cb(void *ptr,
|
||||
size_t size,
|
||||
size_t nmemb,
|
||||
void *userdata)
|
||||
void *userdata)
|
||||
{
|
||||
struct curlbuf *buf = (struct curlbuf *)userdata;
|
||||
int len;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue