"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
Stefan Sperling <stsp@stsp.name>
Subject:
add username to gotd HTTP notifications
To:
gameoftrees@openbsd.org
Date:
Mon, 29 Apr 2024 00:25:18 +0200

Download raw body.

Thread
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",