* 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
|
### 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
|
## 4.4.0
|
||||||
5 April 2020
|
5 April 2020
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
|
||||||
Copyright (C) 2017-2019 Olof Hagsand
|
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.
|
CLIXON is dual license.
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@ typedef struct {
|
||||||
char csm_prompt[CLI_PROMPT_LEN]; /* Prompt for mode */
|
char csm_prompt[CLI_PROMPT_LEN]; /* Prompt for mode */
|
||||||
int csm_nsyntax; /* Num syntax specs registered by plugin */
|
int csm_nsyntax; /* Num syntax specs registered by plugin */
|
||||||
parse_tree csm_pt; /* CLIgen parse tree */
|
parse_tree csm_pt; /* CLIgen parse tree */
|
||||||
|
|
||||||
} cli_syntaxmode_t;
|
} cli_syntaxmode_t;
|
||||||
|
|
||||||
/* Plugin group object. Just a single object, not list. part of cli_handle */
|
/* 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
|
/*! List files in a directory
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
expand_dir(char *dir,
|
expand_dir(char *dir,
|
||||||
int *nr,
|
int *nr,
|
||||||
char ***commands,
|
char ***commands,
|
||||||
mode_t flags,
|
mode_t flags,
|
||||||
int detail)
|
int detail)
|
||||||
{
|
{
|
||||||
DIR *dirp;
|
DIR *dirp;
|
||||||
struct dirent *dp;
|
struct dirent *dp;
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@ int xml_search_indexvar_binary_pos(cxobj *xp, char *indexvar, clixon_xvec *xvec,
|
||||||
#endif
|
#endif
|
||||||
int match_base_child(cxobj *x0, cxobj *x1c, yang_stmt *yc, cxobj **x0cp);
|
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,
|
int clixon_xml_find_index(cxobj *xp, yang_stmt *yp, char *namespace, char *name,
|
||||||
cvec *cvk, clixon_xvec **xvec);
|
cvec *cvk, clixon_xvec *xvec);
|
||||||
int clixon_xml_find_pos(cxobj *xp, yang_stmt *yc, uint32_t pos, clixon_xvec **xvec);
|
int clixon_xml_find_pos(cxobj *xp, yang_stmt *yc, uint32_t pos, clixon_xvec *xvec);
|
||||||
|
|
||||||
#endif /* _CLIXON_XML_SORT_H */
|
#endif /* _CLIXON_XML_SORT_H */
|
||||||
|
|
|
||||||
|
|
@ -310,6 +310,48 @@ nacm_rpc(char *rpc,
|
||||||
goto done;
|
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
|
/*! Prepare datastructures before running through XML tree
|
||||||
* Save rules in a "cache"
|
* Save rules in a "cache"
|
||||||
* These rules match:
|
* 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
|
* Also make instance-id lookups on top object for each rule. Assume at most one result
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
nacm_datanode_prepare(clicon_handle h,
|
nacm_datanode_prepare(clicon_handle h,
|
||||||
cxobj *xt,
|
cxobj *xt,
|
||||||
enum nacm_access access,
|
enum nacm_access access,
|
||||||
cxobj **gvec,
|
cxobj **gvec,
|
||||||
size_t glen,
|
size_t glen,
|
||||||
cxobj **rlistvec,
|
cxobj **rlistvec,
|
||||||
size_t rlistlen,
|
size_t rlistlen,
|
||||||
cvec *nsc,
|
cvec *nsc,
|
||||||
clixon_xvec *rulevec,
|
prepvec **pv_listp)
|
||||||
clixon_xvec *xpathvec)
|
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *rlist;
|
cxobj *rlist;
|
||||||
int i;
|
int i;
|
||||||
int j;
|
int j;
|
||||||
|
int k;
|
||||||
char *gname;
|
char *gname;
|
||||||
cxobj **rvec = NULL; /* rules */
|
cxobj **rvec = NULL; /* rules */
|
||||||
size_t rlen;
|
size_t rlen;
|
||||||
|
|
@ -346,6 +388,7 @@ nacm_datanode_prepare(clicon_handle h,
|
||||||
cxobj **xvec = NULL;
|
cxobj **xvec = NULL;
|
||||||
int xlen = 0;
|
int xlen = 0;
|
||||||
int ret;
|
int ret;
|
||||||
|
prepvec *pv;
|
||||||
|
|
||||||
yspec = clicon_dbspec_yang(h);
|
yspec = clicon_dbspec_yang(h);
|
||||||
for (i=0; i<rlistlen; i++){ /* Loop through rule list */
|
for (i=0; i<rlistlen; i++){ /* Loop through rule list */
|
||||||
|
|
@ -368,7 +411,6 @@ nacm_datanode_prepare(clicon_handle h,
|
||||||
xrule = rvec[j];
|
xrule = rvec[j];
|
||||||
/* 6c) For a "read" access operation, the rule's "access-operations"
|
/* 6c) For a "read" access operation, the rule's "access-operations"
|
||||||
leaf has the "read" bit set or has the special value "*" */
|
leaf has the "read" bit set or has the special value "*" */
|
||||||
|
|
||||||
access_operations = xml_find_body(xrule, "access-operations");
|
access_operations = xml_find_body(xrule, "access-operations");
|
||||||
switch (access){
|
switch (access){
|
||||||
case NACM_READ:
|
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 ((pathobj = xml_find_type(xrule, NULL, "path", CX_ELMNT)) == NULL){
|
||||||
if (xml_find_body(xrule, "rpc-name") || xml_find_body(xrule, "notification-name"))
|
if (xml_find_body(xrule, "rpc-name") || xml_find_body(xrule, "notification-name"))
|
||||||
continue;
|
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;
|
goto done;
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
|
|
@ -421,10 +464,13 @@ nacm_datanode_prepare(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
continue;
|
continue;
|
||||||
if (xlen > 1)
|
/* Here a new xrule is found, add it */
|
||||||
clicon_log(LOG_WARNING, "%s path:%s Clixon only supports single returns, this had: %d", __FUNCTION__, path, xlen);
|
if ((pv = prepvec_add(pv_listp, xrule)) == NULL)
|
||||||
if (clixon_xvec_append(xpathvec, xvec?xvec[0]:NULL) < 0) /* XXX: vector of vectors? */
|
|
||||||
goto done;
|
goto done;
|
||||||
|
for (k=0; k<xlen; k++){
|
||||||
|
if (clixon_xvec_append(pv->pv_xpathvec, xvec[k]) < 0)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
if (xvec){
|
if (xvec){
|
||||||
free(xvec);
|
free(xvec);
|
||||||
xvec = NULL;
|
xvec = NULL;
|
||||||
|
|
@ -438,8 +484,6 @@ nacm_datanode_prepare(clicon_handle h,
|
||||||
path = NULL;
|
path = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (clixon_xvec_append(rulevec, xrule) < 0) /* save it */
|
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
if (rvec){
|
if (rvec){
|
||||||
free(rvec);
|
free(rvec);
|
||||||
|
|
@ -472,15 +516,17 @@ nacm_datanode_prepare(clicon_handle h,
|
||||||
* @retval 2 OK and rule matches permit
|
* @retval 2 OK and rule matches permit
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
nacm_data_write_xrule_xml(cxobj *xn,
|
nacm_data_write_xrule_xml(cxobj *xn,
|
||||||
cxobj *xrule,
|
cxobj *xrule,
|
||||||
cxobj *xp,
|
clixon_xvec *xpathvec,
|
||||||
yang_stmt *yspec)
|
yang_stmt *yspec)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
yang_stmt *ymod;
|
yang_stmt *ymod;
|
||||||
char *module_pattern; /* rule module name */
|
char *module_pattern; /* rule module name */
|
||||||
char *action;
|
char *action;
|
||||||
|
cxobj *xp;
|
||||||
|
int i;
|
||||||
|
|
||||||
if ((module_pattern = xml_find_body(xrule, "module-name")) == NULL)
|
if ((module_pattern = xml_find_body(xrule, "module-name")) == NULL)
|
||||||
goto nomatch;
|
goto nomatch;
|
||||||
|
|
@ -502,11 +548,16 @@ nacm_data_write_xrule_xml(cxobj *xn,
|
||||||
goto deny;
|
goto deny;
|
||||||
goto permit;
|
goto permit;
|
||||||
}
|
}
|
||||||
/* Check if ancestor is xp */
|
for (i=0; i<clixon_xvec_len(xpathvec); i++){
|
||||||
if (xn != xp && !xml_isancestor(xn, xp))
|
xp = clixon_xvec_i(xpathvec, i);
|
||||||
goto nomatch;
|
/* Check if ancestor is xp (for every xpathvec?) */
|
||||||
if (strcmp(action, "deny")==0)
|
if (xn == xp || xml_isancestor(xn, xp)){
|
||||||
goto deny;
|
if (strcmp(action, "deny")==0)
|
||||||
|
goto deny;
|
||||||
|
goto permit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
goto nomatch;
|
||||||
permit:
|
permit:
|
||||||
retval = 2; /* rule match and permit */
|
retval = 2; /* rule match and permit */
|
||||||
done:
|
done:
|
||||||
|
|
@ -536,35 +587,40 @@ nacm_data_write_xrule_xml(cxobj *xn,
|
||||||
* deny: Send error message
|
* deny: Send error message
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
nacm_datanode_write_recurse(clicon_handle h,
|
nacm_datanode_write_recurse(clicon_handle h,
|
||||||
cxobj *xn,
|
cxobj *xn,
|
||||||
clixon_xvec *rulevec,
|
prepvec *pv_list,
|
||||||
clixon_xvec *xpathvec,
|
int defpermit,
|
||||||
int defpermit,
|
yang_stmt *yspec,
|
||||||
yang_stmt *yspec,
|
cbuf *cbret)
|
||||||
cbuf *cbret)
|
|
||||||
{
|
{
|
||||||
int retval=-1;
|
int retval = -1;
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
int i;
|
int ret = 0;
|
||||||
int ret = 0;
|
prepvec *pv;
|
||||||
|
|
||||||
for (i=0; i<clixon_xvec_len(rulevec); i++){ /* Loop through rule list */
|
pv = pv_list;
|
||||||
if ((ret = nacm_data_write_xrule_xml(xn,
|
if (pv){
|
||||||
clixon_xvec_i(rulevec, i),
|
do {
|
||||||
clixon_xvec_i(xpathvec, i),
|
/* return values: -1:Error /0:no match /1: deny /2: permit
|
||||||
yspec)) < 0)
|
*/
|
||||||
goto done;
|
if ((ret = nacm_data_write_xrule_xml(xn, pv->pv_xrule, pv->pv_xpathvec, yspec)) < 0)
|
||||||
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)
|
|
||||||
goto done;
|
goto done;
|
||||||
goto deny;
|
switch(ret){
|
||||||
break;
|
case 0: /* No match, continue with next rule */
|
||||||
}
|
break;
|
||||||
else if (ret == 2) /* Match and accpt: break rule processing but continue recursion */
|
case 1: /* Match and deny: break all traversal and send error back to client */
|
||||||
break;
|
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 no rule match, check default rule: if deny then break traversal and send error */
|
||||||
if (ret == 0 && !defpermit){
|
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 */
|
/* If node should be purged, dont recurse and defer removal to caller */
|
||||||
x = NULL; /* Recursively check XML */
|
x = NULL; /* Recursively check XML */
|
||||||
while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL) {
|
while ((x = xml_child_each(xn, x, CX_ELMNT)) != NULL) {
|
||||||
if ((ret = nacm_datanode_write_recurse(h, x,
|
if ((ret = nacm_datanode_write_recurse(h, x, pv_list,
|
||||||
rulevec, xpathvec,
|
|
||||||
defpermit, yspec, cbret)) < 0)
|
defpermit, yspec, cbret)) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
if (ret == 0)
|
if (ret == 0)
|
||||||
|
|
@ -616,17 +671,16 @@ nacm_datanode_write(clicon_handle h,
|
||||||
cxobj *xnacm,
|
cxobj *xnacm,
|
||||||
cbuf *cbret)
|
cbuf *cbret)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj **gvec = NULL; /* groups */
|
cxobj **gvec = NULL; /* groups */
|
||||||
size_t glen;
|
size_t glen;
|
||||||
cxobj **rlistvec = NULL; /* rule-list */
|
cxobj **rlistvec = NULL; /* rule-list */
|
||||||
size_t rlistlen;
|
size_t rlistlen;
|
||||||
cxobj **rvec = NULL; /* rules */
|
cxobj **rvec = NULL; /* rules */
|
||||||
char *write_default = NULL;
|
char *write_default = NULL;
|
||||||
cvec *nsc = NULL;
|
cvec *nsc = NULL;
|
||||||
clixon_xvec *rulevec = NULL;
|
int ret;
|
||||||
clixon_xvec *xpathvec = NULL;
|
prepvec *pv_list = NULL;
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* Create namespace context for with nacm namespace as default */
|
/* Create namespace context for with nacm namespace as default */
|
||||||
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
|
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");
|
clicon_err(OE_XML, EINVAL, "No nacm write-default rule");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 3. Check all the "group" entries to see if any of them contain a
|
/* 3. Check all the "group" entries to see if any of them contain a
|
||||||
"user-name" entry that equals the username for the session
|
"user-name" entry that equals the username for the session
|
||||||
making the request. (If the "enable-external-groups" leaf is
|
making the request. (If the "enable-external-groups" leaf is
|
||||||
|
|
@ -660,16 +713,10 @@ nacm_datanode_write(clicon_handle h,
|
||||||
goto done;
|
goto done;
|
||||||
/* First run through rules and cache rules as well as lookup objects in xt.
|
/* First run through rules and cache rules as well as lookup objects in xt.
|
||||||
*/
|
*/
|
||||||
if ((rulevec = clixon_xvec_new()) == NULL)
|
if (nacm_datanode_prepare(h, xt, access, gvec, glen, rlistvec, rlistlen, nsc, &pv_list) < 0)
|
||||||
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)
|
|
||||||
goto done;
|
goto done;
|
||||||
/* Then recursivelyy traverse all requested nodes */
|
/* Then recursivelyy traverse all requested nodes */
|
||||||
if ((ret = nacm_datanode_write_recurse(h, xreq,
|
if ((ret = nacm_datanode_write_recurse(h, xreq, pv_list,
|
||||||
rulevec, xpathvec,
|
|
||||||
strcmp(write_default, "deny"),
|
strcmp(write_default, "deny"),
|
||||||
clicon_dbspec_yang(h),
|
clicon_dbspec_yang(h),
|
||||||
cbret)) < 0)
|
cbret)) < 0)
|
||||||
|
|
@ -701,10 +748,8 @@ nacm_datanode_write(clicon_handle h,
|
||||||
retval = 1;
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d (0:deny 1:permit)", __FUNCTION__, retval);
|
||||||
if (xpathvec)
|
if (pv_list)
|
||||||
clixon_xvec_free(xpathvec);
|
prepvec_free(pv_list);
|
||||||
if (rulevec)
|
|
||||||
clixon_xvec_free(rulevec);
|
|
||||||
if (nsc)
|
if (nsc)
|
||||||
xml_nsctx_free(nsc);
|
xml_nsctx_free(nsc);
|
||||||
if (gvec)
|
if (gvec)
|
||||||
|
|
@ -764,13 +809,15 @@ nacm_data_read_action(cxobj *xrule,
|
||||||
static int
|
static int
|
||||||
nacm_data_read_xrule_xml(cxobj *xn,
|
nacm_data_read_xrule_xml(cxobj *xn,
|
||||||
cxobj *xrule,
|
cxobj *xrule,
|
||||||
cxobj *xp,
|
clixon_xvec *xpathvec,
|
||||||
yang_stmt *yspec)
|
yang_stmt *yspec)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
yang_stmt *ymod;
|
yang_stmt *ymod;
|
||||||
char *module_pattern; /* rule module name */
|
char *module_pattern; /* rule module name */
|
||||||
|
cxobj *xp;
|
||||||
|
int i;
|
||||||
|
|
||||||
if ((module_pattern = xml_find_body(xrule, "module-name")) == NULL)
|
if ((module_pattern = xml_find_body(xrule, "module-name")) == NULL)
|
||||||
goto nomatch;
|
goto nomatch;
|
||||||
/* 6a) The rule's "module-name" leaf is "*" or equals the name of
|
/* 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 done;
|
||||||
goto match;
|
goto match;
|
||||||
}
|
}
|
||||||
/* Check if ancestor is xp */
|
for (i=0; i<clixon_xvec_len(xpathvec); i++){
|
||||||
if (xn != xp && !xml_isancestor(xn, xp))
|
xp = clixon_xvec_i(xpathvec, i);
|
||||||
goto nomatch;
|
/* Check if ancestor is xp (for every xpathvec?) */
|
||||||
if (nacm_data_read_action(xrule, xn) < 0)
|
if (xn == xp || xml_isancestor(xn, xp)){
|
||||||
goto done;
|
if (nacm_data_read_action(xrule, xn) < 0)
|
||||||
match:
|
goto done;
|
||||||
retval = 1; /* match */
|
goto match;
|
||||||
done:
|
}
|
||||||
return retval;
|
}
|
||||||
nomatch:
|
nomatch:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
|
done:
|
||||||
|
return retval;
|
||||||
|
match:
|
||||||
|
retval = 1; /* match */
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -816,26 +867,30 @@ nacm_data_read_xrule_xml(cxobj *xn,
|
||||||
static int
|
static int
|
||||||
nacm_datanode_read_recurse(clicon_handle h,
|
nacm_datanode_read_recurse(clicon_handle h,
|
||||||
cxobj *xn,
|
cxobj *xn,
|
||||||
clixon_xvec *rulevec,
|
prepvec *pv_list,
|
||||||
clixon_xvec *xpathvec,
|
|
||||||
yang_stmt *yspec)
|
yang_stmt *yspec)
|
||||||
{
|
{
|
||||||
int retval=-1;
|
int retval = -1;
|
||||||
cxobj *x;
|
cxobj *x;
|
||||||
int i;
|
cxobj *xprev;
|
||||||
cxobj *xprev;
|
int ret;
|
||||||
int ret;
|
prepvec *pv;
|
||||||
|
|
||||||
if (xml_spec(xn)){ /* Check this node */
|
if (xml_spec(xn)){ /* Check this node */
|
||||||
for (i=0; i<clixon_xvec_len(rulevec); i++){ /* Loop through rule list */
|
pv = pv_list;
|
||||||
if ((ret = nacm_data_read_xrule_xml(xn,
|
if (pv){
|
||||||
clixon_xvec_i(rulevec, i),
|
do {
|
||||||
clixon_xvec_i(xpathvec, i),
|
if ((ret = nacm_data_read_xrule_xml(xn,
|
||||||
yspec)) < 0)
|
pv->pv_xrule,
|
||||||
goto done;
|
pv->pv_xpathvec,
|
||||||
if (ret == 1)
|
yspec)) < 0)
|
||||||
break; /* stop at first match */
|
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 0 /* 6(A) in algorithm
|
||||||
* If N did not match any rule R, and default rule is deny, remove that subtree */
|
* If N did not match any rule R, and default rule is deny, remove that subtree */
|
||||||
if (strcmp(read_default, "deny") == 0)
|
if (strcmp(read_default, "deny") == 0)
|
||||||
|
|
@ -849,7 +904,7 @@ nacm_datanode_read_recurse(clicon_handle h,
|
||||||
x = NULL; /* Recursively check XML */
|
x = NULL; /* Recursively check XML */
|
||||||
xprev = NULL;
|
xprev = NULL;
|
||||||
while ((x = xml_child_each(xn, x, CX_ELMNT)) != 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;
|
goto done;
|
||||||
/* check for delayed remove */
|
/* check for delayed remove */
|
||||||
if (xml_flag(x, XML_FLAG_DEL)){
|
if (xml_flag(x, XML_FLAG_DEL)){
|
||||||
|
|
@ -920,16 +975,15 @@ nacm_datanode_read(clicon_handle h,
|
||||||
char *username,
|
char *username,
|
||||||
cxobj *xnacm)
|
cxobj *xnacm)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj **gvec = NULL; /* groups */
|
cxobj **gvec = NULL; /* groups */
|
||||||
size_t glen;
|
size_t glen;
|
||||||
cxobj **rlistvec = NULL; /* rule-list */
|
cxobj **rlistvec = NULL; /* rule-list */
|
||||||
size_t rlistlen;
|
size_t rlistlen;
|
||||||
int i;
|
int i;
|
||||||
char *read_default = NULL;
|
char *read_default = NULL;
|
||||||
cvec *nsc = NULL;
|
cvec *nsc = NULL;
|
||||||
clixon_xvec *rulevec = NULL;
|
prepvec *pv_list = NULL;
|
||||||
clixon_xvec *xpathvec = NULL;
|
|
||||||
|
|
||||||
/* Create namespace context for with nacm namespace as default */
|
/* Create namespace context for with nacm namespace as default */
|
||||||
if ((nsc = xml_nsctx_init(NULL, NACM_NS)) == NULL)
|
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.
|
/* First run through rules and cache rules as well as lookup objects in xt.
|
||||||
* DANGER: objects could be stale if they are removed?
|
* DANGER: objects could be stale if they are removed?
|
||||||
*/
|
*/
|
||||||
if ((rulevec = clixon_xvec_new()) == NULL)
|
if (nacm_datanode_prepare(h, xt, NACM_READ, gvec, glen, rlistvec, rlistlen, nsc, &pv_list) < 0)
|
||||||
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)
|
|
||||||
goto done;
|
goto done;
|
||||||
/* Then recursivelyy traverse all nodes */
|
/* 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;
|
goto done;
|
||||||
#if 1
|
#if 1
|
||||||
/* Step 8(B) above:
|
/* Step 8(B) above:
|
||||||
|
|
@ -999,10 +1048,8 @@ nacm_datanode_read(clicon_handle h,
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
if (xpathvec)
|
if (pv_list)
|
||||||
clixon_xvec_free(xpathvec);
|
prepvec_free(pv_list);
|
||||||
if (rulevec)
|
|
||||||
clixon_xvec_free(rulevec);
|
|
||||||
if (nsc)
|
if (nsc)
|
||||||
xml_nsctx_free(nsc);
|
xml_nsctx_free(nsc);
|
||||||
if (gvec)
|
if (gvec)
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,16 @@
|
||||||
* - instance-identifier as defined by YANG
|
* - instance-identifier as defined by YANG
|
||||||
* - clixon-path is an internal format which both ^ use as internal representation
|
* - 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.
|
* "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.
|
* and defined in RF7950 Sections 9.13 and 14.
|
||||||
* To note: prefixes depend on the XML context in which the value occurs,
|
* 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
|
* "api-path" is "URI-encoded path expression" definition in RFC8040 3.5.3
|
||||||
* BNF:
|
* BNF:
|
||||||
* <api-path> := <root> ("/" (<api-identifier> | <list-instance>))*
|
* <api-path> := <root> ("/" (<api-identifier> | <list-instance>))*
|
||||||
|
|
@ -1365,31 +1371,40 @@ instance_id_resolve(clixon_path *cplist,
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
cp->cp_yang = yc;
|
cp->cp_yang = yc;
|
||||||
if (cp->cp_cvk){
|
switch (yang_keyword_get(yc)){
|
||||||
/* Iterate over yang list keys and assign as names in cvk */
|
case Y_LIST:
|
||||||
if (yang_keyword_get(yc) == Y_LEAF_LIST)
|
if (cp->cp_cvk == NULL){
|
||||||
;
|
#if 0 /* XXX why is this not enforced? */
|
||||||
else if (yang_keyword_get(yc) == Y_LIST){
|
clicon_err(OE_YANG, ENOENT, "key-values mandatory for lists");
|
||||||
#ifdef notused
|
goto fail;
|
||||||
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
|
#endif
|
||||||
i = 0;
|
break;
|
||||||
cva = NULL;
|
}
|
||||||
while ((cva = cvec_each(cp->cp_cvk, cva)) != NULL) {
|
#if 0 /* XXX why is this not enforced? */
|
||||||
if (cv_name_get(cva) == NULL){
|
if (cvec_len(cp->cp_cvk) > cvec_len(yang_cvec_get(yc))){
|
||||||
cvy = cvec_i(yang_cvec_get(yc), i);
|
clicon_err(OE_YANG, ENOENT, "Number of keys in key-value list does not match Yang list ");
|
||||||
cv_name_set(cva, cv_string_get(cvy));
|
goto fail;
|
||||||
}
|
}
|
||||||
i++;
|
#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");
|
clicon_err(OE_YANG, ENOENT, "key-values only defined for list or leaf-list");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
yt = yc;
|
yt = yc;
|
||||||
cp = NEXTQ(clixon_path *, cp);
|
cp = NEXTQ(clixon_path *, cp);
|
||||||
|
|
@ -1439,22 +1454,26 @@ clixon_path_search(cxobj *xt,
|
||||||
yc = cp->cp_yang;
|
yc = cp->cp_yang;
|
||||||
if ((modns = yang_find_mynamespace(yc)) == NULL)
|
if ((modns = yang_find_mynamespace(yc)) == NULL)
|
||||||
goto fail;
|
goto fail;
|
||||||
if (xvecp)
|
if (xvecp){
|
||||||
for (i=0; i<clixon_xvec_len(xvecp); i++){
|
if ((xvecc = clixon_xvec_new()) == NULL)
|
||||||
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)
|
|
||||||
goto done;
|
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)
|
if (xvecp)
|
||||||
clixon_xvec_free(xvecp);
|
clixon_xvec_free(xvecp);
|
||||||
xvecp = xvecc;
|
xvecp = xvecc;
|
||||||
|
|
|
||||||
|
|
@ -179,6 +179,8 @@ populate_self_parent(cxobj *xt,
|
||||||
#endif
|
#endif
|
||||||
retval = 1;
|
retval = 1;
|
||||||
done:
|
done:
|
||||||
|
if (cb)
|
||||||
|
cbuf_free(cb);
|
||||||
return retval;
|
return retval;
|
||||||
fail:
|
fail:
|
||||||
retval = 0;
|
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
|
/*! 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)
|
* 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] xp Parent xml node.
|
||||||
* @param[in] x1 Find this object among xp:s children
|
* @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
|
* @see xml_find_index for a generic search function
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
xml_search_yang(cxobj *xp,
|
xml_search_yang(cxobj *xp,
|
||||||
cxobj *x1,
|
cxobj *x1,
|
||||||
yang_stmt *yc,
|
yang_stmt *yc,
|
||||||
int skip1,
|
int skip1,
|
||||||
char *indexvar,
|
char *indexvar,
|
||||||
clixon_xvec *xvec)
|
clixon_xvec *xvec)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
|
|
@ -779,7 +780,7 @@ xml_search_yang(cxobj *xp,
|
||||||
else
|
else
|
||||||
#endif
|
#endif
|
||||||
if (yang_keyword_get(yc) == Y_LIST || yang_keyword_get(yc) == Y_LEAF_LIST)
|
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);
|
yangi = yang_order(yc);
|
||||||
|
|
||||||
if (xml_search_binary(xp, x1, sorted, yangi, low, upper, skip1, indexvar, xvec) < 0)
|
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
|
/*! 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
|
* 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,
|
* 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
|
* - if no, revert (return 0) so that the overlying algorithm can try next or fallback to
|
||||||
* linear seacrh
|
* linear seacrh
|
||||||
|
|
@ -1306,6 +1307,10 @@ xml_find_index_yang(cxobj *xp,
|
||||||
cprintf(cb, "<%s>", name);
|
cprintf(cb, "<%s>", name);
|
||||||
ycvk = yang_cvec_get(yc); /* Check that those are proper index keys */
|
ycvk = yang_cvec_get(yc); /* Check that those are proper index keys */
|
||||||
cvi = NULL;
|
cvi = NULL;
|
||||||
|
if (cvk == NULL){ /* If list and no keys, all should match */
|
||||||
|
revert++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
i = 0;
|
i = 0;
|
||||||
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
while ((cvi = cvec_each(cvk, cvi)) != NULL) {
|
||||||
if ((kname = cv_name_get(cvi)) == NULL){
|
if ((kname = cv_name_get(cvi)) == NULL){
|
||||||
|
|
@ -1313,7 +1318,7 @@ xml_find_index_yang(cxobj *xp,
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
/* Parameter in cvk is not key or not in right key order, then we cannot call
|
/* 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))){
|
if ((ycv = cvec_i(ycvk, i)) == NULL || strcmp(kname, cv_string_get(ycv))){
|
||||||
revert++;
|
revert++;
|
||||||
|
|
@ -1411,12 +1416,11 @@ xml_find_index_yang(cxobj *xp,
|
||||||
* - Otherwise search is made using linear search
|
* - Otherwise search is made using linear search
|
||||||
*
|
*
|
||||||
* @param[in] xp Parent xml node.
|
* @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] 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] 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[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 0 OK, see xret
|
||||||
* @retval -1 Error
|
* @retval -1 Error
|
||||||
* @code
|
* @code
|
||||||
|
|
@ -1424,10 +1428,12 @@ xml_find_index_yang(cxobj *xp,
|
||||||
* cvec *cvk = NULL; vector of index keys
|
* cvec *cvk = NULL; vector of index keys
|
||||||
* cxobj *x;
|
* cxobj *x;
|
||||||
* ... Populate cvk with key/values eg a:5 b:6
|
* ... 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;
|
* err;
|
||||||
* for (i=0; i<clixon_xvec_len(xvec); i++){
|
* if (clixon_xml_find_index(xp, yp, NULL, "a", ns, cvk, xv) < 0)
|
||||||
* x = clixon_xpath_i(xvec, i);
|
* err;
|
||||||
|
* for (i=0; i<clixon_xvec_len(xv); i++){
|
||||||
|
* x = clixon_xpath_i(xv, i);
|
||||||
* ...
|
* ...
|
||||||
* }
|
* }
|
||||||
* clixon_xvec_free(xvec);
|
* clixon_xvec_free(xvec);
|
||||||
|
|
@ -1443,14 +1449,18 @@ clixon_xml_find_index(cxobj *xp,
|
||||||
char *namespace,
|
char *namespace,
|
||||||
char *name,
|
char *name,
|
||||||
cvec *cvk,
|
cvec *cvk,
|
||||||
clixon_xvec **xvec)
|
clixon_xvec *xvec)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
int ret;
|
int ret;
|
||||||
yang_stmt *yc = NULL;
|
yang_stmt *yc = NULL;
|
||||||
|
|
||||||
|
if (xvec == NULL){
|
||||||
|
clicon_err(OE_YANG, EINVAL, "xvec");
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
if (name == NULL){
|
if (name == NULL){
|
||||||
clicon_err(OE_YANG, ENOENT, "name");
|
clicon_err(OE_YANG, EINVAL, "name");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
if (yp == NULL)
|
if (yp == NULL)
|
||||||
|
|
@ -1461,27 +1471,21 @@ clixon_xml_find_index(cxobj *xp,
|
||||||
if (yp)
|
if (yp)
|
||||||
yc = yang_find_datanode(yp, name);
|
yc = yang_find_datanode(yp, name);
|
||||||
}
|
}
|
||||||
if ((*xvec = clixon_xvec_new()) == NULL)
|
|
||||||
goto done;
|
|
||||||
if (yc){
|
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;
|
goto done;
|
||||||
if (ret == 0){ /* This means yang method did not work for some reason
|
if (ret == 0){ /* This means yang method did not work for some reason
|
||||||
* such as not being list key indexes in cvk, for example
|
* 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;
|
goto done;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
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;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (retval < 0){
|
|
||||||
clixon_xvec_free(*xvec);
|
|
||||||
*xvec = NULL;
|
|
||||||
}
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1499,7 +1503,7 @@ int
|
||||||
clixon_xml_find_pos(cxobj *xp,
|
clixon_xml_find_pos(cxobj *xp,
|
||||||
yang_stmt *yc,
|
yang_stmt *yc,
|
||||||
uint32_t pos,
|
uint32_t pos,
|
||||||
clixon_xvec **xvec)
|
clixon_xvec *xvec)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
cxobj *xc = NULL;
|
cxobj *xc = NULL;
|
||||||
|
|
@ -1513,22 +1517,16 @@ clixon_xml_find_pos(cxobj *xp,
|
||||||
name = yang_argument_get(yc);
|
name = yang_argument_get(yc);
|
||||||
u = 0;
|
u = 0;
|
||||||
xc = NULL;
|
xc = NULL;
|
||||||
if ((*xvec = clixon_xvec_new()) == NULL)
|
|
||||||
goto done;
|
|
||||||
while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL) {
|
while ((xc = xml_child_each(xp, xc, CX_ELMNT)) != NULL) {
|
||||||
if (strcmp(name, xml_name(xc)))
|
if (strcmp(name, xml_name(xc)))
|
||||||
continue;
|
continue;
|
||||||
if (pos == u++){ /* Found */
|
if (pos == u++){ /* Found */
|
||||||
if (clixon_xvec_append(*xvec, xc) < 0)
|
if (clixon_xvec_append(xvec, xc) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
if (retval < 0){
|
|
||||||
clixon_xvec_free(*xvec);
|
|
||||||
*xvec = NULL;
|
|
||||||
}
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -227,9 +227,9 @@ loop_preds(xpath_tree *xt,
|
||||||
* y[k=3] # corresponds to: <name>[<keyname>=<keyval>]
|
* y[k=3] # corresponds to: <name>[<keyname>=<keyval>]
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
xpath_list_optimize_fn(xpath_tree *xt,
|
xpath_list_optimize_fn(xpath_tree *xt,
|
||||||
cxobj *xv,
|
cxobj *xv,
|
||||||
clixon_xvec **xvec)
|
clixon_xvec *xvec)
|
||||||
{
|
{
|
||||||
int retval = -1;
|
int retval = -1;
|
||||||
xpath_tree *xm = NULL;
|
xpath_tree *xm = NULL;
|
||||||
|
|
@ -329,8 +329,10 @@ xpath_optimize_check(xpath_tree *xs,
|
||||||
|
|
||||||
if (!_optimize_enable)
|
if (!_optimize_enable)
|
||||||
return 0; /* use regular code */
|
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 */
|
/* 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;
|
return -1;
|
||||||
if (ret == 1){
|
if (ret == 1){
|
||||||
if (clixon_xvec_extract(xvec, xvec0, xlen0) < 0)
|
if (clixon_xvec_extract(xvec, xvec0, xlen0) < 0)
|
||||||
|
|
|
||||||
|
|
@ -156,8 +156,10 @@ trigger_rpc(clicon_handle h, /* Clicon handle */
|
||||||
goto done;
|
goto done;
|
||||||
cv_name_set(cv, "k");
|
cv_name_set(cv, "k");
|
||||||
cv_string_set(cv, "5");
|
cv_string_set(cv, "5");
|
||||||
|
if ((xv = clixon_xvec_new()) == NULL)
|
||||||
|
goto done;
|
||||||
/* Use form 2c use spec of xc + name */
|
/* 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;
|
goto done;
|
||||||
if (clixon_xvec_len(xv))
|
if (clixon_xvec_len(xv))
|
||||||
val = xml_find_body(clixon_xvec_i(xv,0), "val");
|
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.
|
* realloc. Therefore, we append new data to the userdata buffer.
|
||||||
*/
|
*/
|
||||||
static size_t
|
static size_t
|
||||||
curl_get_cb(void *ptr,
|
curl_get_cb(void *ptr,
|
||||||
size_t size,
|
size_t size,
|
||||||
size_t nmemb,
|
size_t nmemb,
|
||||||
void *userdata)
|
void *userdata)
|
||||||
{
|
{
|
||||||
struct curlbuf *buf = (struct curlbuf *)userdata;
|
struct curlbuf *buf = (struct curlbuf *)userdata;
|
||||||
int len;
|
int len;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue