From b0e344b170feb4da4ba49c016cb184a0518977a7 Mon Sep 17 00:00:00 2001 From: mstar Date: Fri, 9 May 2025 19:18:26 +0200 Subject: [PATCH] Work on Accept activity --- samples/mkAcceptFollow.json | 56 +++++++++++++++++ samples/mkRejectRemoveFollow.json | 56 +++++++++++++++++ web/public/api/activitypub/inbox.go | 97 +++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 samples/mkAcceptFollow.json create mode 100644 samples/mkRejectRemoveFollow.json diff --git a/samples/mkAcceptFollow.json b/samples/mkAcceptFollow.json new file mode 100644 index 0000000..39419ec --- /dev/null +++ b/samples/mkAcceptFollow.json @@ -0,0 +1,56 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "Key": "sec:Key", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "sensitive": "as:sensitive", + "Hashtag": "as:Hashtag", + "quoteUrl": "as:quoteUrl", + "fedibird": "http://fedibird.com/ns#", + "quoteUri": "fedibird:quoteUri", + "toot": "http://joinmastodon.org/ns#", + "Emoji": "toot:Emoji", + "featured": "toot:featured", + "discoverable": "toot:discoverable", + "indexable": "toot:indexable", + "schema": "http://schema.org#", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value", + "misskey": "https://misskey-hub.net/ns#", + "_misskey_content": "misskey:_misskey_content", + "_misskey_quote": "misskey:_misskey_quote", + "_misskey_reaction": "misskey:_misskey_reaction", + "_misskey_votes": "misskey:_misskey_votes", + "_misskey_summary": "misskey:_misskey_summary", + "_misskey_followedMessage": "misskey:_misskey_followedMessage", + "_misskey_requireSigninToViewContents": "misskey:_misskey_requireSigninToViewContents", + "_misskey_makeNotesFollowersOnlyBefore": "misskey:_misskey_makeNotesFollowersOnlyBefore", + "_misskey_makeNotesHiddenBefore": "misskey:_misskey_makeNotesHiddenBefore", + "_misskey_license": "misskey:_misskey_license", + "freeText": { + "@id": "misskey:freeText", + "@type": "schema:text" + }, + "isCat": "misskey:isCat", + "firefish": "https://joinfirefish.org/ns#", + "speakAsCat": "firefish:speakAsCat", + "sharkey": "https://joinsharkey.org/ns#", + "hideOnlineStatus": "sharkey:hideOnlineStatus", + "backgroundUrl": "sharkey:backgroundUrl", + "listenbrainz": "sharkey:listenbrainz", + "enableRss": "sharkey:enableRss", + "vcard": "http://www.w3.org/2006/vcard/ns#" + } + ], + "type": "Accept", + "actor": "https://mk.absturztau.be/users/a1xy7910yc8401z8", + "object": { + "id": "https://activitypub.academy/b2574cea-3c77-4023-9f20-f3837f820aaf", + "type": "Follow", + "actor": "https://activitypub.academy/users/enulius_dorvorglan", + "object": "https://mk.absturztau.be/users/a1xy7910yc8401z8" + }, + "id": "https://mk.absturztau.be/5cfd9f47-2fdc-48cc-8fdd-0c8817811efc" +} diff --git a/samples/mkRejectRemoveFollow.json b/samples/mkRejectRemoveFollow.json new file mode 100644 index 0000000..4bc58c3 --- /dev/null +++ b/samples/mkRejectRemoveFollow.json @@ -0,0 +1,56 @@ +{ + "@context": [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + { + "Key": "sec:Key", + "manuallyApprovesFollowers": "as:manuallyApprovesFollowers", + "sensitive": "as:sensitive", + "Hashtag": "as:Hashtag", + "quoteUrl": "as:quoteUrl", + "fedibird": "http://fedibird.com/ns#", + "quoteUri": "fedibird:quoteUri", + "toot": "http://joinmastodon.org/ns#", + "Emoji": "toot:Emoji", + "featured": "toot:featured", + "discoverable": "toot:discoverable", + "indexable": "toot:indexable", + "schema": "http://schema.org#", + "PropertyValue": "schema:PropertyValue", + "value": "schema:value", + "misskey": "https://misskey-hub.net/ns#", + "_misskey_content": "misskey:_misskey_content", + "_misskey_quote": "misskey:_misskey_quote", + "_misskey_reaction": "misskey:_misskey_reaction", + "_misskey_votes": "misskey:_misskey_votes", + "_misskey_summary": "misskey:_misskey_summary", + "_misskey_followedMessage": "misskey:_misskey_followedMessage", + "_misskey_requireSigninToViewContents": "misskey:_misskey_requireSigninToViewContents", + "_misskey_makeNotesFollowersOnlyBefore": "misskey:_misskey_makeNotesFollowersOnlyBefore", + "_misskey_makeNotesHiddenBefore": "misskey:_misskey_makeNotesHiddenBefore", + "_misskey_license": "misskey:_misskey_license", + "freeText": { + "@id": "misskey:freeText", + "@type": "schema:text" + }, + "isCat": "misskey:isCat", + "firefish": "https://joinfirefish.org/ns#", + "speakAsCat": "firefish:speakAsCat", + "sharkey": "https://joinsharkey.org/ns#", + "hideOnlineStatus": "sharkey:hideOnlineStatus", + "backgroundUrl": "sharkey:backgroundUrl", + "listenbrainz": "sharkey:listenbrainz", + "enableRss": "sharkey:enableRss", + "vcard": "http://www.w3.org/2006/vcard/ns#" + } + ], + "type": "Reject", + "actor": "https://mk.absturztau.be/users/a1xy7910yc8401z8", + "object": { + "id": "https://mk.absturztau.be/follows/a7k4api7gap600n4/a1xy7910yc8401z8", + "type": "Follow", + "actor": "https://activitypub.academy/users/enulius_dorvorglan", + "object": "https://mk.absturztau.be/users/a1xy7910yc8401z8" + }, + "id": "https://mk.absturztau.be/779fd0e4-523c-4a67-a184-208f01f5d6f1" +} diff --git a/web/public/api/activitypub/inbox.go b/web/public/api/activitypub/inbox.go index dcb47e2..a5030d2 100644 --- a/web/public/api/activitypub/inbox.go +++ b/web/public/api/activitypub/inbox.go @@ -7,6 +7,7 @@ import ( "io" "net/http" "regexp" + "strconv" webutils "git.mstar.dev/mstar/goutils/http" "git.mstar.dev/mstar/goutils/other" @@ -87,6 +88,10 @@ func userInbox(w http.ResponseWriter, r *http.Request) { handleUndo(w, r, data) case "Follow": handleFollow(w, r, data) + case "Accept": + handleAccept(w, r, data) + case "Reject": + handleReject(w, r, data) default: webutils.ProblemDetailsStatusOnly(w, 500) } @@ -312,3 +317,95 @@ func handleFollow(w http.ResponseWriter, r *http.Request, object map[string]any) log.Error().Err(err).Msg("Failed to commit follow activity transaction") } } + +func handleAccept(w http.ResponseWriter, r *http.Request, object map[string]any) { + log := hlog.FromRequest(r) + rawTarget, ok := object["object"] + if !ok { + webutils.ProblemDetails( + w, + http.StatusBadRequest, + "/errors/bad-request-data", + "Bad activity data", + other.IntoPointer(`Request data needs to contain a field "object"`), + nil, + ) + return + } + // FIXME: Also handle other undo cases, such as follows + var targetObjectId string + // I *think* the spec says that this must be an object. Not sure though + switch target := rawTarget.(type) { + case string: + targetObjectId = target + case map[string]any: + objType, ok := target["type"].(string) + // TODO: Ensure accept is only used for follows + if !ok || objType != "Follow" { + webutils.ProblemDetails( + w, + http.StatusBadRequest, + "/errors/bad-request-data", + "Bad activity data", + other.IntoPointer(`Target object type must be a string with value "Follow"`), + nil, + ) + return + } + targetObjectId, ok = target["id"].(string) + if !ok { + webutils.ProblemDetails( + w, + http.StatusBadRequest, + "/errors/bad-request-data", + "Bad activity data", + other.IntoPointer(`Missing id in undone object`), + nil, + ) + return + } + default: + webutils.ProblemDetails( + w, + http.StatusBadRequest, + "/errors/bad-request-data", + "Bad activity data", + other.IntoPointer(`Request data needs to contain a field "object" of type string or object`), + nil, + ) + return + } + internalIdMatch := objectIdRegex.FindStringSubmatch(targetObjectId) + if len(internalIdMatch) != 2 { + webutils.ProblemDetails( + w, + http.StatusBadRequest, + "/errors/bad-request-data", + "Bad activity data", + other.IntoPointer(`Request data target object is not internal id`), + nil, + ) + return + } + internalId := internalIdMatch[1] + followActivity, err := dbgen.Activity.Where(dbgen.Activity.Id.Eq(internalId)).First() + switch err { + case gorm.ErrRecordNotFound: + webutils.ProblemDetailsStatusOnly(w, http.StatusNotFound) + return + case nil: + default: + log.Error(). + Err(err). + Str("target-id", internalId). + Msg("Failed to get target follow activity from db") + webutils.ProblemDetailsStatusOnly(w, http.StatusInternalServerError) + return + } + relationId := other.Must(strconv.ParseUint(followActivity.ObjectId, 10, 64)) + dbrel := dbgen.UserToUserRelation + _, err = dbrel.Where(dbrel.ID.Eq(relationId)). + UpdateColumn(dbrel.Relation, models.RelationFollow) +} + +func handleReject(w http.ResponseWriter, r *http.Request, object map[string]any) {}