diff options
author | 2021-10-10 12:39:25 +0200 | |
---|---|---|
committer | 2021-10-10 12:39:25 +0200 | |
commit | 367bdca25093ee76b36506d8a5e6733b0aa2e2bb (patch) | |
tree | 37b1b82ae6e9fad7e6d95b8abbb58bdb42049707 /internal/federation/federatingdb/create.go | |
parent | Derive visibility fixes (#271) (diff) | |
download | gotosocial-367bdca25093ee76b36506d8a5e6733b0aa2e2bb.tar.xz |
Handle forwarded messages (#273)
* correct path of foss_satan
* add APIri and notes
* test create forward note
* rename target => receiving account
* split up create into separate funcs
* update extractFromCtx
* tidy up from federator processing
* foss satan => http not https
* check if status in db
* mock dereference of status from IRI
* add forward message deref test
* update test with activities
* add remote_account_2 to test rig
Diffstat (limited to 'internal/federation/federatingdb/create.go')
-rw-r--r-- | internal/federation/federatingdb/create.go | 337 |
1 files changed, 228 insertions, 109 deletions
diff --git a/internal/federation/federatingdb/create.go b/internal/federation/federatingdb/create.go index 2a31108f2..4262dfcb4 100644 --- a/internal/federation/federatingdb/create.go +++ b/internal/federation/federatingdb/create.go @@ -22,11 +22,13 @@ import ( "context" "errors" "fmt" + "strings" "github.com/go-fed/activity/streams/vocab" "github.com/sirupsen/logrus" "github.com/superseriousbusiness/gotosocial/internal/ap" "github.com/superseriousbusiness/gotosocial/internal/db" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/id" "github.com/superseriousbusiness/gotosocial/internal/messages" ) @@ -59,144 +61,261 @@ func (f *federatingDB) Create(ctx context.Context, asType vocab.Type) error { l.Debug("entering Create") } - targetAcct, fromFederatorChan, err := extractFromCtx(ctx) - if err != nil { - return err - } - if targetAcct == nil || fromFederatorChan == nil { - // If the target account or federator channel wasn't set on the context, that means this request didn't pass + receivingAccount, requestingAccount, fromFederatorChan := extractFromCtx(ctx) + if receivingAccount == nil || fromFederatorChan == nil { + // If the receiving account or federator channel wasn't set on the context, that means this request didn't pass // through the API, but came from inside GtS as the result of another activity on this instance. That being so, // we can safely just ignore this activity, since we know we've already processed it elsewhere. return nil } switch asType.GetTypeName() { + case ap.ActivityBlock: + // BLOCK SOMETHING + return f.activityBlock(ctx, asType, receivingAccount, requestingAccount, fromFederatorChan) case ap.ActivityCreate: // CREATE SOMETHING - create, ok := asType.(vocab.ActivityStreamsCreate) - if !ok { - return errors.New("CREATE: could not convert type to create") - } - object := create.GetActivityStreamsObject() - for objectIter := object.Begin(); objectIter != object.End(); objectIter = objectIter.Next() { - switch objectIter.GetType().GetTypeName() { - case ap.ObjectNote: - // CREATE A NOTE - note := objectIter.GetActivityStreamsNote() - status, err := f.typeConverter.ASStatusToStatus(ctx, note) - if err != nil { - return fmt.Errorf("CREATE: error converting note to status: %s", err) - } - - // id the status based on the time it was created - statusID, err := id.NewULIDFromTime(status.CreatedAt) - if err != nil { - return err - } - status.ID = statusID - - if err := f.db.PutStatus(ctx, status); err != nil { - if err == db.ErrAlreadyExists { - // the status already exists in the database, which means we've already handled everything else, - // so we can just return nil here and be done with it. - return nil - } - // an actual error has happened - return fmt.Errorf("CREATE: database error inserting status: %s", err) - } - - fromFederatorChan <- messages.FromFederator{ - APObjectType: ap.ObjectNote, - APActivityType: ap.ActivityCreate, - GTSModel: status, - ReceivingAccount: targetAcct, - } - } - } + return f.activityCreate(ctx, asType, receivingAccount, requestingAccount, fromFederatorChan) case ap.ActivityFollow: // FOLLOW SOMETHING - follow, ok := asType.(vocab.ActivityStreamsFollow) - if !ok { - return errors.New("CREATE: could not convert type to follow") - } + return f.activityFollow(ctx, asType, receivingAccount, requestingAccount, fromFederatorChan) + case ap.ActivityLike: + // LIKE SOMETHING + return f.activityLike(ctx, asType, receivingAccount, requestingAccount, fromFederatorChan) + } + return nil +} - followRequest, err := f.typeConverter.ASFollowToFollowRequest(ctx, follow) - if err != nil { - return fmt.Errorf("CREATE: could not convert Follow to follow request: %s", err) - } +/* + BLOCK HANDLERS +*/ - newID, err := id.NewULID() - if err != nil { - return err - } - followRequest.ID = newID +func (f *federatingDB) activityBlock(ctx context.Context, asType vocab.Type, receiving *gtsmodel.Account, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) error { + blockable, ok := asType.(vocab.ActivityStreamsBlock) + if !ok { + return errors.New("activityBlock: could not convert type to block") + } - if err := f.db.Put(ctx, followRequest); err != nil { - return fmt.Errorf("CREATE: database error inserting follow request: %s", err) - } + block, err := f.typeConverter.ASBlockToBlock(ctx, blockable) + if err != nil { + return fmt.Errorf("activityBlock: could not convert Block to gts model block") + } - fromFederatorChan <- messages.FromFederator{ - APObjectType: ap.ActivityFollow, - APActivityType: ap.ActivityCreate, - GTSModel: followRequest, - ReceivingAccount: targetAcct, - } - case ap.ActivityLike: - // LIKE SOMETHING - like, ok := asType.(vocab.ActivityStreamsLike) - if !ok { - return errors.New("CREATE: could not convert type to like") - } + newID, err := id.NewULID() + if err != nil { + return err + } + block.ID = newID - fave, err := f.typeConverter.ASLikeToFave(ctx, like) - if err != nil { - return fmt.Errorf("CREATE: could not convert Like to fave: %s", err) + if err := f.db.Put(ctx, block); err != nil { + return fmt.Errorf("activityBlock: database error inserting block: %s", err) + } + + fromFederatorChan <- messages.FromFederator{ + APObjectType: ap.ActivityBlock, + APActivityType: ap.ActivityCreate, + GTSModel: block, + ReceivingAccount: receiving, + } + return nil +} + +/* + CREATE HANDLERS +*/ + +func (f *federatingDB) activityCreate(ctx context.Context, asType vocab.Type, receivingAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) error { + create, ok := asType.(vocab.ActivityStreamsCreate) + if !ok { + return errors.New("activityCreate: could not convert type to create") + } + + // create should have an object + object := create.GetActivityStreamsObject() + if object == nil { + return errors.New("Create had no Object") + } + + errs := []string{} + // iterate through the object(s) to see what we're meant to be creating + for objectIter := object.Begin(); objectIter != object.End(); objectIter = objectIter.Next() { + asObjectType := objectIter.GetType() + if asObjectType == nil { + // currently we can't do anything with just a Create of something that's not an Object with a type + // TODO: process a Create with an Object that's just a URI or something + errs = append(errs, "object of Create was not a Type") + continue } - newID, err := id.NewULID() - if err != nil { - return err + // we have a type -- what is it? + asObjectTypeName := asObjectType.GetTypeName() + switch asObjectTypeName { + case ap.ObjectNote: + // CREATE A NOTE + if err := f.createNote(ctx, objectIter.GetActivityStreamsNote(), receivingAccount, requestingAccount, fromFederatorChan); err != nil { + errs = append(errs, err.Error()) + } + default: + errs = append(errs, fmt.Sprintf("received an object on a Create that we couldn't handle: %s", asObjectType.GetTypeName())) } - fave.ID = newID + } + + if len(errs) != 0 { + return fmt.Errorf("activityCreate: one or more errors while processing activity: %s", strings.Join(errs, "; ")) + } - if err := f.db.Put(ctx, fave); err != nil { - return fmt.Errorf("CREATE: database error inserting fave: %s", err) + return nil +} + +// createNote handles a Create activity with a Note type. +func (f *federatingDB) createNote(ctx context.Context, note vocab.ActivityStreamsNote, receivingAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) error { + l := f.log.WithFields(logrus.Fields{ + "func": "createNote", + "receivingAccount": receivingAccount.URI, + "requestingAccount": requestingAccount.URI, + }) + + // Check if we have a forward. + // In other words, was the note posted to our inbox by at least one actor who actually created the note, or are they just forwarding it? + forward := true + + // note should have an attributedTo + noteAttributedTo := note.GetActivityStreamsAttributedTo() + if noteAttributedTo == nil { + return errors.New("createNote: note had no attributedTo") + } + + // compare the attributedTo(s) with the actor who posted this to our inbox + for attributedToIter := noteAttributedTo.Begin(); attributedToIter != noteAttributedTo.End(); attributedToIter = attributedToIter.Next() { + if !attributedToIter.IsIRI() { + continue + } + iri := attributedToIter.GetIRI() + if requestingAccount.URI == iri.String() { + // at least one creator of the note, and the actor who posted the note to our inbox, are the same, so it's not a forward + forward = false } + } + // If we do have a forward, we should ignore the content for now and just dereference based on the URL/ID of the note instead, to get the note straight from the horse's mouth + if forward { + l.Trace("note is a forward") + id := note.GetJSONLDId() + if !id.IsIRI() { + // if the note id isn't an IRI, there's nothing we can do here + return nil + } + // pass the note iri into the processor and have it do the dereferencing instead of doing it here fromFederatorChan <- messages.FromFederator{ - APObjectType: ap.ActivityLike, + APObjectType: ap.ObjectNote, APActivityType: ap.ActivityCreate, - GTSModel: fave, - ReceivingAccount: targetAcct, - } - case ap.ActivityBlock: - // BLOCK SOMETHING - blockable, ok := asType.(vocab.ActivityStreamsBlock) - if !ok { - return errors.New("CREATE: could not convert type to block") + APIri: id.GetIRI(), + GTSModel: nil, + ReceivingAccount: receivingAccount, } + return nil + } - block, err := f.typeConverter.ASBlockToBlock(ctx, blockable) - if err != nil { - return fmt.Errorf("CREATE: could not convert Block to gts model block") - } + // if we reach this point, we know it's not a forwarded status, so proceed with processing it as normal - newID, err := id.NewULID() - if err != nil { - return err - } - block.ID = newID + status, err := f.typeConverter.ASStatusToStatus(ctx, note) + if err != nil { + return fmt.Errorf("createNote: error converting note to status: %s", err) + } - if err := f.db.Put(ctx, block); err != nil { - return fmt.Errorf("CREATE: database error inserting block: %s", err) - } + // id the status based on the time it was created + statusID, err := id.NewULIDFromTime(status.CreatedAt) + if err != nil { + return err + } + status.ID = statusID - fromFederatorChan <- messages.FromFederator{ - APObjectType: ap.ActivityBlock, - APActivityType: ap.ActivityCreate, - GTSModel: block, - ReceivingAccount: targetAcct, + if err := f.db.PutStatus(ctx, status); err != nil { + if err == db.ErrAlreadyExists { + // the status already exists in the database, which means we've already handled everything else, + // so we can just return nil here and be done with it. + return nil } + // an actual error has happened + return fmt.Errorf("createNote: database error inserting status: %s", err) } + + fromFederatorChan <- messages.FromFederator{ + APObjectType: ap.ObjectNote, + APActivityType: ap.ActivityCreate, + GTSModel: status, + ReceivingAccount: receivingAccount, + } + + return nil +} + +/* + FOLLOW HANDLERS +*/ + +func (f *federatingDB) activityFollow(ctx context.Context, asType vocab.Type, receivingAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) error { + follow, ok := asType.(vocab.ActivityStreamsFollow) + if !ok { + return errors.New("activityFollow: could not convert type to follow") + } + + followRequest, err := f.typeConverter.ASFollowToFollowRequest(ctx, follow) + if err != nil { + return fmt.Errorf("activityFollow: could not convert Follow to follow request: %s", err) + } + + newID, err := id.NewULID() + if err != nil { + return err + } + followRequest.ID = newID + + if err := f.db.Put(ctx, followRequest); err != nil { + return fmt.Errorf("activityFollow: database error inserting follow request: %s", err) + } + + fromFederatorChan <- messages.FromFederator{ + APObjectType: ap.ActivityFollow, + APActivityType: ap.ActivityCreate, + GTSModel: followRequest, + ReceivingAccount: receivingAccount, + } + + return nil +} + +/* + LIKE HANDLERS +*/ + +func (f *federatingDB) activityLike(ctx context.Context, asType vocab.Type, receivingAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) error { + like, ok := asType.(vocab.ActivityStreamsLike) + if !ok { + return errors.New("activityLike: could not convert type to like") + } + + fave, err := f.typeConverter.ASLikeToFave(ctx, like) + if err != nil { + return fmt.Errorf("activityLike: could not convert Like to fave: %s", err) + } + + newID, err := id.NewULID() + if err != nil { + return err + } + fave.ID = newID + + if err := f.db.Put(ctx, fave); err != nil { + return fmt.Errorf("activityLike: database error inserting fave: %s", err) + } + + fromFederatorChan <- messages.FromFederator{ + APObjectType: ap.ActivityLike, + APActivityType: ap.ActivityCreate, + GTSModel: fave, + ReceivingAccount: receivingAccount, + } + return nil } |