restconf credentials plugin
This commit is contained in:
parent
e40d785d5c
commit
55010e7541
6 changed files with 141 additions and 48 deletions
|
|
@ -1095,8 +1095,8 @@ from_client(int s,
|
||||||
goto done;
|
goto done;
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
clicon_debug(1, "%s retval=%d", __FUNCTION__, retval);
|
||||||
if (msg)
|
if (msg)
|
||||||
free(msg);
|
free(msg);
|
||||||
clicon_debug(1, "%s retval=%d", __FUNCTION__, retval);
|
|
||||||
return retval; /* -1 here terminates backend */
|
return retval; /* -1 here terminates backend */
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,60 @@ restconf_code2reason(int code)
|
||||||
return clicon_int2str(http_reason_phrase_map, code);
|
return clicon_int2str(http_reason_phrase_map, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*!
|
/*! HTTP error 400
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
badrequest(FCGX_Request *r)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
|
||||||
|
FCGX_FPrintF(r->out, "Status: 400\r\n"); /* 400 bad request */
|
||||||
|
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "<h1>Clixon Bad request/h1>\n");
|
||||||
|
FCGX_FPrintF(r->out, "The requested URL %s or data is in some way badly formed.\n",
|
||||||
|
path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! HTTP error 401
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
unauthorized(FCGX_Request *r)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
|
||||||
|
FCGX_FPrintF(r->out, "Status: 401\r\n"); /* 401 unauthorized */
|
||||||
|
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "<error-tag>access-denied</error-tag>\n");
|
||||||
|
FCGX_FPrintF(r->out, "The requested URL %s was unauthorized.\n", path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! HTTP error 403
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
forbidden(FCGX_Request *r)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
|
||||||
|
FCGX_FPrintF(r->out, "Status: 403\r\n"); /* 403 forbidden */
|
||||||
|
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "<h1>Grideye Forbidden</h1>\n");
|
||||||
|
FCGX_FPrintF(r->out, "The requested URL %s was forbidden.\n", path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! HTTP error 404
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
notfound(FCGX_Request *r)
|
notfound(FCGX_Request *r)
|
||||||
|
|
@ -165,31 +218,9 @@ notfound(FCGX_Request *r)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
/*! HTTP error 409
|
||||||
badrequest(FCGX_Request *r)
|
* @param[in] r Fastcgi request handle
|
||||||
{
|
*/
|
||||||
char *path;
|
|
||||||
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
|
||||||
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
|
|
||||||
FCGX_FPrintF(r->out, "Status: 400\r\n"); /* 400 bad request */
|
|
||||||
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
|
|
||||||
FCGX_FPrintF(r->out, "<h1>Clixon Bad request/h1>\n");
|
|
||||||
FCGX_FPrintF(r->out, "The requested URL %s or data is in some way badly formed.\n",
|
|
||||||
path);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
notimplemented(FCGX_Request *r)
|
|
||||||
{
|
|
||||||
clicon_debug(1, "%s", __FUNCTION__);
|
|
||||||
FCGX_FPrintF(r->out, "Status: 501\r\n");
|
|
||||||
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
|
|
||||||
FCGX_FPrintF(r->out, "<h1>Not Implemented/h1>\n");
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
conflict(FCGX_Request *r)
|
conflict(FCGX_Request *r)
|
||||||
{
|
{
|
||||||
|
|
@ -200,6 +231,35 @@ conflict(FCGX_Request *r)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*! HTTP error 500
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
internal_server_error(FCGX_Request *r)
|
||||||
|
{
|
||||||
|
char *path;
|
||||||
|
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
path = FCGX_GetParam("DOCUMENT_URI", r->envp);
|
||||||
|
FCGX_FPrintF(r->out, "Status: 500\r\n"); /* 500 internal server error */
|
||||||
|
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "<h1>Grideye Internal server error when accessing %s</h1>\n", path);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*! HTTP error 501
|
||||||
|
* @param[in] r Fastcgi request handle
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
notimplemented(FCGX_Request *r)
|
||||||
|
{
|
||||||
|
clicon_debug(1, "%s", __FUNCTION__);
|
||||||
|
FCGX_FPrintF(r->out, "Status: 501\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "Content-Type: text/html\r\n\r\n");
|
||||||
|
FCGX_FPrintF(r->out, "<h1>Not Implemented/h1>\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/*! Specialization of clicon_debug with xml tree */
|
/*! Specialization of clicon_debug with xml tree */
|
||||||
int
|
int
|
||||||
clicon_debug_xml(int dbglevel,
|
clicon_debug_xml(int dbglevel,
|
||||||
|
|
@ -329,7 +389,10 @@ restconf_plugin_load(clicon_handle h)
|
||||||
(int)strlen(filename), filename);
|
(int)strlen(filename), filename);
|
||||||
if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL)
|
if ((handle = plugin_load(h, filename, RTLD_NOW)) == NULL)
|
||||||
goto quit;
|
goto quit;
|
||||||
_credentials_fn = dlsym(handle, PLUGIN_CREDENTIALS);
|
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) {
|
if ((plugins = realloc(plugins, (nplugins+1) * sizeof (*plugins))) == NULL) {
|
||||||
clicon_err(OE_UNIX, errno, "realloc");
|
clicon_err(OE_UNIX, errno, "realloc");
|
||||||
goto quit;
|
goto quit;
|
||||||
|
|
@ -383,6 +446,14 @@ restconf_plugin_start(clicon_handle h,
|
||||||
return 0;
|
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
|
int
|
||||||
restconf_credentials(clicon_handle h,
|
restconf_credentials(clicon_handle h,
|
||||||
FCGX_Request *r,
|
FCGX_Request *r,
|
||||||
|
|
@ -397,13 +468,14 @@ restconf_credentials(clicon_handle h,
|
||||||
clicon_err(OE_XML, errno, "strdup");
|
clicon_err(OE_XML, errno, "strdup");
|
||||||
goto done;
|
goto done;
|
||||||
}
|
}
|
||||||
retval = 0;
|
goto ok;
|
||||||
goto done;
|
|
||||||
}
|
}
|
||||||
if (_credentials_fn(h, r, user) < 0)
|
if (_credentials_fn(h, r, user) < 0)
|
||||||
user = NULL;
|
*user = NULL;
|
||||||
|
ok:
|
||||||
retval = 0;
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
|
clicon_debug(1, "%s retval:%d user:%s", __FUNCTION__, retval, *user);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,10 +45,15 @@
|
||||||
*/
|
*/
|
||||||
int restconf_err2code(char *tag);
|
int restconf_err2code(char *tag);
|
||||||
const char *restconf_code2reason(int code);
|
const char *restconf_code2reason(int code);
|
||||||
int notfound(FCGX_Request *r);
|
|
||||||
int badrequest(FCGX_Request *r);
|
int badrequest(FCGX_Request *r);
|
||||||
int notimplemented(FCGX_Request *r);
|
int unauthorized(FCGX_Request *r);
|
||||||
|
int forbidden(FCGX_Request *r);
|
||||||
|
int notfound(FCGX_Request *r);
|
||||||
int conflict(FCGX_Request *r);
|
int conflict(FCGX_Request *r);
|
||||||
|
int internal_server_error(FCGX_Request *r);
|
||||||
|
int notimplemented(FCGX_Request *r);
|
||||||
|
|
||||||
int clicon_debug_xml(int dbglevel, char *str, cxobj *cx);
|
int clicon_debug_xml(int dbglevel, char *str, cxobj *cx);
|
||||||
int test(FCGX_Request *r, int dbg);
|
int test(FCGX_Request *r, int dbg);
|
||||||
cbuf *readdata(FCGX_Request *r);
|
cbuf *readdata(FCGX_Request *r);
|
||||||
|
|
|
||||||
|
|
@ -317,27 +317,37 @@ api_restconf(clicon_handle h,
|
||||||
if (str2cvec(data, '&', '=', &dvec) < 0)
|
if (str2cvec(data, '&', '=', &dvec) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
|
||||||
retval = 0;
|
|
||||||
test(r, 1);
|
test(r, 1);
|
||||||
/* If present, check credentials. See "plugin_credentials" in plugin
|
/* If present, check credentials. See "plugin_credentials" in plugin
|
||||||
* See RFC 8040 section 2.5
|
* See RFC 8040 section 2.5
|
||||||
*/
|
*/
|
||||||
if (restconf_credentials(h, r, &username) < 0)
|
if (restconf_credentials(h, r, &username) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
clicon_debug(1, "%s username:%s", __FUNCTION__, username);
|
||||||
clicon_debug(1, "%s credentials ok username:%s (should be non-NULL)",
|
clicon_debug(1, "%s credentials ok username:%s (should be non-NULL)",
|
||||||
__FUNCTION__, username);
|
__FUNCTION__, username);
|
||||||
if (username == NULL)
|
if (username == NULL){
|
||||||
goto done;
|
unauthorized(r);
|
||||||
if (strcmp(method, "yang-library-version")==0)
|
goto ok;
|
||||||
retval = api_yang_library_version(h, r);
|
}
|
||||||
else if (strcmp(method, "data") == 0) /* restconf, skip /api/data */
|
if (strcmp(method, "yang-library-version")==0){
|
||||||
retval = api_data(h, r, path, pcvec, 2, qvec, data);
|
if (api_yang_library_version(h, r) < 0)
|
||||||
else if (strcmp(method, "operations") == 0) /* rpc */
|
goto done;
|
||||||
retval = api_operations(h, r, path, pcvec, 2, qvec, data, username);
|
}
|
||||||
|
else if (strcmp(method, "data") == 0){ /* restconf, skip /api/data */
|
||||||
|
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)
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
else if (strcmp(method, "test") == 0)
|
else if (strcmp(method, "test") == 0)
|
||||||
retval = test(r, 0);
|
test(r, 0);
|
||||||
else
|
else
|
||||||
retval = notfound(r);
|
notfound(r);
|
||||||
|
ok:
|
||||||
|
retval = 0;
|
||||||
done:
|
done:
|
||||||
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
clicon_debug(1, "%s retval:%d", __FUNCTION__, retval);
|
||||||
if (pvec)
|
if (pvec)
|
||||||
|
|
|
||||||
|
|
@ -906,8 +906,17 @@ api_operation_post(clicon_handle h,
|
||||||
if ((xtop = xml_new("rpc", NULL, NULL)) == NULL)
|
if ((xtop = xml_new("rpc", NULL, NULL)) == NULL)
|
||||||
goto done;
|
goto done;
|
||||||
xbot = xtop;
|
xbot = xtop;
|
||||||
|
/* XXX: something strange for rpc user */
|
||||||
if (api_path2xml(oppath, yspec, xtop, 1, &xbot, &y) < 0)
|
if (api_path2xml(oppath, yspec, xtop, 1, &xbot, &y) < 0)
|
||||||
goto done;
|
goto done;
|
||||||
|
#if 1
|
||||||
|
{
|
||||||
|
cbuf *c = cbuf_new();
|
||||||
|
clicon_xml2cbuf(c, xtop, 0, 0);
|
||||||
|
clicon_debug(1, "%s xinput:%s", __FUNCTION__, cbuf_get(c));
|
||||||
|
cbuf_free(c);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (data && strlen(data)){
|
if (data && strlen(data)){
|
||||||
/* Parse input data as json or xml into xml */
|
/* Parse input data as json or xml into xml */
|
||||||
if (parse_xml){
|
if (parse_xml){
|
||||||
|
|
|
||||||
|
|
@ -1500,11 +1500,9 @@ api_path2xml_vec(char **vec,
|
||||||
name = local;
|
name = local;
|
||||||
}
|
}
|
||||||
if (y0->yn_keyword == Y_SPEC){ /* top-node */
|
if (y0->yn_keyword == Y_SPEC){ /* top-node */
|
||||||
clicon_debug(1, "%s 1 %s", __FUNCTION__, name);
|
|
||||||
y = yang_find_topnode((yang_spec*)y0, name, schemanode);
|
y = yang_find_topnode((yang_spec*)y0, name, schemanode);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
clicon_debug(1, "%s 2 %s", __FUNCTION__, name);
|
|
||||||
y = schemanode?yang_find_schemanode((yang_node*)y0, name):
|
y = schemanode?yang_find_schemanode((yang_node*)y0, name):
|
||||||
yang_find_datanode((yang_node*)y0, name);
|
yang_find_datanode((yang_node*)y0, name);
|
||||||
}
|
}
|
||||||
|
|
@ -1593,7 +1591,6 @@ api_path2xml_vec(char **vec,
|
||||||
* @param[in] schemanode If set use schema nodes otherwise data nodes.
|
* @param[in] schemanode If set use schema nodes otherwise data nodes.
|
||||||
* @param[out] xbotp Resulting xml tree (end of xpath)
|
* @param[out] xbotp Resulting xml tree (end of xpath)
|
||||||
* @param[out] ybotp Yang spec matching xbotp
|
* @param[out] ybotp Yang spec matching xbotp
|
||||||
* @see api_path2xml_vec
|
|
||||||
* @example
|
* @example
|
||||||
* api_path: /subif-entry=foo/subid
|
* api_path: /subif-entry=foo/subid
|
||||||
* xtop[in] <config/>
|
* xtop[in] <config/>
|
||||||
|
|
@ -1602,6 +1599,7 @@ api_path2xml_vec(char **vec,
|
||||||
* </subif-entry></config>
|
* </subif-entry></config>
|
||||||
* xbotp: <subid/>
|
* xbotp: <subid/>
|
||||||
* ybotp: Y_LEAF subid
|
* ybotp: Y_LEAF subid
|
||||||
|
* @see api_path2xml_vec
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
api_path2xml(char *api_path,
|
api_path2xml(char *api_path,
|
||||||
|
|
@ -1615,7 +1613,6 @@ api_path2xml(char *api_path,
|
||||||
char **vec = NULL;
|
char **vec = NULL;
|
||||||
int nvec;
|
int nvec;
|
||||||
|
|
||||||
clicon_debug(1, "%s 0", __FUNCTION__);
|
|
||||||
if (*api_path!='/'){
|
if (*api_path!='/'){
|
||||||
clicon_err(OE_DB, 0, "Invalid key: %s", api_path);
|
clicon_err(OE_DB, 0, "Invalid key: %s", api_path);
|
||||||
goto done;
|
goto done;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue