From e46f8e2a668587f70c8048723a486e2b85fd2fe7 Mon Sep 17 00:00:00 2001 From: Gabe Kangas Date: Thu, 7 Apr 2022 16:14:47 -0700 Subject: [PATCH] Troubleshoot misskey follows Store the original follow request object and use it for approvals. Closes #1690 --- activitypub/apmodels/actor.go | 2 ++ activitypub/inbox/follow.go | 2 +- activitypub/inbox/worker_test.go | 2 ++ activitypub/persistence/followers.go | 1 + activitypub/persistence/persistence.go | 25 ++++++++++++++++--------- activitypub/requests/acceptFollow.go | 8 ++++---- activitypub/resolvers/follow.go | 3 ++- controllers/admin/followers.go | 4 ++-- core/data/data.go | 4 +++- core/data/migrations.go | 12 ++++++++++++ db/db.go | 2 ++ db/models.go | 21 ++++++++++++--------- db/query.sql | 4 ++-- db/query.sql.go | 23 ++++++++++++++--------- db/schema.sql | 1 + 15 files changed, 76 insertions(+), 38 deletions(-) diff --git a/activitypub/apmodels/actor.go b/activitypub/apmodels/actor.go index 7efd5708eb..48abe43d1f 100644 --- a/activitypub/apmodels/actor.go +++ b/activitypub/apmodels/actor.go @@ -30,6 +30,8 @@ type ActivityPubActor struct { FullUsername string // Image is the avatar image of the Actor. Image *url.URL + // RequestObject is the actual follow request object. + RequestObject vocab.ActivityStreamsFollow // W3IDSecurityV1PublicKey is the public key of the actor. W3IDSecurityV1PublicKey vocab.W3IDSecurityV1PublicKeyProperty // DisabledAt is the time, if any, this follower was blocked/removed. diff --git a/activitypub/inbox/follow.go b/activitypub/inbox/follow.go index 70b26abf16..c392112be4 100644 --- a/activitypub/inbox/follow.go +++ b/activitypub/inbox/follow.go @@ -39,7 +39,7 @@ func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsF localAccountName := data.GetDefaultFederationUsername() if approved { - if err := requests.SendFollowAccept(follow.Inbox, follow.FollowRequestIri, localAccountName); err != nil { + if err := requests.SendFollowAccept(follow.Inbox, activity, localAccountName); err != nil { log.Errorln("unable to send follow accept", err) return err } diff --git a/activitypub/inbox/worker_test.go b/activitypub/inbox/worker_test.go index 93b090eb24..33ba624a29 100644 --- a/activitypub/inbox/worker_test.go +++ b/activitypub/inbox/worker_test.go @@ -74,10 +74,12 @@ func TestBlockedDomains(t *testing.T) { func TestBlockedActors(t *testing.T) { person := makeFakePerson() + fakeRequest := streams.NewActivityStreamsFollow() persistence.AddFollow(apmodels.ActivityPubActor{ ActorIri: person.GetJSONLDId().GetIRI(), Inbox: person.GetJSONLDId().GetIRI(), FollowRequestIri: person.GetJSONLDId().GetIRI(), + RequestObject: fakeRequest, }, false) persistence.BlockOrRejectFollower(person.GetJSONLDId().GetIRI().String()) diff --git a/activitypub/persistence/followers.go b/activitypub/persistence/followers.go index df40c581f9..61f1735757 100644 --- a/activitypub/persistence/followers.go +++ b/activitypub/persistence/followers.go @@ -23,6 +23,7 @@ func createFederationFollowersTable() { "created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "approved_at" TIMESTAMP, "disabled_at" TIMESTAMP, + "request_object" BLOB, PRIMARY KEY (iri)); CREATE INDEX iri_index ON ap_followers (iri); CREATE INDEX approved_at_index ON ap_followers (approved_at);` diff --git a/activitypub/persistence/persistence.go b/activitypub/persistence/persistence.go index 68d61d8a00..26628f8466 100644 --- a/activitypub/persistence/persistence.go +++ b/activitypub/persistence/persistence.go @@ -36,7 +36,13 @@ func AddFollow(follow apmodels.ActivityPubActor, approved bool) error { if follow.Image != nil { image = follow.Image.String() } - return createFollow(follow.ActorIri.String(), follow.Inbox.String(), follow.FollowRequestIri.String(), follow.Name, follow.Username, image, approved) + + followRequestObject, err := apmodels.Serialize(follow.RequestObject) + if err != nil { + return errors.Wrap(err, "error serializing follow request object") + } + + return createFollow(follow.ActorIri.String(), follow.Inbox.String(), follow.FollowRequestIri.String(), follow.Name, follow.Username, image, followRequestObject, approved) } // RemoveFollow will remove a follow from the datastore. @@ -109,7 +115,7 @@ func BlockOrRejectFollower(iri string) error { }) } -func createFollow(actor string, inbox string, request string, name string, username string, image string, approved bool) error { +func createFollow(actor, inbox, request, name, username, image string, requestObject []byte, approved bool) error { tx, err := _datastore.DB.Begin() if err != nil { log.Debugln(err) @@ -127,13 +133,14 @@ func createFollow(actor string, inbox string, request string, name string, usern } if err = _datastore.GetQueries().WithTx(tx).AddFollower(context.Background(), db.AddFollowerParams{ - Iri: actor, - Inbox: inbox, - Name: sql.NullString{String: name, Valid: true}, - Username: username, - Image: sql.NullString{String: image, Valid: true}, - ApprovedAt: approvedAt, - Request: request, + Iri: actor, + Inbox: inbox, + Name: sql.NullString{String: name, Valid: true}, + Username: username, + Image: sql.NullString{String: image, Valid: true}, + ApprovedAt: approvedAt, + Request: request, + RequestObject: requestObject, }); err != nil { log.Errorln("error creating new federation follow: ", err) } diff --git a/activitypub/requests/acceptFollow.go b/activitypub/requests/acceptFollow.go index 7cb93a0870..add89e24f8 100644 --- a/activitypub/requests/acceptFollow.go +++ b/activitypub/requests/acceptFollow.go @@ -14,8 +14,8 @@ import ( ) // SendFollowAccept will send an accept activity to a follow request from a specified local user. -func SendFollowAccept(inbox *url.URL, followRequestIRI *url.URL, fromLocalAccountName string) error { - followAccept := makeAcceptFollow(followRequestIRI, fromLocalAccountName) +func SendFollowAccept(inbox *url.URL, originalFollowActivity vocab.ActivityStreamsFollow, fromLocalAccountName string) error { + followAccept := makeAcceptFollow(originalFollowActivity, fromLocalAccountName) localAccountIRI := apmodels.MakeLocalIRIForAccount(fromLocalAccountName) var jsonmap map[string]interface{} @@ -31,7 +31,7 @@ func SendFollowAccept(inbox *url.URL, followRequestIRI *url.URL, fromLocalAccoun return nil } -func makeAcceptFollow(followRequestIri *url.URL, fromAccountName string) vocab.ActivityStreamsAccept { +func makeAcceptFollow(originalFollowActivity vocab.ActivityStreamsFollow, fromAccountName string) vocab.ActivityStreamsAccept { acceptIDString := shortid.MustGenerate() acceptID := apmodels.MakeLocalIRIForResource(acceptIDString) actorID := apmodels.MakeLocalIRIForAccount(fromAccountName) @@ -45,7 +45,7 @@ func makeAcceptFollow(followRequestIri *url.URL, fromAccountName string) vocab.A accept.SetActivityStreamsActor(actor) object := streams.NewActivityStreamsObjectProperty() - object.AppendIRI(followRequestIri) + object.AppendActivityStreamsFollow(originalFollowActivity) accept.SetActivityStreamsObject(object) return accept diff --git a/activitypub/resolvers/follow.go b/activitypub/resolvers/follow.go index 8a2c2aa975..42211c9cd8 100644 --- a/activitypub/resolvers/follow.go +++ b/activitypub/resolvers/follow.go @@ -2,11 +2,11 @@ package resolvers import ( "context" - "errors" "fmt" "github.com/go-fed/activity/streams/vocab" "github.com/owncast/owncast/activitypub/apmodels" + "github.com/pkg/errors" log "github.com/sirupsen/logrus" ) @@ -32,6 +32,7 @@ func MakeFollowRequest(c context.Context, activity vocab.ActivityStreamsFollow) Name: person.Name, Username: fullUsername, Image: person.Image, + RequestObject: activity, } return &followRequest, nil diff --git a/controllers/admin/followers.go b/controllers/admin/followers.go index 624688c989..dd144791c0 100644 --- a/controllers/admin/followers.go +++ b/controllers/admin/followers.go @@ -37,14 +37,14 @@ func ApproveFollower(w http.ResponseWriter, r *http.Request) { localAccountName := data.GetDefaultFederationUsername() - follower, err := persistence.GetFollower(approval.ActorIRI) + followRequest, err := persistence.GetFollower(approval.ActorIRI) if err != nil { controllers.WriteSimpleResponse(w, false, err.Error()) return } // Send the approval to the follow requestor. - if err := requests.SendFollowAccept(follower.Inbox, follower.FollowRequestIri, localAccountName); err != nil { + if err := requests.SendFollowAccept(followRequest.Inbox, followRequest.RequestObject, localAccountName); err != nil { controllers.WriteSimpleResponse(w, false, err.Error()) return } diff --git a/core/data/data.go b/core/data/data.go index bd7bab6b66..3b2d61d19e 100644 --- a/core/data/data.go +++ b/core/data/data.go @@ -17,7 +17,7 @@ import ( ) const ( - schemaVersion = 3 + schemaVersion = 4 ) var ( @@ -139,6 +139,8 @@ func migrateDatabase(db *sql.DB, from, to int) error { migrateToSchema2(db) case 2: migrateToSchema3(db) + case 3: + migrateToSchema4(db) default: log.Fatalln("missing database migration step") } diff --git a/core/data/migrations.go b/core/data/migrations.go index 178e5fba2a..dc7bc7316f 100644 --- a/core/data/migrations.go +++ b/core/data/migrations.go @@ -9,6 +9,18 @@ import ( "github.com/teris-io/shortid" ) +func migrateToSchema4(db *sql.DB) { + stmt, err := db.Prepare("ALTER TABLE ap_followers ADD COLUMN request_object BLOB") + if err != nil { + log.Fatal(err) + } + defer stmt.Close() + _, err = stmt.Exec() + if err != nil { + log.Warnln(err) + } +} + func migrateToSchema3(db *sql.DB) { // Since it's just a backlog of chat messages let's wipe the old messages // and recreate the table. diff --git a/db/db.go b/db/db.go index c3c034ae37..cbb8c9a5d8 100644 --- a/db/db.go +++ b/db/db.go @@ -1,4 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 package db diff --git a/db/models.go b/db/models.go index b3d09dd78a..41b67d0ad7 100644 --- a/db/models.go +++ b/db/models.go @@ -1,4 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 package db @@ -16,15 +18,16 @@ type ApAcceptedActivity struct { } type ApFollower struct { - Iri string - Inbox string - Name sql.NullString - Username string - Image sql.NullString - Request string - CreatedAt sql.NullTime - ApprovedAt sql.NullTime - DisabledAt sql.NullTime + Iri string + Inbox string + Name sql.NullString + Username string + Image sql.NullString + Request string + RequestObject []byte + CreatedAt sql.NullTime + ApprovedAt sql.NullTime + DisabledAt sql.NullTime } type ApOutbox struct { diff --git a/db/query.sql b/db/query.sql index 2f3ba1829c..a1a3d6d35e 100644 --- a/db/query.sql +++ b/db/query.sql @@ -24,7 +24,7 @@ UPDATE ap_followers SET approved_at = $1, disabled_at = null WHERE iri = $2; UPDATE ap_followers SET approved_at = null, disabled_at = $1 WHERE iri = $2; -- name: GetFollowerByIRI :one -SELECT iri, inbox, name, username, image, request, created_at, approved_at, disabled_at FROM ap_followers WHERE iri = $1; +SELECT iri, inbox, name, username, image, request, request_object, created_at, approved_at, disabled_at FROM ap_followers WHERE iri = $1; -- name: GetOutboxWithOffset :many SELECT value FROM ap_outbox LIMIT $1 OFFSET $2; @@ -39,7 +39,7 @@ SELECT value, live_notification, created_at FROM ap_outbox WHERE iri = $1; DELETE FROM ap_followers WHERE iri = $1; -- name: AddFollower :exec -INSERT INTO ap_followers(iri, inbox, request, name, username, image, approved_at) values($1, $2, $3, $4, $5, $6, $7); +INSERT INTO ap_followers(iri, inbox, request, request_object, name, username, image, approved_at) values($1, $2, $3, $4, $5, $6, $7, $8); -- name: AddToOutbox :exec INSERT INTO ap_outbox(iri, value, type, live_notification) values($1, $2, $3, $4); diff --git a/db/query.sql.go b/db/query.sql.go index 20c5ef860a..d5d29a416b 100644 --- a/db/query.sql.go +++ b/db/query.sql.go @@ -1,4 +1,6 @@ // Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.13.0 // source: query.sql package db @@ -10,17 +12,18 @@ import ( ) const addFollower = `-- name: AddFollower :exec -INSERT INTO ap_followers(iri, inbox, request, name, username, image, approved_at) values($1, $2, $3, $4, $5, $6, $7) +INSERT INTO ap_followers(iri, inbox, request, request_object, name, username, image, approved_at) values($1, $2, $3, $4, $5, $6, $7, $8) ` type AddFollowerParams struct { - Iri string - Inbox string - Request string - Name sql.NullString - Username string - Image sql.NullString - ApprovedAt sql.NullTime + Iri string + Inbox string + Request string + RequestObject []byte + Name sql.NullString + Username string + Image sql.NullString + ApprovedAt sql.NullTime } func (q *Queries) AddFollower(ctx context.Context, arg AddFollowerParams) error { @@ -28,6 +31,7 @@ func (q *Queries) AddFollower(ctx context.Context, arg AddFollowerParams) error arg.Iri, arg.Inbox, arg.Request, + arg.RequestObject, arg.Name, arg.Username, arg.Image, @@ -229,7 +233,7 @@ func (q *Queries) GetFederationFollowersWithOffset(ctx context.Context, arg GetF } const getFollowerByIRI = `-- name: GetFollowerByIRI :one -SELECT iri, inbox, name, username, image, request, created_at, approved_at, disabled_at FROM ap_followers WHERE iri = $1 +SELECT iri, inbox, name, username, image, request, request_object, created_at, approved_at, disabled_at FROM ap_followers WHERE iri = $1 ` func (q *Queries) GetFollowerByIRI(ctx context.Context, iri string) (ApFollower, error) { @@ -242,6 +246,7 @@ func (q *Queries) GetFollowerByIRI(ctx context.Context, iri string) (ApFollower, &i.Username, &i.Image, &i.Request, + &i.RequestObject, &i.CreatedAt, &i.ApprovedAt, &i.DisabledAt, diff --git a/db/schema.sql b/db/schema.sql index 2bef2bd7a7..4e5edb6683 100644 --- a/db/schema.sql +++ b/db/schema.sql @@ -8,6 +8,7 @@ CREATE TABLE IF NOT EXISTS ap_followers ( "username" TEXT NOT NULL, "image" TEXT, "request" TEXT NOT NULL, + "request_object" BLOB, "created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "approved_at" TIMESTAMP, "disabled_at" TIMESTAMP,