Browse Source

Troubleshoot misskey follows

Store the original follow request object and use it for approvals.
Closes #1690
pull/1819/head
Gabe Kangas 3 years ago
parent
commit
e46f8e2a66
No known key found for this signature in database
GPG Key ID: 9A56337728BC81EA
  1. 2
      activitypub/apmodels/actor.go
  2. 2
      activitypub/inbox/follow.go
  3. 2
      activitypub/inbox/worker_test.go
  4. 1
      activitypub/persistence/followers.go
  5. 25
      activitypub/persistence/persistence.go
  6. 8
      activitypub/requests/acceptFollow.go
  7. 3
      activitypub/resolvers/follow.go
  8. 4
      controllers/admin/followers.go
  9. 4
      core/data/data.go
  10. 12
      core/data/migrations.go
  11. 2
      db/db.go
  12. 21
      db/models.go
  13. 4
      db/query.sql
  14. 23
      db/query.sql.go
  15. 1
      db/schema.sql

2
activitypub/apmodels/actor.go

@ -30,6 +30,8 @@ type ActivityPubActor struct {
FullUsername string FullUsername string
// Image is the avatar image of the Actor. // Image is the avatar image of the Actor.
Image *url.URL Image *url.URL
// RequestObject is the actual follow request object.
RequestObject vocab.ActivityStreamsFollow
// W3IDSecurityV1PublicKey is the public key of the actor. // W3IDSecurityV1PublicKey is the public key of the actor.
W3IDSecurityV1PublicKey vocab.W3IDSecurityV1PublicKeyProperty W3IDSecurityV1PublicKey vocab.W3IDSecurityV1PublicKeyProperty
// DisabledAt is the time, if any, this follower was blocked/removed. // DisabledAt is the time, if any, this follower was blocked/removed.

2
activitypub/inbox/follow.go

@ -39,7 +39,7 @@ func handleFollowInboxRequest(c context.Context, activity vocab.ActivityStreamsF
localAccountName := data.GetDefaultFederationUsername() localAccountName := data.GetDefaultFederationUsername()
if approved { 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) log.Errorln("unable to send follow accept", err)
return err return err
} }

2
activitypub/inbox/worker_test.go

