* Restructure and more generic plugin API (cli,backend,restconf,netconf)

* For preparation for authorization RFC8341
  * Plugins add clixon_plugin_init() and api struct for function pointers, eg:
```
static const struct clixon_plugin_api api = {
    "example",
    clixon_plugin_init,
    ...
}
clixon_plugin_api *clixon_plugin_init(clicon_handle h)
{
    return (void*)&api;
}
```
  * Moved specific plugin functions from apps/ to generic functions in lib/
    * New generic plugin load function: clixon_plugins_load()
  * Removed client-local netconf plugins netconf_plugin_callbacks()
    * This was code used before generic YANG rpc calls
  * Added username to clixon handle:
    * clicon_username_get() / clicon_username_set()
  * Added authentication plugin callback
  * Removed some obscure plugin code that seem not to be used (please report if needed!)
    * CLI parse hook
    * CLICON_FIND_PLUGIN
    * clicon_valcb()
* Removed username to rpc calls (added below)
This commit is contained in:
Olof hagsand 2018-04-02 10:38:53 +02:00
parent b8e35742b9
commit 79e3fbdaa9
41 changed files with 470 additions and 772 deletions

View file

@ -62,7 +62,7 @@ LIBDEPS = $(top_srcdir)/lib/src/$(CLIXON_LIB)
LIBS = -L$(top_srcdir)/lib/src @LIBS@ -l:$(CLIXON_LIB)
CPPFLAGS = @CPPFLAGS@
CPPFLAGS = @CPPFLAGS@ -fPIC
INCLUDES = -I. -I$(top_srcdir)/lib/src -I$(top_srcdir)/lib -I$(top_srcdir)/include -I$(top_srcdir) @INCLUDES@

View file

@ -60,11 +60,6 @@ int notimplemented(FCGX_Request *r);
int clicon_debug_xml(int dbglevel, char *str, cxobj *cx);
int test(FCGX_Request *r, int dbg);
cbuf *readdata(FCGX_Request *r);
int restconf_plugin_load(clicon_handle h);
int restconf_plugin_start(clicon_handle h, int argc, char **argv);
int restconf_plugin_unload(clicon_handle h);
int restconf_credentials(clicon_handle h, FCGX_Request *r, char **user);
int get_user_cookie(char *cookiestr, char *attribute, char **val);

View file

