* Experimental NACM RFC8341 Network Configuration Access Control Model.
* CLICON_NACM_MODE config option, default is disabled. * Added username attribute to all rpc:s from frontend to backend * Added NACM backend module in example
This commit is contained in:
parent
04a3f4db1b
commit
7650803475
32 changed files with 908 additions and 274 deletions
|
|
@ -212,8 +212,10 @@ notfound(FCGX_Request *r)
|
|||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
|
||||
FCGX_FPrintF(r->out, "Status: 404\r\n"); /* 404 not found */
|
||||
|
||||
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
|
||||
FCGX_FPrintF(r->out, "<h1>Not Found</h1>\n");
|
||||
FCGX_FPrintF(r->out, "Not Found\n");
|
||||
FCGX_FPrintF(r->out, "The requested URL %s was not found on this server.\n",
|
||||
path);
|
||||
return 0;
|
||||
|
|
@ -409,8 +411,8 @@ api_return_err(clicon_handle h,
|
|||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
if ((cb = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
if ((xtag = xpath_first(xerr, "error-tag")) == NULL){
|
||||
notfound(r); /* bad reply? */
|
||||
if ((xtag = xpath_first(xerr, "//error-tag")) == NULL){
|
||||
notfound(r);
|
||||
goto ok;
|
||||
}
|
||||
tagstr = xml_body(xtag);
|
||||
|
|
|
|||
|
|
@ -92,6 +92,9 @@
|
|||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] dvec Stream input daat
|
||||
* @param[in] pretty Set to 1 for pretty-printed xml/json output
|
||||
* @param[in] use_xml Set to 0 for JSON and 1 for XML
|
||||
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
|
||||
*/
|
||||
static int
|
||||
api_data(clicon_handle h,
|
||||
|
|
@ -100,28 +103,17 @@ api_data(clicon_handle h,
|
|||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data)
|
||||
char *data,
|
||||
int pretty,
|
||||
int use_xml,
|
||||
int parse_xml)
|
||||
{
|
||||
int retval = -1;
|
||||
char *request_method;
|
||||
int pretty;
|
||||
char *media_content_type;
|
||||
int parse_xml = 0; /* By default expect and parse JSON */
|
||||
char *media_accept;
|
||||
int use_xml = 0; /* By default use JSON */
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
||||
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
|
||||
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
||||
if (strcmp(media_accept, "application/yang-data+xml")==0)
|
||||
use_xml++;
|
||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
||||
if (media_content_type &&
|
||||
strcmp(media_content_type, "application/yang-data+xml")==0)
|
||||
parse_xml++;
|
||||
|
||||
if (strcmp(request_method, "OPTIONS")==0)
|
||||
retval = api_data_options(h, r);
|
||||
else if (strcmp(request_method, "HEAD")==0)
|
||||
|
|
@ -150,6 +142,7 @@ api_data(clicon_handle h,
|
|||
* @param[in] pi Offset, where to start pcvec
|
||||
* @param[in] qvec Vector of query string (QUERY_STRING)
|
||||
* @param[in] data Stream input data
|
||||
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
|
||||
*/
|
||||
static int
|
||||
api_operations(clicon_handle h,
|
||||
|
|
@ -158,28 +151,17 @@ api_operations(clicon_handle h,
|
|||
cvec *pcvec,
|
||||
int pi,
|
||||
cvec *qvec,
|
||||
char *data)
|
||||
char *data,
|
||||
int pretty,
|
||||
int use_xml,
|
||||
int parse_xml)
|
||||
{
|
||||
int retval = -1;
|
||||
char *request_method;
|
||||
int pretty;
|
||||
char *media_content_type;
|
||||
int parse_xml = 0; /* By default expect and parse JSON */
|
||||
char *media_accept;
|
||||
int use_xml = 0; /* By default use JSON */
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
request_method = FCGX_GetParam("REQUEST_METHOD", r->envp);
|
||||
clicon_debug(1, "%s method:%s", __FUNCTION__, request_method);
|
||||
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
||||
if (strcmp(media_accept, "application/yang-data+xml")==0)
|
||||
use_xml++;
|
||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
||||
if (media_content_type &&
|
||||
strcmp(media_content_type, "application/yang-data+xml")==0)
|
||||
parse_xml++;
|
||||
|
||||
if (strcmp(request_method, "GET")==0)
|
||||
retval = api_operations_get(h, r, path, pcvec, pi, qvec, data, pretty, use_xml);
|
||||
else if (strcmp(request_method, "POST")==0)
|
||||
|
|
@ -293,7 +275,6 @@ api_yang_library_version(clicon_handle h,
|
|||
if (xml_rootchild(xt, 0, &xt) < 0)
|
||||
goto done;
|
||||
if ((cb = cbuf_new()) == NULL){
|
||||
clicon_err(OE_XML, errno, "cbuf_new");
|
||||
goto done;
|
||||
}
|
||||
if (use_xml){
|
||||
|
|
@ -335,16 +316,33 @@ api_restconf(clicon_handle h,
|
|||
cbuf *cb = NULL;
|
||||
char *data;
|
||||
int authenticated = 0;
|
||||
char *media_accept;
|
||||
char *media_content_type;
|
||||
int pretty;
|
||||
int parse_xml = 0; /* By default expect and parse JSON */
|
||||
int use_xml = 0; /* By default use JSON */
|
||||
cbuf *cbret = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
|
||||
clicon_debug(1, "%s", __FUNCTION__);
|
||||
path = FCGX_GetParam("REQUEST_URI", r->envp);
|
||||
query = FCGX_GetParam("QUERY_STRING", r->envp);
|
||||
pretty = clicon_option_bool(h, "CLICON_RESTCONF_PRETTY");
|
||||
/* get xml/json in put and output */
|
||||
media_accept = FCGX_GetParam("HTTP_ACCEPT", r->envp);
|
||||
if (media_accept && strcmp(media_accept, "application/yang-data+xml")==0)
|
||||
use_xml++;
|
||||
media_content_type = FCGX_GetParam("HTTP_CONTENT_TYPE", r->envp);
|
||||
if (media_content_type &&
|
||||
strcmp(media_content_type, "application/yang-data+xml")==0)
|
||||
parse_xml++;
|
||||
if ((pvec = clicon_strsep(path, "/", &pn)) == NULL)
|
||||
goto done;
|
||||
/* Sanity check of path. Should be /restconf/ */
|
||||
if (pn < 2){
|
||||
retval = notfound(r);
|
||||
goto done;
|
||||
notfound(r);
|
||||
goto ok;
|
||||
}
|
||||
if (strlen(pvec[0]) != 0){
|
||||
retval = notfound(r);
|
||||
|
|
@ -390,7 +388,13 @@ api_restconf(clicon_handle h,
|
|||
clicon_username_set(h, "none");
|
||||
}
|
||||
else{
|
||||
unauthorized(r);
|
||||
if (netconf_access_denied_xml(&xret, "protocol", "The requested URL was unauthorized") < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xret, "//rpc-error")) != NULL){
|
||||
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
goto ok;
|
||||
}
|
||||
clicon_debug(1, "%s auth2:%d %s", __FUNCTION__, authenticated, clicon_username_get(h));
|
||||
|
|
@ -399,11 +403,13 @@ api_restconf(clicon_handle h,
|
|||
goto done;
|
||||
}
|
||||
else if (strcmp(method, "data") == 0){ /* restconf, skip /api/data */
|
||||
if (api_data(h, r, path, pcvec, 2, qvec, data) < 0)
|
||||
if (api_data(h, r, path, pcvec, 2, qvec, data,
|
||||
pretty, use_xml, parse_xml) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(method, "operations") == 0){ /* rpc */
|
||||
if (api_operations(h, r, path, pcvec, 2, qvec, data) < 0)
|
||||
if (api_operations(h, r, path, pcvec, 2, qvec, data,
|
||||
pretty, use_xml, parse_xml) < 0)
|
||||
goto done;
|
||||
}
|
||||
else if (strcmp(method, "test") == 0)
|
||||
|
|
@ -424,6 +430,10 @@ api_restconf(clicon_handle h,
|
|||
cvec_free(pcvec);
|
||||
if (cb)
|
||||
cbuf_free(cb);
|
||||
if (cbret)
|
||||
cbuf_free(cbret);
|
||||
if (xret)
|
||||
xml_free(xret);
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -557,7 +567,7 @@ main(int argc,
|
|||
|
||||
/* Initialize plugins group */
|
||||
if ((dir = clicon_restconf_dir(h)) != NULL)
|
||||
if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir) < 0)
|
||||
if (clixon_plugins_load(h, CLIXON_PLUGIN_INIT, dir, NULL) < 0)
|
||||
return -1;
|
||||
|
||||
/* Parse yang database spec file */
|
||||
|
|
@ -598,7 +608,6 @@ main(int argc,
|
|||
clicon_debug(1, "top-level %s not found", path);
|
||||
notfound(r);
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
clicon_debug(1, "NULL URI");
|
||||
|
|
|
|||
|
|
@ -185,7 +185,7 @@ api_data_get2(clicon_handle h,
|
|||
cbuf *cbx = NULL;
|
||||
yang_spec *yspec;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
cxobj *xerr = NULL;
|
||||
cxobj **xvec = NULL;
|
||||
size_t xlen;
|
||||
int i;
|
||||
|
|
@ -199,13 +199,19 @@ api_data_get2(clicon_handle h,
|
|||
clicon_debug(1, "%s pi:%d", __FUNCTION__, pi);
|
||||
/* We know "data" is element pi-1 */
|
||||
if (api_path2xpath_cvv(yspec, pcvec, pi, cbpath) < 0){
|
||||
notfound(r);
|
||||
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
path = cbuf_get(cbpath);
|
||||
clicon_debug(1, "%s path:%s", __FUNCTION__, path);
|
||||
if (clicon_rpc_get(h, path, &xret) < 0){
|
||||
notfound(r);
|
||||
if (netconf_operation_failed_xml(&xerr, "protocol", clicon_err_reason) < 0)
|
||||
goto done;
|
||||
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/* We get return via netconf which is complete tree from root
|
||||
|
|
@ -394,10 +400,9 @@ api_data_post(clicon_handle h,
|
|||
yang_node *y = NULL;
|
||||
yang_spec *yspec;
|
||||
cxobj *xa;
|
||||
cxobj *xu;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xretcom = NULL;
|
||||
cxobj *xerr;
|
||||
cxobj *xerr = NULL;
|
||||
char *username;
|
||||
|
||||
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
||||
|
|
@ -414,16 +419,6 @@ api_data_post(clicon_handle h,
|
|||
goto done;
|
||||
/* Translate api_path to xtop/xbot */
|
||||
xbot = xtop;
|
||||
/* For internal XML protocol: add username attribute for backend access control
|
||||
*/
|
||||
if ((username = clicon_username_get(h)) != NULL){
|
||||
if ((xu = xml_new("username", xtop, NULL)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xu, CX_ATTR);
|
||||
if (xml_value_set(xu, username) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
||||
goto done;
|
||||
/* Parse input data as json or xml into xml */
|
||||
|
|
@ -457,7 +452,11 @@ api_data_post(clicon_handle h,
|
|||
/* Create text buffer for transfer to backend */
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cbx, "<rpc><edit-config><target><candidate /></target>");
|
||||
/* For internal XML protocol: add username attribute for access control
|
||||
*/
|
||||
username = clicon_username_get(h);
|
||||
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||
cprintf(cbx, "<edit-config><target><candidate /></target>");
|
||||
cprintf(cbx, "<default-operation>none</default-operation>");
|
||||
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
||||
goto done;
|
||||
|
|
@ -471,7 +470,10 @@ api_data_post(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
/* Assume this is validation failed since commit includes validate */
|
||||
if (clicon_rpc_netconf(h, "<rpc><commit/></rpc>", &xretcom, NULL) < 0)
|
||||
cbuf_reset(cbx);
|
||||
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||
cprintf(cbx, "<commit/></rpc>");
|
||||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xretcom, "//rpc-error")) != NULL){
|
||||
if (clicon_rpc_discard_changes(h) < 0)
|
||||
|
|
@ -600,11 +602,10 @@ api_data_put(clicon_handle h,
|
|||
yang_node *y = NULL;
|
||||
yang_spec *yspec;
|
||||
cxobj *xa;
|
||||
cxobj *xu;
|
||||
char *api_path;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xretcom = NULL;
|
||||
cxobj *xerr;
|
||||
cxobj *xerr = NULL;
|
||||
char *username;
|
||||
|
||||
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
|
||||
|
|
@ -621,15 +622,7 @@ api_data_put(clicon_handle h,
|
|||
goto done;
|
||||
/* Translate api_path to xtop/xbot */
|
||||
xbot = xtop;
|
||||
/* For internal XML protocol: add username attribute for backend access control
|
||||
*/
|
||||
if ((username = clicon_username_get(h)) != NULL){
|
||||
if ((xu = xml_new("username", xtop, NULL)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xu, CX_ATTR);
|
||||
if (xml_value_set(xu, username) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
||||
goto done;
|
||||
/* Parse input data as json or xml into xml */
|
||||
|
|
@ -688,7 +681,11 @@ api_data_put(clicon_handle h,
|
|||
/* Create text buffer for transfer to backend */
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cbx, "<rpc><edit-config><target><candidate /></target>");
|
||||
/* For internal XML protocol: add username attribute for access control
|
||||
*/
|
||||
username = clicon_username_get(h);
|
||||
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||
cprintf(cbx, "<edit-config><target><candidate /></target>");
|
||||
cprintf(cbx, "<default-operation>none</default-operation>");
|
||||
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
||||
goto done;
|
||||
|
|
@ -701,7 +698,10 @@ api_data_put(clicon_handle h,
|
|||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
if (clicon_rpc_netconf(h, "<rpc><commit/></rpc>", &xretcom, NULL) < 0)
|
||||
cbuf_reset(cbx);
|
||||
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||
cprintf(cbx, "<commit/></rpc>");
|
||||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xretcom, "//rpc-error")) != NULL){
|
||||
if (clicon_rpc_discard_changes(h) < 0)
|
||||
|
|
@ -779,14 +779,13 @@ api_data_delete(clicon_handle h,
|
|||
cxobj *xtop = NULL; /* xpath root */
|
||||
cxobj *xbot = NULL;
|
||||
cxobj *xa;
|
||||
cxobj *xu;
|
||||
cbuf *cbx = NULL;
|
||||
yang_node *y = NULL;
|
||||
yang_spec *yspec;
|
||||
enum operation_type op = OP_DELETE;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xretcom = NULL;
|
||||
cxobj *xerr;
|
||||
cxobj *xerr = NULL;
|
||||
char *username;
|
||||
|
||||
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
|
||||
|
|
@ -800,15 +799,7 @@ api_data_delete(clicon_handle h,
|
|||
if ((xtop = xml_new("config", NULL, NULL)) == NULL)
|
||||
goto done;
|
||||
xbot = xtop;
|
||||
/* For internal XML protocol: add username attribute for backend access control
|
||||
*/
|
||||
if ((username = clicon_username_get(h)) != NULL){
|
||||
if ((xu = xml_new("username", xtop, NULL)) == NULL)
|
||||
goto done;
|
||||
xml_type_set(xu, CX_ATTR);
|
||||
if (xml_value_set(xu, username) < 0)
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (api_path && api_path2xml(api_path, yspec, xtop, YC_DATANODE, &xbot, &y) < 0)
|
||||
goto done;
|
||||
if ((xa = xml_new("operation", xbot, NULL)) == NULL)
|
||||
|
|
@ -818,7 +809,11 @@ api_data_delete(clicon_handle h,
|
|||
goto done;
|
||||
if ((cbx = cbuf_new()) == NULL)
|
||||
goto done;
|
||||
cprintf(cbx, "<rpc><edit-config><target><candidate /></target>");
|
||||
/* For internal XML protocol: add username attribute for access control
|
||||
*/
|
||||
username = clicon_username_get(h);
|
||||
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||
cprintf(cbx, "<edit-config><target><candidate /></target>");
|
||||
cprintf(cbx, "<default-operation>none</default-operation>");
|
||||
if (clicon_xml2cbuf(cbx, xtop, 0, 0) < 0)
|
||||
goto done;
|
||||
|
|
@ -831,7 +826,10 @@ api_data_delete(clicon_handle h,
|
|||
goto ok;
|
||||
}
|
||||
/* Assume this is validation failed since commit includes validate */
|
||||
if (clicon_rpc_netconf(h, "<rpc><commit/></rpc>", &xretcom, NULL) < 0)
|
||||
cbuf_reset(cbx);
|
||||
cprintf(cbx, "<rpc username=\"%s\">", username?username:"");
|
||||
cprintf(cbx, "<commit/></rpc>");
|
||||
if (clicon_rpc_netconf(h, cbuf_get(cbx), &xretcom, NULL) < 0)
|
||||
goto done;
|
||||
if ((xerr = xpath_first(xretcom, "//rpc-error")) != NULL){
|
||||
if (clicon_rpc_discard_changes(h) < 0)
|
||||
|
|
@ -984,7 +982,7 @@ api_operations_post(clicon_handle h,
|
|||
yang_stmt *youtput;
|
||||
cxobj *xdata = NULL;
|
||||
cxobj *xret = NULL;
|
||||
cxobj *xerr;
|
||||
cxobj *xerr = NULL;
|
||||
cbuf *cbx = NULL;
|
||||
cxobj *xtop = NULL; /* xpath root */
|
||||
cxobj *xe;
|
||||
|
|
@ -1011,7 +1009,10 @@ api_operations_post(clicon_handle h,
|
|||
if (yang_abs_schema_nodeid(yspec, oppath, &yrpc) < 0)
|
||||
goto done;
|
||||
if (yrpc == NULL){
|
||||
retval = notfound(r);
|
||||
if (netconf_operation_failed_xml(&xerr, "protocol", "yang node not found") < 0)
|
||||
goto done;
|
||||
if (api_return_err(h, r, xerr, pretty, use_xml) < 0)
|
||||
goto done;
|
||||
goto ok;
|
||||
}
|
||||
/* Create an xml message:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue