From: Stefan Sperling Subject: add username to gotd HTTP notifications To: gameoftrees@openbsd.org Date: Mon, 29 Apr 2024 00:25:18 +0200 At present HTTP notifications only present data from uploaded objects, which is arbitrary. Also send the authenticated username which cannot be faked, unless account credentials are shared between users. ok? ----------------------------------------------- expose authenticated gotd user account in HTTP notifications M gotd/gotd.conf.5 | 12+ 0- M gotd/gotd.h | 2+ 0- M gotd/libexec/got-notify-http/got-notify-http.c | 27+ 12- M gotd/notify.c | 17+ 5- M gotd/session_write.c | 20+ 4- M regress/gotd/http_notification.sh | 8+ 0- 6 files changed, 86 insertions(+), 21 deletions(-) diff 99ed25deb726bbb5294a88152cb1ab8a2049f537 5b0a21516db2700c8c1c7a7eedca08f705ca2ef6 commit - 99ed25deb726bbb5294a88152cb1ab8a2049f537 commit + 5b0a21516db2700c8c1c7a7eedca08f705ca2ef6 blob - c23b8863b3184121b8c368dd247b965685f04711 blob + 3dea3bfa8e197050bf539168af1366e3871504f4 --- gotd/gotd.conf.5 +++ gotd/gotd.conf.5 @@ -380,6 +380,10 @@ When several commits are batched in a single send oper the fields are available for each commit object. .It Dv repo The repository name as string. +.It Dv auth_user +The committer's user account as authenticated by +.Xr gotd 8 +as a string. .It Dv id The commit ID as string, may be abbreviated. .It Dv committer @@ -448,6 +452,10 @@ to be set: .Bl -tag -compact -width Ds .It Dv repo The repository name as string. +.It Dv auth_user +The committer's user account as authenticated by +.Xr gotd 8 +as a string. .It Dv ref The removed branch reference. .It Dv id @@ -458,6 +466,10 @@ The tag notification has the following fields, all gua .Bl -tag -width Ds .It repo The repository name as string. +.It Dv auth_user +The committer's user account as authenticated by +.Xr gotd 8 +as a string. .It tag The tag reference. .It tagger blob - c4375525ccc474f8fe090e2653580512c0faf220 blob + 02ec51dafe5e458386554dc03f1fad6aa05c0026 --- gotd/gotd.h +++ gotd/gotd.h @@ -477,6 +477,8 @@ struct gotd_imsg_notification_content { struct gotd_imsg_notify { char repo_name[NAME_MAX]; char subject_line[64]; + size_t username_len; + /* Followed by username_len data bytes. */ }; int parse_config(const char *, enum gotd_procid, struct gotd *); blob - 4ee2c0b6e836e4cf92abff873a5b9c94c9da2a21 blob + 110aa3179a27bd4af209fd577262ccccdd94f82b --- gotd/libexec/got-notify-http/got-notify-http.c +++ gotd/libexec/got-notify-http/got-notify-http.c @@ -46,7 +46,7 @@ static int http_timeout = 300; /* 5 minutes in secon __dead static void usage(void) { - fprintf(stderr, "usage: %s [-c] -r repo -h host -p port path\n", + fprintf(stderr, "usage: %s [-c] -r repo -h host -p port -u user path\n", getprogname()); exit(1); } @@ -200,7 +200,7 @@ json_author(FILE *fp, const char *type, char *address, } static int -jsonify_branch_rm(FILE *fp, char *line, const char *repo) +jsonify_branch_rm(FILE *fp, char *line, const char *repo, const char *user) { char *ref, *id; @@ -220,6 +220,8 @@ jsonify_branch_rm(FILE *fp, char *line, const char *re fputc('{', fp); json_field(fp, "type", "branch-deleted", 1); json_field(fp, "repo", repo, 1); + if (user) + json_field(fp, "auth_user", user, 1); json_field(fp, "ref", ref, 1); json_field(fp, "id", id, 0); fputc('}', fp); @@ -228,7 +230,7 @@ jsonify_branch_rm(FILE *fp, char *line, const char *re } static int -jsonify_commit_short(FILE *fp, char *line, const char *repo) +jsonify_commit_short(FILE *fp, char *line, const char *repo, const char *user) { char *t, *date, *id, *author, *message; @@ -252,6 +254,8 @@ jsonify_commit_short(FILE *fp, char *line, const char fprintf(fp, "{\"type\":\"commit\",\"short\":true,"); json_field(fp, "repo", repo, 1); + if (user) + json_field(fp, "auth_user", user, 1); json_field(fp, "id", id, 1); json_author(fp, "committer", author, 1); json_date(fp, "date", date, 1); @@ -262,7 +266,8 @@ jsonify_commit_short(FILE *fp, char *line, const char } static int -jsonify_commit(FILE *fp, const char *repo, char **line, ssize_t *linesize) +jsonify_commit(FILE *fp, const char *repo, const char *user, + char **line, ssize_t *linesize) { const char *errstr; char *author = NULL; @@ -291,6 +296,8 @@ jsonify_commit(FILE *fp, const char *repo, char **line fprintf(fp, "{\"type\":\"commit\",\"short\":false,"); json_field(fp, "repo", repo, 1); + if (user) + json_field(fp, "auth_user", user, 1); json_field(fp, "id", l, 1); while (!done) { @@ -557,7 +564,8 @@ jsonify_commit(FILE *fp, const char *repo, char **line } static int -jsonify_tag(FILE *fp, const char *repo, char **line, ssize_t *linesize) +jsonify_tag(FILE *fp, const char *repo, const char *user, + char **line, ssize_t *linesize) { const char *errstr; char *l; @@ -580,6 +588,8 @@ jsonify_tag(FILE *fp, const char *repo, char **line, s fputc('{', fp); json_field(fp, "type", "tag", 1); json_field(fp, "repo", repo, 1); + if (user) + json_field(fp, "auth_user", user, 1); json_field(fp, "tag", l, 1); while (!done) { @@ -688,7 +698,7 @@ jsonify_tag(FILE *fp, const char *repo, char **line, s } static int -jsonify(FILE *fp, const char *repo) +jsonify(FILE *fp, const char *repo, const char *user) { char *line = NULL; size_t linesize = 0; @@ -708,25 +718,26 @@ jsonify(FILE *fp, const char *repo) needcomma = 1; if (strncmp(line, "Removed refs/heads/", 19) == 0) { - if (jsonify_branch_rm(fp, line, repo) == -1) + if (jsonify_branch_rm(fp, line, repo, user) == -1) fatal("jsonify_branch_rm"); continue; } if (strncmp(line, "commit ", 7) == 0) { - if (jsonify_commit(fp, repo, &line, &linesize) == -1) + if (jsonify_commit(fp, repo, user, + &line, &linesize) == -1) fatal("jsonify_commit"); continue; } if (*line >= '0' && *line <= '9') { - if (jsonify_commit_short(fp, line, repo) == -1) + if (jsonify_commit_short(fp, line, repo, user) == -1) fatal("jsonify_commit_short"); continue; } if (strncmp(line, "tag ", 4) == 0) { - if (jsonify_tag(fp, repo, &line, &linesize) == -1) + if (jsonify_tag(fp, repo, user, &line, &linesize) == -1) fatal("jsonify_tag"); continue; } @@ -843,6 +854,7 @@ main(int argc, char **argv) const char *errstr; const char *repo = NULL; const char *host = NULL, *port = NULL, *path = NULL; + const char *gotd_auth_user = NULL; char *auth, *line, *spc; size_t len; ssize_t r; @@ -858,7 +870,7 @@ main(int argc, char **argv) log_init(0, LOG_DAEMON); - while ((ch = getopt(argc, argv, "ch:p:r:")) != -1) { + while ((ch = getopt(argc, argv, "ch:p:r:u:")) != -1) { switch (ch) { case 'c': tls = 1; @@ -872,6 +884,9 @@ main(int argc, char **argv) case 'r': repo = optarg; break; + case 'u': + gotd_auth_user = optarg; + break; default: usage(); } @@ -909,7 +924,7 @@ main(int argc, char **argv) if (tmpfp == NULL) fatal("opentemp"); - jsonify(tmpfp, repo); + jsonify(tmpfp, repo, gotd_auth_user); paylen = ftello(tmpfp); if (paylen == -1) blob - cc0b17db44d592bb3d0b20f73b44556cac260e9c blob + 936d588b3f5a49ce4f818a58d0f599adf5ddc0b9 --- gotd/notify.c +++ gotd/notify.c @@ -261,9 +261,10 @@ notify_email(struct gotd_notification_target *target, } static void -notify_http(struct gotd_notification_target *target, const char *repo, int fd) +notify_http(struct gotd_notification_target *target, const char *repo, + const char *username, int fd) { - const char *argv[10]; + const char *argv[12]; int argc = 0; argv[argc++] = GOTD_PATH_PROG_NOTIFY_HTTP; @@ -276,6 +277,8 @@ notify_http(struct gotd_notification_target *target, c argv[argc++] = target->conf.http.hostname; argv[argc++] = "-p"; argv[argc++] = target->conf.http.port; + argv[argc++] = "-u"; + argv[argc++] = username; argv[argc++] = target->conf.http.path; @@ -294,12 +297,15 @@ send_notification(struct imsg *imsg, struct gotd_imsge struct gotd_repo *repo; struct gotd_notification_target *target; int fd; + char *username = NULL; datalen = imsg->hdr.len - IMSG_HEADER_SIZE; - if (datalen != sizeof(inotify)) + if (datalen < sizeof(inotify)) return got_error(GOT_ERR_PRIVSEP_LEN); - memcpy(&inotify, imsg->data, datalen); + memcpy(&inotify, imsg->data, sizeof(inotify)); + if (datalen != sizeof(inotify) + inotify.username_len) + return got_error(GOT_ERR_PRIVSEP_LEN); repo = gotd_find_repo_by_name(inotify.repo_name, gotd_notify.repos); if (repo == NULL) @@ -309,18 +315,23 @@ send_notification(struct imsg *imsg, struct gotd_imsge if (fd == -1) return got_error(GOT_ERR_PRIVSEP_NO_FD); + username = strndup(imsg->data + sizeof(inotify), inotify.username_len); + if (username == NULL) + return got_error_from_errno("strndup"); + if (lseek(fd, 0, SEEK_SET) == -1) { err = got_error_from_errno("lseek"); goto done; } + STAILQ_FOREACH(target, &repo->notification_targets, entry) { switch (target->type) { case GOTD_NOTIFICATION_VIA_EMAIL: notify_email(target, inotify.subject_line, fd); break; case GOTD_NOTIFICATION_VIA_HTTP: - notify_http(target, repo->name, fd); + notify_http(target, repo->name, username, fd); break; } } @@ -332,6 +343,7 @@ send_notification(struct imsg *imsg, struct gotd_imsge } done: close(fd); + free(username); return err; } blob - 1d073e97037a9cb64129ca446a58b0f3fe2a2a62 blob + d957c78baa66812aad479f8135c63971e8397e2e --- gotd/session_write.c +++ gotd/session_write.c @@ -497,6 +497,7 @@ forward_notification(struct gotd_session_client *clien size_t datalen; struct gotd_imsg_notify inotify; const char *action; + struct ibuf *wbuf; memset(&inotify, 0, sizeof(inotify)); @@ -566,13 +567,28 @@ forward_notification(struct gotd_session_client *clien "%s: %s %s %s", gotd_session.repo_cfg->name, client->username, action, notif->refname); - if (gotd_imsg_compose_event(iev, GOTD_IMSG_NOTIFY, - PROC_SESSION_WRITE, notif->fd, &inotify, sizeof(inotify)) - == -1) { - err = got_error_from_errno("imsg compose NOTIFY"); + inotify.username_len = strlen(client->username); + wbuf = imsg_create(&iev->ibuf, GOTD_IMSG_NOTIFY, + PROC_SESSION_WRITE, gotd_session.pid, + sizeof(inotify) + inotify.username_len); + if (wbuf == NULL) { + err = got_error_from_errno("imsg_create NOTIFY"); goto done; } + if (imsg_add(wbuf, &inotify, sizeof(inotify)) == -1) { + err = got_error_from_errno("imsg_add NOTIFY"); + goto done; + } + if (imsg_add(wbuf, client->username, inotify.username_len) == -1) { + err = got_error_from_errno("imsg_add NOTIFY"); + goto done; + } + + ibuf_fd_set(wbuf, notif->fd); notif->fd = -1; + + imsg_close(&iev->ibuf, wbuf); + gotd_imsg_event_add(iev); done: if (notif->fd != -1) close(notif->fd); blob - 23a8d91f145b36a4fca7522f147af43bd6c98956 blob + 33fbf586ff94a94ca7f4a10f06a3814106db946d --- regress/gotd/http_notification.sh +++ regress/gotd/http_notification.sh @@ -64,6 +64,7 @@ test_file_changed() { "type":"commit", "short":false, "repo":"test-repo", + "auth_user":"${GOTD_DEVUSER}", "id":"$commit_id", "author":{ "full":"$GOT_AUTHOR", @@ -155,6 +156,7 @@ test_bad_utf8() { "type":"commit", "short":false, "repo":"test-repo", + "auth_user":"${GOTD_DEVUSER}", "id":"$commit_id", "author":{ "full":"$GOT_AUTHOR", @@ -255,6 +257,7 @@ test_many_commits_not_summarized() { "type":"commit", "short":false, "repo":"test-repo", + "auth_user":"${GOTD_DEVUSER}", "id":"$commit_id", "author":{ "full":"$GOT_AUTHOR", @@ -359,6 +362,7 @@ test_many_commits_summarized() { "type":"commit", "short":true, "repo":"test-repo", + "auth_user":"${GOTD_DEVUSER}", "id":"$commit_id", "committer":{ "user":"$GOT_AUTHOR_8" @@ -439,6 +443,7 @@ test_branch_created() { "type":"commit", "short":false, "repo":"test-repo", + "auth_user":"${GOTD_DEVUSER}", "id":"$commit_id", "author":{ "full":"$GOT_AUTHOR", @@ -517,6 +522,7 @@ test_branch_removed() { {"notifications":[{ "type":"branch-deleted", "repo":"test-repo", + "auth_user":"${GOTD_DEVUSER}", "ref":"refs/heads/newbranch", "id":"$commit_id" }]} @@ -570,6 +576,7 @@ test_tag_created() { {"notifications":[{ "type":"tag", "repo":"test-repo", + "auth_user":"${GOTD_DEVUSER}", "tag":"refs/tags/1.0", "tagger":{ "full":"$GOT_AUTHOR", @@ -649,6 +656,7 @@ test_tag_changed() { {"notifications":[{ "type":"tag", "repo":"test-repo", + "auth_user":"${GOTD_DEVUSER}", "tag":"refs/tags/1.0", "tagger":{ "full":"$GOT_AUTHOR",