@ -358,129 +358,6 @@ readdata(FCGX_Request *r)
return cb;
}
static int nplugins = 0;
static plghndl_t *plugins = NULL;
static plgcredentials_t *_credentials_fn = NULL; /* Credentials callback */
/*! Load all plugins you can find in CLICON_RESTCONF_DIR
*/
int
restconf_plugin_load(clicon_handle h)
{
int retval = -1;
char *dir;
int ndp;
struct dirent *dp = NULL;
int i;
plghndl_t *handle;
char filename[MAXPATHLEN];
clicon_debug(1, "%s", __FUNCTION__);
if ((dir = clicon_restconf_dir(h)) == NULL){
retval = 0;
goto quit;
}
/* Get plugin objects names from plugin directory */
if((ndp = clicon_file_dirent(dir, &dp, "(.so)$", S_IFREG))<0)
goto quit;
/* Load all plugins */
for (i = 0; i < ndp; i++) {
snprintf(filename, MAXPATHLEN-1, "%s/%s", dir, dp[i].d_name);
clicon_debug(1, "DEBUG: Loading plugin '%.*s' ...",
(int)strlen(filename), filename);
if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL)
goto quit;
if ((_credentials_fn = dlsym(handle, PLUGIN_CREDENTIALS)) == NULL)
clicon_debug(1, "Failed to load %s", PLUGIN_CREDENTIALS);
else
clicon_debug(1, "%s callback loaded", PLUGIN_CREDENTIALS);
if ((plugins = realloc(plugins, (nplugins+1) * sizeof (*plugins))) == NULL) {
clicon_err(OE_UNIX, errno, "realloc");
goto quit;
}
plugins[nplugins++] = handle;
}
retval = 0;
quit:
if (dp)
free(dp);
return retval;
}
/*! Unload all restconf plugins */
int
restconf_plugin_unload(clicon_handle h)
{
int i;
for (i = 0; i < nplugins; i++)
plugin_unload(h, plugins[i]);
if (plugins){
free(plugins);
plugins = NULL;
}
nplugins = 0;
return 0;
}
/*! Call plugin_start in all plugins
*/
int
restconf_plugin_start(clicon_handle h,
int argc,
char **argv)
{
int i;
plgstart_t *startfn;
for (i = 0; i < nplugins; i++) {
/* Call exit function is it exists */
if ((startfn = dlsym(plugins[i], PLUGIN_START)) == NULL)
break;
optind = 0;
if (startfn(h, argc, argv) < 0) {
clicon_debug(1, "plugin_start() failed\n");
return -1;
}
}
return 0;
}
/*! Run the restconf user-defined credentials callback if present
* The callback is expected to return the authenticated user, or NULL if not
* authenticasted.
* If no callback exists, return user "none"
* @param[in] h Clicon handle
* @param[in] r Fastcgi request handle
* @param[out] user The authenticated user (or NULL). Malloced, must be freed.
*/
int
restconf_credentials(clicon_handle h,
FCGX_Request *r,
char **user)
{
int retval = -1;
clicon_debug(1, "%s", __FUNCTION__);
/* If no authentication callback then allow anything. Is this OK? */
if (_credentials_fn == NULL){
if ((*user = strdup("none")) == NULL){
clicon_err(OE_XML, errno, "strdup");
goto done;
}
goto ok;
}
if (_credentials_fn(h, r, user) < 0)
*user = NULL;
ok:
retval = 0;
done:
clicon_debug(1, "%s retval:%d user:%s", __FUNCTION__, retval, *user);
return retval;
}
/*! Parse a cookie string and return value of cookie attribute
* @param[in] cookiestr cookie string according to rfc6265 (modified)
* @param[in] attribute cookie attribute

View file

@ -57,11 +57,6 @@ int notimplemented(FCGX_Request *r);
int clicon_debug_xml(int dbglevel, char *str, cxobj *cx);
int test(FCGX_Request *r, int dbg);
cbuf *readdata(FCGX_Request *r);
int restconf_plugin_load(clicon_handle h);
int restconf_plugin_start(clicon_handle h, int argc, char **argv);
int restconf_plugin_unload(clicon_handle h);
int restconf_credentials(clicon_handle h, FCGX_Request *r, char **user);
int get_user_cookie(char *cookiestr, char *attribute, char **val);

View file

@ -92,7 +92,6 @@
* @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] username Authenticated user
*/
static int
api_data(clicon_handle h,
@ -101,8 +100,7 @@ api_data(clicon_handle h,
cvec *pcvec,
int pi,
cvec *qvec,
char *data,
char *username)
char *data)
{
int retval = -1;
char *request_method;
@ -127,17 +125,17 @@ api_data(clicon_handle h,
if (strcmp(request_method, "OPTIONS")==0)
retval = api_data_options(h, r);
else if (strcmp(request_method, "HEAD")==0)
retval = api_data_head(h, r, pcvec, pi, qvec, username, pretty, use_xml);
retval = api_data_head(h, r, pcvec, pi, qvec, pretty, use_xml);
else if (strcmp(request_method, "GET")==0)
retval = api_data_get(h, r, pcvec, pi, qvec, username, pretty, use_xml);
retval = api_data_get(h, r, pcvec, pi, qvec, pretty, use_xml);
else if (strcmp(request_method, "POST")==0)
retval = api_data_post(h, r, api_path, pcvec, pi, qvec, data, username, pretty, use_xml, parse_xml);
retval = api_data_post(h, r, api_path, pcvec, pi, qvec, data, pretty, use_xml, parse_xml);
else if (strcmp(request_method, "PUT")==0)
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, username, pretty, use_xml, parse_xml);
retval = api_data_put(h, r, api_path, pcvec, pi, qvec, data, pretty, use_xml, parse_xml);
else if (strcmp(request_method, "PATCH")==0)
retval = api_data_patch(h, r, api_path, pcvec, pi, qvec, data, username);
retval = api_data_patch(h, r, api_path, pcvec, pi, qvec, data);
else if (strcmp(request_method, "DELETE")==0)
retval = api_data_delete(h, r, api_path, pi, username, pretty, use_xml);
retval = api_data_delete(h, r, api_path, pi, pretty, use_xml);
else
retval = notfound(r);
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
@ -152,7 +150,6 @@ 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] username Authenticated user
*/
static int
api_operations(clicon_handle h,
@ -161,8 +158,7 @@ api_operations(clicon_handle h,
cvec *pcvec,
int pi,
cvec *qvec,
char *data,
char *username)
char *data)
{
int retval = -1;
char *request_method;
@ -185,9 +181,9 @@ api_operations(clicon_handle h,
parse_xml++;
if (strcmp(request_method, "GET")==0)
retval = api_operations_get(h, r, path, pcvec, pi, qvec, data, username, pretty, use_xml);
retval = api_operations_get(h, r, path, pcvec, pi, qvec, data, pretty, use_xml);
else if (strcmp(request_method, "POST")==0)
retval = api_operations_post(h, r, path, pcvec, pi, qvec, data, username,
retval = api_operations_post(h, r, path, pcvec, pi, qvec, data,
pretty, use_xml, parse_xml);
else
retval = notfound(r);
@ -338,7 +334,7 @@ api_restconf(clicon_handle h,
cvec *pcvec = NULL; /* for rest api */
cbuf *cb = NULL;
char *data;
char *username = NULL;
int authenticated = 0;
clicon_debug(1, "%s", __FUNCTION__);
path = FCGX_GetParam("REQUEST_URI", r->envp);
@ -384,12 +380,14 @@ api_restconf(clicon_handle h,
/* If present, check credentials. See "plugin_credentials" in plugin
* See RFC 8040 section 2.5
*/
if (restconf_credentials(h, r, &username) < 0)
if ((authenticated = clixon_plugin_auth(h, r)) < 0)
goto done;
clicon_debug(1, "%s username:%s", __FUNCTION__, username);
clicon_debug(1, "%s credentials ok username:%s (should be non-NULL)",
__FUNCTION__, username);
if (username == NULL){
/* If set but no user, we set a dummy user */
if (authenticated){
if (clicon_username_get(h) == NULL)
clicon_username_set(h, "none");
}
else{
unauthorized(r);
goto ok;
}
@ -398,11 +396,11 @@ 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, username) < 0)
if (api_data(h, r, path, pcvec, 2, qvec, data) < 0)
goto done;
}
else if (strcmp(method, "operations") == 0){ /* rpc */
if (api_operations(h, r, path, pcvec, 2, qvec, data, username) < 0)
if (api_operations(h, r, path, pcvec, 2, qvec, data) < 0)
goto done;
}
else if (strcmp(method, "test") == 0)
@ -423,8 +421,6 @@ api_restconf(clicon_handle h,
cvec_free(pcvec);
if (cb)
cbuf_free(cb);
if (username)
free(username);
return retval;
}
@ -500,6 +496,7 @@ main(int argc,
char *path;
clicon_handle h;
char *yangspec=NULL;
char *dir;
/* In the startup, logs to stderr & debug flag set later */
clicon_log_init(__PROGRAM__, LOG_INFO, CLICON_LOG_SYSLOG);
@ -556,8 +553,9 @@ main(int argc,
clicon_option_str_set(h, "CLICON_YANG_MODULE_MAIN", yangspec);
/* Initialize plugins group */
if (restconf_plugin_load(h) < 0)
return -1;
if ((dir = clicon_restconf_dir(h)) != NULL)
if (clixon_plugins_load(h, clicon_restconf_dir(h)) < 0)
return -1;
/* Parse yang database spec file */
if (yang_spec_main(h) == NULL)
@ -605,7 +603,7 @@ main(int argc,
}
retval = 0;
done:
restconf_plugin_unload(h);
clixon_plugin_unload(h);
restconf_terminate(h);
return retval;
}

View file

@ -227,7 +227,6 @@ api_return_err(clicon_handle h,
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
* @param[in] pi Offset, where path starts
* @param[in] qvec Vector of query string (QUERY_STRING)
* @param[in] username Authenticated user
* @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] head If 1 is HEAD, otherwise GET
@ -254,7 +253,6 @@ api_data_get2(clicon_handle h,
cvec *pcvec,
int pi,
cvec *qvec,
char *username,
int pretty,
int use_xml,
int head)
@ -284,7 +282,7 @@ api_data_get2(clicon_handle h,
}
path = cbuf_get(cbpath);
clicon_debug(1, "%s path:%s", __FUNCTION__, path);
if (clicon_rpc_get(h, path, username, &xret) < 0){
if (clicon_rpc_get(h, path, &xret) < 0){
notfound(r);
goto ok;
}
@ -362,7 +360,6 @@ api_data_get2(clicon_handle h,
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
* @param[in] pi Offset, where path starts
* @param[in] qvec Vector of query string (QUERY_STRING)
* @param[in] username Authenticated user
* @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
*
@ -377,11 +374,10 @@ api_data_head(clicon_handle h,
cvec *pcvec,
int pi,
cvec *qvec,
char *username,
int pretty,
int use_xml)
{
return api_data_get2(h, r, pcvec, pi, qvec, username, pretty, use_xml, 1);
return api_data_get2(h, r, pcvec, pi, qvec, pretty, use_xml, 1);
}
/*! REST GET method
@ -391,7 +387,6 @@ api_data_head(clicon_handle h,
* @param[in] pcvec Vector of path ie DOCUMENT_URI element
* @param[in] pi Offset, where path starts
* @param[in] qvec Vector of query string (QUERY_STRING)
* @param[in] username Authenticated user
* @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
* @code
@ -416,11 +411,10 @@ api_data_get(clicon_handle h,
cvec *pcvec,
int pi,
cvec *qvec,
char *username,
int pretty,
int use_xml)
{
return api_data_get2(h, r, pcvec, pi, qvec, username, pretty, use_xml, 0);
return api_data_get2(h, r, pcvec, pi, qvec, pretty, use_xml, 0);
}
/*! Generic REST POST method
@ -431,7 +425,6 @@ api_data_get(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] username Authenticated user
* @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 for output data
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
@ -464,7 +457,6 @@ api_data_post(clicon_handle h,
int pi,
cvec *qvec,
char *data,
char *username,
int pretty,
int use_xml,
int parse_xml)
@ -484,6 +476,7 @@ api_data_post(clicon_handle h,
cxobj *xret = NULL;
cxobj *xretcom = NULL;
cxobj *xerr;
char *username;
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
__FUNCTION__,
@ -501,7 +494,7 @@ api_data_post(clicon_handle h,
xbot = xtop;
/* For internal XML protocol: add username attribute for backend access control
*/
if (username){
if ((username = clicon_username_get(h)) != NULL){
if ((xu = xml_new("username", xtop, NULL)) == NULL)
goto done;
xml_type_set(xu, CX_ATTR);
@ -645,7 +638,6 @@ match_list_keys(yang_stmt *y,
* @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] username Authenticated user
* @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 for output data
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
@ -670,7 +662,6 @@ api_data_put(clicon_handle h,
int pi,
cvec *qvec,
char *data,
char *username,
int pretty,
int use_xml,
int parse_xml)
@ -692,6 +683,7 @@ api_data_put(clicon_handle h,
cxobj *xret = NULL;
cxobj *xretcom = NULL;
cxobj *xerr;
char *username;
clicon_debug(1, "%s api_path:\"%s\" json:\"%s\"",
__FUNCTION__, api_path0, data);
@ -709,7 +701,7 @@ api_data_put(clicon_handle h,
xbot = xtop;
/* For internal XML protocol: add username attribute for backend access control
*/
if (username){
if ((username = clicon_username_get(h)) != NULL){
if ((xu = xml_new("username", xtop, NULL)) == NULL)
goto done;
xml_type_set(xu, CX_ATTR);
@ -824,7 +816,6 @@ api_data_put(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] username Authenticated user
* Netconf: <edit-config> (nc:operation="merge")
* See RFC8040 Sec 4.6
*/
@ -835,8 +826,7 @@ api_data_patch(clicon_handle h,
cvec *pcvec,
int pi,
cvec *qvec,
char *data,
char *username)
char *data)
{
notimplemented(r);
return 0;
@ -847,7 +837,6 @@ api_data_patch(clicon_handle h,
* @param[in] r Fastcgi request handle
* @param[in] api_path According to restconf (Sec 3.5.3.1 in rfc8040)
* @param[in] pi Offset, where path starts
* @param[in] username Authenticated user
* @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
* See RFC 8040 Sec 4.7
@ -860,7 +849,6 @@ api_data_delete(clicon_handle h,
FCGX_Request *r,
char *api_path,
int pi,
char *username,
int pretty,
int use_xml)
{
@ -877,6 +865,7 @@ api_data_delete(clicon_handle h,
cxobj *xret = NULL;
cxobj *xretcom = NULL;
cxobj *xerr;
char *username;
clicon_debug(1, "%s api_path:%s", __FUNCTION__, api_path);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
@ -891,7 +880,7 @@ api_data_delete(clicon_handle h,
xbot = xtop;
/* For internal XML protocol: add username attribute for backend access control
*/
if (username){
if ((username = clicon_username_get(h)) != NULL){
if ((xu = xml_new("username", xtop, NULL)) == NULL)
goto done;
xml_type_set(xu, CX_ATTR);
@ -955,7 +944,6 @@ api_data_delete(clicon_handle h,
* @param[in] pi Offset, where path starts
* @param[in] qvec Vector of query string (QUERY_STRING)
* @param[in] data Stream input data
* @param[in] username Authenticated user
* @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
*
@ -976,7 +964,6 @@ api_operations_get(clicon_handle h,
int pi,
cvec *qvec,
char *data,
char *username,
int pretty,
int use_xml)
{
@ -1047,7 +1034,6 @@ api_operations_get(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] username Authenticated user
* @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 for output data
* @param[in] parse_xml Set to 0 for JSON and 1 for XML for input data
@ -1063,7 +1049,6 @@ api_operations_post(clicon_handle h,
int pi,
cvec *qvec,
char *data,
char *username,
int pretty,
int use_xml,
int parse_xml)
@ -1085,7 +1070,8 @@ api_operations_post(clicon_handle h,
cxobj *xoutput;
cxobj *x;
cxobj *xa;
char *username;
clicon_debug(1, "%s json:\"%s\" path:\"%s\"", __FUNCTION__, data, path);
if ((yspec = clicon_dbspec_yang(h)) == NULL){
clicon_err(OE_FATAL, 0, "No DB_SPEC");
@ -1112,7 +1098,7 @@ api_operations_post(clicon_handle h,
xbot = xtop;
/* For internal XML protocol: add username attribute for backend access control
*/
if (username){
if ((username = clicon_username_get(h)) != NULL){
if ((xa = xml_new("username", xtop, NULL)) == NULL)
goto done;
xml_type_set(xa, CX_ATTR);

View file

@ -46,31 +46,31 @@
*/
int api_data_options(clicon_handle h, FCGX_Request *r);
int api_data_head(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi,
cvec *qvec, char *username, int pretty, int use_xml);
cvec *qvec, int pretty, int use_xml);
int api_data_get(clicon_handle h, FCGX_Request *r, cvec *pcvec, int pi,
cvec *qvec, char *username, int pretty, int use_xml);
cvec *qvec, int pretty, int use_xml);
int api_data_post(clicon_handle h, FCGX_Request *r, char *api_path,
cvec *pcvec, int pi,
cvec *qvec, char *data, char *username,
cvec *qvec, char *data,
int pretty, int use_xml, int parse_xml);
int api_data_put(clicon_handle h, FCGX_Request *r, char *api_path,
cvec *pcvec, int pi,
cvec *qvec, char *data, char *username,
cvec *qvec, char *data,
int pretty, int use_xml, int parse_xml);
int api_data_patch(clicon_handle h, FCGX_Request *r, char *api_path,
cvec *pcvec, int pi,
cvec *qvec, char *data, char *username);
cvec *qvec, char *data);
int api_data_delete(clicon_handle h, FCGX_Request *r, char *api_path, int pi,
char *username, int pretty, int use_xml);
int pretty, int use_xml);
int api_operations_get(clicon_handle h, FCGX_Request *r,
char *path,
cvec *pcvec, int pi, cvec *qvec, char *data, char *username,
cvec *pcvec, int pi, cvec *qvec, char *data,
int pretty, int use_xml);
int api_operations_post(clicon_handle h, FCGX_Request *r,
char *path,
cvec *pcvec, int pi, cvec *qvec, char *data,
char *username, int pretty, int use_xml, int parse_xml);
int pretty, int use_xml, int parse_xml);
#endif /* _RESTCONF_METHODS_H_ */