changeset 50:b474f0985e39

scictl: add API key support
author David Demelier <markand@malikania.fr>
date Wed, 17 Aug 2022 09:11:58 +0200
parents 9d8df0c1db63
children 054cc00e23d2
files libsci/apic.c libsci/apic.h man/scictl.8 scictl/scictl.c scid/http.c scid/pageutil.c
diffstat 6 files changed, 65 insertions(+), 33 deletions(-) [+]
line wrap: on
line diff
--- a/libsci/apic.c	Wed Aug 17 09:11:44 2022 +0200
+++ b/libsci/apic.c	Wed Aug 17 09:11:58 2022 +0200
@@ -31,6 +31,7 @@
 	CURL *curl;
 	CURLcode code;
 	struct curl_slist *headers;
+	char keyhdr[128];
 };
 
 struct apiconf apiconf = {
@@ -77,30 +78,30 @@
 	return fp;
 }
 
-static struct curlpack
-create_curl(FILE *fp, const char *body, const char *url)
+static void
+create_curl(struct curlpack *pack, FILE *fp, const char *body, const char *url)
 {
-	struct curlpack pack = {0};
+	/* Create API key string. */
+	snprintf(pack->keyhdr, sizeof (pack->keyhdr), "X-Api-Key: %s", apiconf.key);
 
-	pack.headers = curl_slist_append(pack.headers, "Content-Type: application/json");
-	pack.curl = curl_easy_init();
-	curl_easy_setopt(pack.curl, CURLOPT_HTTPHEADER, pack.headers);
-	curl_easy_setopt(pack.curl, CURLOPT_URL, url);
-	curl_easy_setopt(pack.curl, CURLOPT_FOLLOWLOCATION, 1L);
-	curl_easy_setopt(pack.curl, CURLOPT_TIMEOUT, 3L);
-	curl_easy_setopt(pack.curl, CURLOPT_WRITEFUNCTION, writer);
-	curl_easy_setopt(pack.curl, CURLOPT_WRITEDATA, fp);
-	curl_easy_setopt(pack.curl, CURLOPT_NOSIGNAL, 1L);
+	pack->headers = curl_slist_append(pack->headers, "Content-Type: application/json");
+	pack->headers = curl_slist_append(pack->headers, pack->keyhdr);
+	pack->curl = curl_easy_init();
+	curl_easy_setopt(pack->curl, CURLOPT_HTTPHEADER, pack->headers);
+	curl_easy_setopt(pack->curl, CURLOPT_URL, url);
+	curl_easy_setopt(pack->curl, CURLOPT_FOLLOWLOCATION, 1L);
+	curl_easy_setopt(pack->curl, CURLOPT_TIMEOUT, 3L);
+	curl_easy_setopt(pack->curl, CURLOPT_WRITEFUNCTION, writer);
+	curl_easy_setopt(pack->curl, CURLOPT_WRITEDATA, fp);
+	curl_easy_setopt(pack->curl, CURLOPT_NOSIGNAL, 1L);
 
 	/* Assume POST request if there is a body. */
 	if (body) {
-		curl_easy_setopt(pack.curl, CURLOPT_POSTFIELDS, body);
-		curl_easy_setopt(pack.curl, CURLOPT_POSTFIELDSIZE, strlen(body));
+		curl_easy_setopt(pack->curl, CURLOPT_POSTFIELDS, body);
+		curl_easy_setopt(pack->curl, CURLOPT_POSTFIELDSIZE, strlen(body));
 	}
 
-	pack.code = curl_easy_perform(pack.curl);
-
-	return pack;
+	pack->code = curl_easy_perform(pack->curl);
 }
 
 static json_t *
@@ -111,13 +112,13 @@
 	size_t responsesz;
 	json_t *doc = NULL;
 	json_error_t error;
-	struct curlpack curl;
+	struct curlpack curl = {0};
 
 	memset(req, 0, sizeof (*req));
 
 	url  = create_url(fmt, ap);
 	fp   = create_file(&response, &responsesz);
-	curl = create_curl(fp, body, url);
+	create_curl(&curl, fp, body, url);
 
 	/* Perform that request now. */
 	fclose(fp);
@@ -151,9 +152,6 @@
 	ret = perform(req, NULL, fmt, ap);
 	va_end(ap);
 
-	if (!ret || (!json_is_object(ret) && !json_is_array(ret)))
-		snprintf(req->error, sizeof (req->error), "invalid JSON document received");
-
 	return ret;
 }
 
@@ -177,7 +175,7 @@
 	free(body);
 	json_decref(ret);
 
-	return 0;
+	return req->error[0] ? -1 : 0;
 }
 
 json_t *
--- a/libsci/apic.h	Wed Aug 17 09:11:44 2022 +0200
+++ b/libsci/apic.h	Wed Aug 17 09:11:58 2022 +0200
@@ -37,6 +37,10 @@
  * \brief Maximum URL length.
  */
 #define APIC_URL_MAX 512
