optimized NACM read paths

This commit is contained in:
Olof hagsand 2020-04-15 22:02:33 +02:00
parent 1fb3dcc686
commit f401c07c4b

View file

@ -71,6 +71,7 @@
#include "clixon_xml_nsctx.h" #include "clixon_xml_nsctx.h"
#include "clixon_xml_map.h" #include "clixon_xml_map.h"
#include "clixon_path.h" #include "clixon_path.h"
#include "clixon_xml_vec.h"
#include "clixon_nacm.h" #include "clixon_nacm.h"
/* NACM namespace for use with xml namespace contexts and xpath */ /* NACM namespace for use with xml namespace contexts and xpath */
@ -523,19 +524,12 @@ static int
nacm_data_read_xrule_xml(cxobj *xt, nacm_data_read_xrule_xml(cxobj *xt,
cxobj *xn, cxobj *xn,
cxobj *xrule, cxobj *xrule,
cxobj *xp,
yang_stmt *yspec) yang_stmt *yspec)
{ {
int retval = -1; int retval = -1;
cxobj *xp;
int ret;
cxobj *pathobj; cxobj *pathobj;
char *access_operations;
cvec *nsc0 = NULL; /* Non-canonical namespace context */
char *path0; /* Non-canonical path */
char *path=NULL; /* Canonical path */
yang_stmt *ymod; yang_stmt *ymod;
cxobj **xvec = NULL;
size_t xlen = 0;
char *module_rule; /* rule module name */ char *module_rule; /* rule module name */
if ((module_rule = xml_find_body(xrule, "module-name")) == NULL) if ((module_rule = xml_find_body(xrule, "module-name")) == NULL)
@ -553,26 +547,12 @@ nacm_data_read_xrule_xml(cxobj *xt,
(2) the "rule-type" is "data-node" and the "path" matches the (2) the "rule-type" is "data-node" and the "path" matches the
requested data node, action node, or notification node. */ requested data node, action node, or notification node. */
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"))
goto nomatch;
}
access_operations = xml_find_body(xrule, "access-operations");
/* 6c) For a "read" access operation, the rule's "access-operations"
leaf has the "read" bit set or has the special value "*" */
if (!match_access(access_operations, "read", NULL))
goto nomatch;
if (pathobj == NULL){
if (nacm_data_read_action(xrule, xn) < 0) if (nacm_data_read_action(xrule, xn) < 0)
goto done; goto done;
goto match; goto match;
} }
path0 = clixon_trim2(xml_body(pathobj), " \t\n");
/* Create namespace context for with nacm namespace as default */ /* Create namespace context for with nacm namespace as default */
if (xml_nsctx_node(pathobj, &nsc0) < 0)
goto done;
/* instance-id requires canonical paths */
if (xpath2canonical(path0, nsc0, yspec, &path, NULL) < 0)
goto done;
/* path may not be yang-consistent gives an error,... but retval =0 back,... /* path may not be yang-consistent gives an error,... but retval =0 back,...
* Here I may need another function. * Here I may need another function.
* I have a top-level node "xt", a node "xn" and a "path". * I have a top-level node "xt", a node "xn" and a "path".
@ -580,33 +560,15 @@ nacm_data_read_xrule_xml(cxobj *xt,
* The brute force is to evaluate the result in xvec and then see if ancestors of * The brute force is to evaluate the result in xvec and then see if ancestors of
* xn is in xvec. * xn is in xvec.
*/ */
if ((ret = clixon_xml_find_instance_id(xt, yspec, &xvec, &xlen, "%s", path)) < 0)
goto done; /* Check if ancestor is xp */
switch (ret){ if (xn != xp && !xml_isancestor(xn, xp))
case 0: /* Actually some kind of failure, eg yang mismatch */
goto nomatch; goto nomatch;
break; if (nacm_data_read_action(xrule, xn) < 0)
case 1: goto done;
if (xlen == 0)
goto nomatch;
assert(xlen == 1);
xp = xvec[0]; /* XXX: loop? */
/* Check if ancestor is xp */
if (xn != xp && !xml_isancestor(xn, xp))
goto nomatch;
if (nacm_data_read_action(xrule, xn) < 0)
goto done;
break;
} /* switch */
match: match:
retval = 1; /* match */ retval = 1; /* match */
done: done:
if (path)
free(path);
if (nsc0)
cvec_free(nsc0);
if (xvec)
free(xvec);
return retval; return retval;
nomatch: nomatch:
retval = 0; retval = 0;
@ -635,52 +597,25 @@ nacm_datanode_read_recurse(clicon_handle h,
cxobj **rlistvec, cxobj **rlistvec,
size_t rlistlen, size_t rlistlen,
cvec *nsc, cvec *nsc,
clixon_xvec *rulevec,
clixon_xvec *xpathvec,
yang_stmt *yspec) yang_stmt *yspec)
{ {
int retval=-1; int retval=-1;
cxobj *rlist;
cxobj **rvec = NULL; /* rules */
size_t rlen;
cxobj *x; cxobj *x;
int i; int i;
int j;
char *gname;
cxobj *xrule;
cxobj *xprev; cxobj *xprev;
int match; int ret;
if (xml_spec(xn)){ /* Check this node */ if (xml_spec(xn)){ /* Check this node */
for (i=0; i<rlistlen; i++){ /* Loop through rule list */ for (i=0; i<clixon_xvec_len(rulevec); i++){ /* Loop through rule list */
rlist = rlistvec[i]; if ((ret = nacm_data_read_xrule_xml(xt, xn,
/* Loop through user's group to find match in this rule-list */ clixon_xvec_i(rulevec, i),
for (j=0; j<glen; j++){ clixon_xvec_i(xpathvec, i),
gname = xml_find_body(gvec[j], "name"); yspec)) < 0)
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL) goto done;
break; /* found */ if (ret == 1)
} break; /* stop at first match */
if (j==glen) /* not found */
continue;
/* 6. For each rule-list entry found, process all rules, in order,
until a rule that matches the requested access operation is
found. (see 6 sub rules in nacm_rule_datanode
*/
if (xpath_vec(rlist, nsc, "rule", &rvec, &rlen) < 0)
goto done;
match = 0;
for (j=0; j<rlen; j++){ /* Loop through rules */
xrule = rvec[j];
if ((match = nacm_data_read_xrule_xml(xt, xn, xrule, yspec)) < 0)
goto done;
if (match == 1){ /* match */
break;
}
}
if (rvec){
free(rvec);
rvec=NULL;
}
if (match)
break;
} }
#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 */
@ -689,12 +624,14 @@ nacm_datanode_read_recurse(clicon_handle h,
goto done; goto done;
#endif #endif
} }
/* If node should be purged, dont recurse and defewr removal to caller */ /* If node should be purged, dont recurse and defewr removal to caller */
if (xml_flag(xn, XML_FLAG_DEL) == 0){ if (xml_flag(xn, XML_FLAG_DEL) == 0){
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, xt, x, gvec, glen, rlistvec, rlistlen, nsc, yspec) < 0) if (nacm_datanode_read_recurse(h, xt, x, gvec, glen, rlistvec, rlistlen, nsc,
rulevec, xpathvec, 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)){
@ -706,10 +643,123 @@ nacm_datanode_read_recurse(clicon_handle h,
} }
retval = 0; retval = 0;
done: done:
if (rvec){ return retval;
free(rvec); }
rvec=NULL;
} /*! Prepare datastructures before running through XML tree
* Save rules in a "cache"
* These rules match:
* - user/group
* - have read access-op, etc
* Also make instance-id lookups on top object for each rule. Assume at most one result
*/
static int
nacm_datanode_read_prepare(clicon_handle h,
cxobj *xt,
cxobj **gvec,
size_t glen,
cxobj **rlistvec,
size_t rlistlen,
cvec *nsc,
clixon_xvec *rulevec,
clixon_xvec *xpathvec)
{
int retval = -1;
cxobj *rlist;
int i;
int j;
char *gname;
cxobj **rvec = NULL; /* rules */
size_t rlen;
cxobj *xrule;
cxobj *pathobj;
char *access_operations;
char *path0; /* Non-canonical path */
char *path=NULL; /* Canonical path */
cvec *nsc0 = NULL; /* Non-canonical namespace context */
yang_stmt *yspec;
cxobj **xvec = NULL;
size_t xlen = 0;
int ret;
yspec = clicon_dbspec_yang(h);
for (i=0; i<rlistlen; i++){ /* Loop through rule list */
rlist = rlistvec[i];
/* Loop through user's group to find match in this rule-list */
for (j=0; j<glen; j++){
gname = xml_find_body(gvec[j], "name");
if (xpath_first(rlist, nsc, ".[group='%s']", gname)!=NULL)
break; /* found */
}
if (j==glen) /* not found */
continue;
/* 6. For each rule-list entry found, process all rules, in order,
until a rule that matches the requested access operation is
found. (see 6 sub rules in nacm_rule_datanode
*/
if (xpath_vec(rlist, nsc, "rule", &rvec, &rlen) < 0)
goto done;
for (j=0; j<rlen; j++){ /* Loop through rules */
xrule = rvec[j];
/* 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");
if (!match_access(access_operations, "read", NULL))
continue;
/* 6b) Either (1) the rule does not have a "rule-type" defined or
(2) the "rule-type" is "data-node" and the "path" matches the
requested data node, action node, or notification node. */
if ((pathobj = xml_find_type(xrule, NULL, "path", CX_ELMNT)) == NULL){
if (xml_find_body(xrule, "rpc-name") || xml_find_body(xrule, "notification-name"))
continue;
if (clixon_xvec_append(xpathvec, NULL) < 0) /* XXX: vector of vectors? */
goto done;
}
else{
path0 = clixon_trim2(xml_body(pathobj), " \t\n");
/* Create namespace context for with nacm namespace as default */
if (xml_nsctx_node(pathobj, &nsc0) < 0)
goto done;
/* instance-id requires canonical paths */
if (xpath2canonical(path0, nsc0, yspec, &path, NULL) < 0)
goto done;
if ((ret = clixon_xml_find_instance_id(xt, yspec, &xvec, &xlen, "%s", path)) < 0)
goto done;
if (ret == 0)
continue;
if (xlen > 1)
clicon_log(LOG_WARNING, "%s path:%s Clixon only supports single returns, this had: %lu", __FUNCTION__, path, xlen);
if (clixon_xvec_append(xpathvec, xvec?xvec[0]:NULL) < 0) /* XXX: vector of vectors? */
goto done;
if (xvec){
free(xvec);
xvec = NULL;
}
if (nsc0){
cvec_free(nsc0);
nsc0 = NULL;
}
if (path){
free(path);
path = NULL;
}
}
if (clixon_xvec_append(rulevec, xrule) < 0) /* save it */
goto done;
}
if (rvec){
free(rvec);
rvec=NULL;
}
}
retval = 0;
done:
if (xvec)
free(xvec);
if (path)
free(path);
if (nsc0)
cvec_free(nsc0);
return retval; return retval;
} }
@ -769,14 +819,16 @@ 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;
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)
@ -804,8 +856,19 @@ nacm_datanode_read(clicon_handle h,
clicon_err(OE_XML, EINVAL, "No nacm read-default rule"); clicon_err(OE_XML, EINVAL, "No nacm read-default rule");
goto done; goto done;
} }
/* 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_read_prepare(h, xt, gvec, glen, rlistvec, rlistlen, nsc,
rulevec, xpathvec) < 0)
goto done;
/* Then recursivelyy traverse all nodes */
if (nacm_datanode_read_recurse(h, xt, xt, gvec, glen, rlistvec, rlistlen, nsc, if (nacm_datanode_read_recurse(h, xt, xt, gvec, glen, rlistvec, rlistlen, nsc,
clicon_dbspec_yang(h)) < 0) rulevec, xpathvec, clicon_dbspec_yang(h)) < 0)
goto done; goto done;
#if 1 #if 1
/* Step 8(B) above: /* Step 8(B) above:
@ -836,6 +899,10 @@ 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)
clixon_xvec_free(xpathvec);
if (rulevec)
clixon_xvec_free(rulevec);
if (nsc) if (nsc)
xml_nsctx_free(nsc); xml_nsctx_free(nsc);
if (gvec) if (gvec)