@ -74,10 +74,12 @@ func TestBlockedDomains(t *testing.T) {
func TestBlockedActors(t *testing.T) { func TestBlockedActors(t *testing.T) {
person := makeFakePerson() person := makeFakePerson()
fakeRequest := streams.NewActivityStreamsFollow()
persistence.AddFollow(apmodels.ActivityPubActor{ persistence.AddFollow(apmodels.ActivityPubActor{
ActorIri: person.GetJSONLDId().GetIRI(), ActorIri: person.GetJSONLDId().GetIRI(),
Inbox: person.GetJSONLDId().GetIRI(), Inbox: person.GetJSONLDId().GetIRI(),
FollowRequestIri: person.GetJSONLDId().GetIRI(), FollowRequestIri: person.GetJSONLDId().GetIRI(),
RequestObject: fakeRequest,
}, false) }, false)
persistence.BlockOrRejectFollower(person.GetJSONLDId().GetIRI().String()) persistence.BlockOrRejectFollower(person.GetJSONLDId().GetIRI().String())

1
activitypub/persistence/followers.go

@ -23,6 +23,7 @@ func createFederationFollowersTable() {
"created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"approved_at" TIMESTAMP, "approved_at" TIMESTAMP,
"disabled_at" TIMESTAMP, "disabled_at" TIMESTAMP,
"request_object" BLOB,
PRIMARY KEY (iri)); PRIMARY KEY (iri));
CREATE INDEX iri_index ON ap_followers (iri); CREATE INDEX iri_index ON ap_followers (iri);
CREATE INDEX approved_at_index ON ap_followers (approved_at);` CREATE INDEX approved_at_index ON ap_followers (approved_at);`

25
activitypub/persistence/persistence.go

@ -36,7 +36,13 @@ func AddFollow(follow apmodels.ActivityPubActor, approved bool) error {
if follow.Image != nil { if follow.Image != nil {
image = follow.Image.String() 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. // 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() tx, err := _datastore.DB.Begin()
if err != nil { if err != nil {
log.Debugln(err) 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{ if err = _datastore.GetQueries().WithTx(tx).AddFollower(context.Background(), db.AddFollowerParams{
Iri: actor, Iri: actor,
Inbox: inbox, Inbox: inbox,
Name: sql.NullString{String: name, Valid: true}, Name: sql.NullString{String: name, Valid: true},
Username: username, Username: username,
Image: sql.NullString{String: image, Valid: true}, Image: sql.NullString{String: image, Valid: true},
ApprovedAt: approvedAt, ApprovedAt: approvedAt,
Request: request, Request: request,
RequestObject: requestObject,
}); err != nil { }); err != nil {
log.Errorln("error creating new federation follow: ", err) log.Errorln("error creating new federation follow: ", err)
} }

8
activitypub/requests/acceptFollow.go

@ -14,8 +14,8 @@ import (
) )
// SendFollowAccept will send an accept activity to a follow request from a specified local user. // 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 { func SendFollowAccept(inbox *url.URL, originalFollowActivity vocab.ActivityStreamsFollow, fromLocalAccountName string) error {
followAccept := makeAcceptFollow(followRequestIRI, fromLocalAccountName) followAccept := makeAcceptFollow(originalFollowActivity, fromLocalAccountName)
localAccountIRI := apmodels.MakeLocalIRIForAccount(fromLocalAccountName) localAccountIRI := apmodels.MakeLocalIRIForAccount(fromLocalAccountName)
var jsonmap map[string]interface{} var jsonmap map[string]interface{}
@ -31,7 +31,7 @@ func SendFollowAccept(inbox *url.URL, followRequestIRI *url.URL, fromLocalAccoun
return nil return nil
} }
func makeAcceptFollow(followRequestIri *url.URL, fromAccountName string) vocab.ActivityStreamsAccept { func makeAcceptFollow(originalFollowActivity vocab.ActivityStreamsFollow, fromAccountName string) vocab.ActivityStreamsAccept {
acceptIDString := shortid.MustGenerate() acceptIDString := shortid.MustGenerate()
acceptID := apmodels.MakeLocalIRIForResource(acceptIDString) acceptID := apmodels.MakeLocalIRIForResource(acceptIDString)
actorID := apmodels.MakeLocalIRIForAccount(fromAccountName) actorID := apmodels.MakeLocalIRIForAccount(fromAccountName)
@ -45,7 +45,7 @@ func makeAcceptFollow(followRequestIri *url.URL, fromAccountName string) vocab.A
accept.SetActivityStreamsActor(actor) accept.SetActivityStreamsActor(actor)
object := streams.NewActivityStreamsObjectProperty() object := streams.NewActivityStreamsObjectProperty()
object.AppendIRI(followRequestIri) object.AppendActivityStreamsFollow(originalFollowActivity)
accept.SetActivityStreamsObject(object) accept.SetActivityStreamsObject(object)
return accept return accept

3
activitypub/resolvers/follow.go

@ -2,11 +2,11 @@ package resolvers
import ( import (
"context" "context"
"errors"
"fmt" "fmt"
"github.com/go-fed/activity/streams/vocab" "github.com/go-fed/activity/streams/vocab"
"github.com/owncast/owncast/activitypub/apmodels" "github.com/owncast/owncast/activitypub/apmodels"
"github.com/pkg/errors"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
) )
@ -32,6 +32,7 @@ func MakeFollowRequest(c context.Context, activity vocab.ActivityStreamsFollow)
Name: person.Name, Name: person.Name,
Username: fullUsername, Username: fullUsername,
Image: person.Image, Image: person.Image,
RequestObject: activity,
} }
return &followRequest, nil return &followRequest, nil

4
controllers/admin/followers.go

@ -37,14 +37,14 @@ func ApproveFollower(w http.ResponseWriter, r *http.Request) {
localAccountName := data.GetDefaultFederationUsername() localAccountName := data.GetDefaultFederationUsername()
follower, err := persistence.GetFollower(approval.ActorIRI) followRequest, err := persistence.GetFollower(approval.ActorIRI)
if err != nil { if err != nil {
controllers.WriteSimpleResponse(w, false, err.Error()) controllers.WriteSimpleResponse(w, false, err.Error())
return return
} }
// Send the approval to the follow requestor. // 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()) controllers.WriteSimpleResponse(w, false, err.Error())
return return
} }

4
core/data/data.go

@ -17,7 +17,7 @@ import (
) )
const ( const (
schemaVersion = 3 schemaVersion = 4
) )
var ( var (
@ -139,6 +139,8 @@ func migrateDatabase(db *sql.DB, from, to int) error {
migrateToSchema2(db) migrateToSchema2(db)
case 2: case 2:
migrateToSchema3(db) migrateToSchema3(db)
case 3:
migrateToSchema4(db)
default: default:
log.Fatalln("missing database migration step") log.Fatalln("missing database migration step")
} }

12
core/data/migrations.go

@ -9,6 +9,18 @@ import (
"github.com/teris-io/shortid" "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) { func migrateToSchema3(db *sql.DB) {
// Since it's just a backlog of chat messages let's wipe the old messages // Since it's just a backlog of chat messages let's wipe the old messages
// and recreate the table. // and recreate the table.

2
db/db.go

@ -1,4 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.13.0
package db package db

21
db/models.go

@ -1,4 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.13.0
package db package db
@ -16,15 +18,16 @@ type ApAcceptedActivity struct {
} }
type ApFollower struct { type ApFollower struct {
Iri string Iri string
Inbox string Inbox string
Name sql.NullString Name sql.NullString
Username string Username string
Image sql.NullString Image sql.NullString
Request string Request string
CreatedAt sql.NullTime RequestObject []byte
ApprovedAt sql.NullTime CreatedAt sql.NullTime
DisabledAt sql.NullTime ApprovedAt sql.NullTime
DisabledAt sql.NullTime
} }
type ApOutbox struct { type ApOutbox struct {

4
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; UPDATE ap_followers SET approved_at = null, disabled_at = $1 WHERE iri = $2;
-- name: GetFollowerByIRI :one -- 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 -- name: GetOutboxWithOffset :many
SELECT value FROM ap_outbox LIMIT $1 OFFSET $2; 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; DELETE FROM ap_followers WHERE iri = $1;
-- name: AddFollower :exec -- 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 -- name: AddToOutbox :exec
INSERT INTO ap_outbox(iri, value, type, live_notification) values($1, $2, $3, $4); INSERT INTO ap_outbox(iri, value, type, live_notification) values($1, $2, $3, $4);

23
db/query.sql.go

@ -1,4 +1,6 @@
// Code generated by sqlc. DO NOT EDIT. // Code generated by sqlc. DO NOT EDIT.
// versions:
// sqlc v1.13.0
// source: query.sql // source: query.sql
package db package db
@ -10,17 +12,18 @@ import (
) )
const addFollower = `-- name: AddFollower :exec 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 { type AddFollowerParams struct {
Iri string Iri string
Inbox string Inbox string
Request string Request string
Name sql.NullString RequestObject []byte
Username string Name sql.NullString
Image sql.NullString Username string
ApprovedAt sql.NullTime Image sql.NullString
ApprovedAt sql.NullTime
} }
func (q *Queries) AddFollower(ctx context.Context, arg AddFollowerParams) error { 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.Iri,
arg.Inbox, arg.Inbox,
arg.Request, arg.Request,
arg.RequestObject,
arg.Name, arg.Name,
arg.Username, arg.Username,
arg.Image, arg.Image,
@ -229,7 +233,7 @@ func (q *Queries) GetFederationFollowersWithOffset(ctx context.Context, arg GetF
} }
const getFollowerByIRI = `-- name: GetFollowerByIRI :one 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) { 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.Username,
&i.Image, &i.Image,
&i.Request, &i.Request,
&i.RequestObject,
&i.CreatedAt, &i.CreatedAt,
&i.ApprovedAt, &i.ApprovedAt,
&i.DisabledAt, &i.DisabledAt,

1
db/schema.sql

@ -8,6 +8,7 @@ CREATE TABLE IF NOT EXISTS ap_followers (
"username" TEXT NOT NULL, "username" TEXT NOT NULL,
"image" TEXT, "image" TEXT,
"request" TEXT NOT NULL, "request" TEXT NOT NULL,
"request_object" BLOB,
"created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP, "created_at" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
"approved_at" TIMESTAMP, "approved_at" TIMESTAMP,
"disabled_at" TIMESTAMP, "disabled_at" TIMESTAMP,

Loading…
Cancel
Save