+/**
+ * \brief Maximum URL length.
+ */
+#define APIC_KEY_MAX (40 + 1)
 
 /**
  * \brief Request context.
@@ -54,6 +58,7 @@
  */
 extern struct apiconf {
 	char baseurl[APIC_URL_MAX];     /*!< Base API URL for requets. */
+	char key[APIC_KEY_MAX];         /*!< Maximum length for API key. */
 } apiconf /*! Global variable. */;
 
 /**
--- a/man/scictl.8	Wed Aug 17 09:11:44 2022 +0200
+++ b/man/scictl.8	Wed Aug 17 09:11:58 2022 +0200
@@ -144,6 +144,21 @@
 List all workers present on the system. It does not indicate if those workers
 are actually running.
 .El
+.\" ENVIRONMENT
+.Sh ENVIRONMENT
+The following environment variables affects
+.Nm :
+.Bl -tag
+.It Ev SCI_API_URL
+Points to a HTTP URL where
+.Nm scid
+is running. Must start with a HTTP scheme such as
+.Dq http
+or
+.Dq https .
+.It Ev SCI_API_KEY
+Secret API key to perform requests.
+.El
 .\" SEE ALSO
 .Sh SEE ALSO
 .Xr sci 7 ,
--- a/scictl/scictl.c	Wed Aug 17 09:11:44 2022 +0200
+++ b/scictl/scictl.c	Wed Aug 17 09:11:58 2022 +0200
@@ -322,12 +322,22 @@
 main(int argc, char **argv)
 {
 	int ch;
+	const char *env;
 
 	opterr = 0;
 	setenv("POSIXLY_CORRECT", "1", 1);
 
-	while ((ch = getopt(argc, argv, "u:")) != -1) {
+	/* Environment first, options after. */
+	if ((env = getenv("SCI_API_URL")))
+		util_strlcpy(apiconf.baseurl, env, sizeof (apiconf.baseurl));
+	if ((env = getenv("SCI_API_KEY")))
+		util_strlcpy(apiconf.key, env, sizeof (apiconf.key));
+
+	while ((ch = getopt(argc, argv, "k:u:")) != -1) {
 		switch (ch) {
+		case 'k':
+			util_strlcpy(apiconf.key, optarg, sizeof (apiconf.key));
+			break;
 		case 'u':
 			util_strlcpy(apiconf.baseurl, optarg, sizeof (apiconf.baseurl));
 			break;
@@ -352,6 +362,10 @@
 	if (strcmp(argv[0], "help") == 0)
 		help();
 
+	/* At this step, every command requires an API key. */
+	if (strlen(apiconf.key) == 0)
+		util_die("abort: no API key defined\n");
+
 	for (size_t i = 0; commands[i].name; ++i) {
 		if (strcmp(commands[i].name, argv[0]) == 0) {
 			commands[i].exec(argc, argv);
--- a/scid/http.c	Wed Aug 17 09:11:44 2022 +0200
+++ b/scid/http.c	Wed Aug 17 09:11:58 2022 +0200
@@ -52,10 +52,11 @@
 static int
 allowed(const struct kreq *req)
 {
-	for (size_t i = 0; i < req->reqsz; ++i)
+	for (size_t i = 0; i < req->reqsz; ++i) {
 		if (strcmp(req->reqs[i].key, "X-Api-Key") == 0 &&
 		    strcmp(req->reqs[i].val, scid.apikey) == 0)
 			return 1;
+	}
 
 	return 0;
 }
@@ -78,13 +79,13 @@
 	/* Any API page requires authentication key. */
 	if (req->method == KMETHOD_POST && !allowed(req)) {
 		log_warn("http: client not allowed");
-		pageutil_status(req, KHTTP_401);
+		pageutil_json(req, KHTTP_401, NULL);
 	} else {
 		for (size_t i = 0; apis[i].prefix; ++i)
 			if (strncmp(req->path, apis[i].prefix, strlen(apis[i].prefix)) == 0)
 				return apis[i].handler(req);
 
-		pageutil_status(req, KHTTP_404);
+		pageutil_json(req, KHTTP_404, NULL);
 	}
 }
 
--- a/scid/pageutil.c	Wed Aug 17 09:11:44 2022 +0200
+++ b/scid/pageutil.c	Wed Aug 17 09:11:58 2022 +0200
@@ -19,8 +19,9 @@
 #include <assert.h>
 
 #include "pageutil.h"
+#include "scid.h"
 #include "theme.h"
-#include "scid.h"
+#include "util.h"
 
 void
 pageutil_render(struct kreq *req,
@@ -82,13 +83,11 @@
 
 	khttp_head(req, kresps[KRESP_CONTENT_TYPE], "%s", kmimetypes[KMIME_APP_JSON]);
 	khttp_head(req, kresps[KRESP_STATUS], "%s", khttps[status]);
+	khttp_body(req);
 
 	if (doc) {
-		if ((body = json_dumps(doc, JSON_COMPACT))) {
-			khttp_body(req);
-			khttp_printf(req, "%s", body);
-		}
-
+		body = util_json_dump(doc);
+		khttp_printf(req, "%s", body);
 		json_decref(doc);
 	}