From 071b7e9b01e41d4048b1bbb3d9caaac4c8a526de Mon Sep 17 00:00:00 2001 From: Anders Betts Date: Thu, 17 Sep 2026 21:48:00 +0200 Subject: Add token login to bokfctl and enforce admin scope for tokens - bokfctl: --token / BOKFD_TOKEN via client_token_login - tokens must carry the admin scope for admin commands (was bypassed) - docs: token-based snapshot for restic backups --- clients/bokfctl.c | 23 +++++++++++++++++++---- clients/client.c | 49 +++++++++++++++++++++++++++++++++++++++++-------- clients/client.h | 4 ++++ 3 files changed, 64 insertions(+), 12 deletions(-) (limited to 'clients') diff --git a/clients/bokfctl.c b/clients/bokfctl.c index 1f1372c..b5ecf47 100644 --- a/clients/bokfctl.c +++ b/clients/bokfctl.c @@ -17,6 +17,7 @@ static void usage(void) " (env BOKFD_SOCKET, default /run/bokfd/bokfd.sock)\n" " --user NAME login user (env BOKFD_USER)\n" " --password PW login password (env BOKFD_PASSWORD)\n" + " --token TOKEN API token instead of user/password (env BOKFD_TOKEN)\n" " --org ID active org for this request\n" " --version\n" "\n" @@ -39,6 +40,7 @@ int main(int argc, char **argv) target = "/run/bokfd/bokfd.sock"; const char *user = getenv("BOKFD_USER"); const char *password = getenv("BOKFD_PASSWORD"); + const char *token = getenv("BOKFD_TOKEN"); int64_t org = 0; const char *cmd = NULL; const char *args_json = NULL; @@ -70,6 +72,14 @@ int main(int argc, char **argv) return 2; } password = val; + } else if (!strncmp(a, "--token", 7) && + (a[7] == '\0' || a[7] == '=')) { + val = a[7] == '=' ? a + 8 : (i + 1 < argc ? argv[++i] : NULL); + if (!val) { + fprintf(stderr, "bokfctl: --token requires a value\n"); + return 2; + } + token = val; } else if (!strncmp(a, "--org", 5) && (a[5] == '\0' || a[5] == '=')) { val = a[5] == '=' ? a + 6 : (i + 1 < argc ? argv[++i] : NULL); @@ -111,14 +121,19 @@ int main(int argc, char **argv) char *session = NULL; if (!is_local_cmd(cmd)) { - if (!user || !password) { + char *lerr = NULL; + int login_rc; + if (token && *token) { + login_rc = client_token_login(&conn, token, &session, &lerr); + } else if (user && password) { + login_rc = client_login(&conn, user, password, &session, &lerr); + } else { fprintf(stderr, - "bokfctl: set BOKFD_USER and BOKFD_PASSWORD (or --user/--password) to log in\n"); + "bokfctl: set BOKFD_TOKEN or BOKFD_USER/BOKFD_PASSWORD (or --token/--user/--password) to log in\n"); client_close(&conn); return 2; } - char *lerr = NULL; - if (client_login(&conn, user, password, &session, &lerr) != 0) { + if (login_rc != 0) { fprintf(stderr, "%s\n", lerr ? lerr : "login failed"); int rc = lerr && lerr[0] == '{' ? 1 : 2; free(lerr); diff --git a/clients/client.c b/clients/client.c index 8fb604b..fd119f1 100644 --- a/clients/client.c +++ b/clients/client.c @@ -361,18 +361,12 @@ char *client_rpc(struct client_conn *c, const char *cmd, const char *session, return client_read_line(c); } -int client_login(struct client_conn *c, const char *user, const char *password, - char **session_out, char **err_out) +static int session_open(struct client_conn *c, const char *args, + char **session_out, char **err_out) { *session_out = NULL; *err_out = NULL; - char *args = client_make_login_args(user, password); - if (!args) { - *err_out = xstrdup("could not build login request"); - return -1; - } char *resp = client_rpc(c, "session.open", NULL, 0, args); - free(args); if (!resp) { *err_out = xstrdup(client_last_error()); return -1; @@ -396,6 +390,45 @@ int client_login(struct client_conn *c, const char *user, const char *password, return 0; } +int client_login(struct client_conn *c, const char *user, const char *password, + char **session_out, char **err_out) +{ + char *args = client_make_login_args(user, password); + if (!args) { + *session_out = NULL; + *err_out = xstrdup("could not build login request"); + return -1; + } + int rc = session_open(c, args, session_out, err_out); + free(args); + return rc; +} + +int client_token_login(struct client_conn *c, const char *token, + char **session_out, char **err_out) +{ + yyjson_mut_doc *d = yyjson_mut_doc_new(NULL); + if (!d) { + *session_out = NULL; + *err_out = xstrdup("out of memory"); + return -1; + } + yyjson_mut_val *o = yyjson_mut_obj(d); + yyjson_mut_doc_set_root(d, o); + yyjson_mut_obj_add_strcpy(d, o, "method", "token"); + yyjson_mut_obj_add_strcpy(d, o, "token", token); + char *args = yyjson_mut_write(d, 0, NULL); + yyjson_mut_doc_free(d); + if (!args) { + *session_out = NULL; + *err_out = xstrdup("could not build token request"); + return -1; + } + int rc = session_open(c, args, session_out, err_out); + free(args); + return rc; +} + int client_ok(const char *response) { if (!response) diff --git a/clients/client.h b/clients/client.h index 9372e72..40a8669 100644 --- a/clients/client.h +++ b/clients/client.h @@ -36,6 +36,10 @@ char *client_rpc(struct client_conn *c, const char *cmd, const char *session, int client_login(struct client_conn *c, const char *user, const char *password, char **session_out, char **err_out); +/* Token login (same contract as client_login). */ +int client_token_login(struct client_conn *c, const char *token, + char **session_out, char **err_out); + /* Convenience: true when the response line has "ok":true. */ int client_ok(const char *response); /* Extract result.session into buf; returns 0 on success. */ -- cgit v1.3