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
This commit is contained in:
tobi 2021-10-10 12:39:25 +02:00 committed by GitHub
commit 367bdca250
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 765 additions and 383 deletions

View file

@ -48,12 +48,9 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
l.Debug("entering Accept")
}
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, _, 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
@ -77,7 +74,7 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
}
// make sure the addressee of the original follow is the same as whatever inbox this landed in
if gtsFollowRequest.AccountID != targetAcct.ID {
if gtsFollowRequest.AccountID != receivingAccount.ID {
return errors.New("ACCEPT: follow object account and inbox account were not the same")
}
follow, err := f.db.AcceptFollowRequest(ctx, gtsFollowRequest.AccountID, gtsFollowRequest.TargetAccountID)
@ -89,7 +86,7 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
APObjectType: ap.ActivityFollow,
APActivityType: ap.ActivityAccept,
GTSModel: follow,
ReceivingAccount: targetAcct,
ReceivingAccount: receivingAccount,
}
return nil
@ -114,7 +111,7 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
return fmt.Errorf("ACCEPT: error converting asfollow to gtsfollow: %s", err)
}
// make sure the addressee of the original follow is the same as whatever inbox this landed in
if gtsFollow.AccountID != targetAcct.ID {
if gtsFollow.AccountID != receivingAccount.ID {
return errors.New("ACCEPT: follow object account and inbox account were not the same")
}
follow, err := f.db.AcceptFollowRequest(ctx, gtsFollow.AccountID, gtsFollow.TargetAccountID)
@ -126,7 +123,7 @@ func (f *federatingDB) Accept(ctx context.Context, accept vocab.ActivityStreamsA
APObjectType: ap.ActivityFollow,
APActivityType: ap.ActivityAccept,
GTSModel: follow,
ReceivingAccount: targetAcct,
ReceivingAccount: receivingAccount,
}
return nil

View file

@ -44,12 +44,9 @@ func (f *federatingDB) Announce(ctx context.Context, announce vocab.ActivityStre
l.Debug("entering Announce")
}
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, _, 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
@ -57,7 +54,7 @@ func (f *federatingDB) Announce(ctx context.Context, announce vocab.ActivityStre
boost, isNew, err := f.typeConverter.ASAnnounceToStatus(ctx, announce)
if err != nil {
return fmt.Errorf("ANNOUNCE: error converting announce to boost: %s", err)
return fmt.Errorf("Announce: error converting announce to boost: %s", err)
}
if !isNew {
@ -70,7 +67,7 @@ func (f *federatingDB) Announce(ctx context.Context, announce vocab.ActivityStre
APObjectType: ap.ActivityAnnounce,
APActivityType: ap.ActivityCreate,
GTSModel: boost,
ReceivingAccount: targetAcct,
ReceivingAccount: receivingAccount,
}
return nil

View file

@ -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.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,
}
}
}
case ap.ActivityFollow:
// FOLLOW SOMETHING
follow, ok := asType.(vocab.ActivityStreamsFollow)
if !ok {
return errors.New("CREATE: could not convert type to follow")
}
followRequest, err := f.typeConverter.ASFollowToFollowRequest(ctx, follow)
if err != nil {
return fmt.Errorf("CREATE: 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("CREATE: database error inserting follow request: %s", err)
}
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")
}
fave, err := f.typeConverter.ASLikeToFave(ctx, like)
if err != nil {
return fmt.Errorf("CREATE: 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("CREATE: database error inserting fave: %s", err)
}
fromFederatorChan <- messages.FromFederator{
APObjectType: ap.ActivityLike,
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")
}
block, err := f.typeConverter.ASBlockToBlock(ctx, blockable)
if err != nil {
return fmt.Errorf("CREATE: could not convert Block to gts model block")
}
newID, err := id.NewULID()
if err != nil {
return err
}
block.ID = newID
if err := f.db.Put(ctx, block); err != nil {
return fmt.Errorf("CREATE: database error inserting block: %s", err)
}
fromFederatorChan <- messages.FromFederator{
APObjectType: ap.ActivityBlock,
APActivityType: ap.ActivityCreate,
GTSModel: block,
ReceivingAccount: targetAcct,
}
return f.activityBlock(ctx, asType, receivingAccount, requestingAccount, fromFederatorChan)
case ap.ActivityCreate:
// CREATE SOMETHING
return f.activityCreate(ctx, asType, receivingAccount, requestingAccount, fromFederatorChan)
case ap.ActivityFollow:
// FOLLOW SOMETHING
return f.activityFollow(ctx, asType, receivingAccount, requestingAccount, fromFederatorChan)
case ap.ActivityLike:
// LIKE SOMETHING
return f.activityLike(ctx, asType, receivingAccount, requestingAccount, fromFederatorChan)
}
return nil
}
/*
BLOCK HANDLERS
*/
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")
}
block, err := f.typeConverter.ASBlockToBlock(ctx, blockable)
if err != nil {
return fmt.Errorf("activityBlock: could not convert Block to gts model block")
}
newID, err := id.NewULID()
if err != nil {
return err
}
block.ID = newID
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
}
// 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()))
}
}
if len(errs) != 0 {
return fmt.Errorf("activityCreate: one or more errors while processing activity: %s", strings.Join(errs, "; "))
}
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.ObjectNote,
APActivityType: ap.ActivityCreate,
APIri: id.GetIRI(),
GTSModel: nil,
ReceivingAccount: receivingAccount,
}
return nil
}
// if we reach this point, we know it's not a forwarded status, so proceed with processing it as normal
status, err := f.typeConverter.ASStatusToStatus(ctx, note)
if err != nil {
return fmt.Errorf("createNote: 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("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
}

View file

@ -0,0 +1,91 @@
/*
GoToSocial
Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package federatingdb_test
import (
"context"
"testing"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/messages"
)
type CreateTestSuite struct {
FederatingDBTestSuite
}
func (suite *CreateTestSuite) TestCreateNote() {
receivingAccount := suite.testAccounts["local_account_1"]
requestingAccount := suite.testAccounts["remote_account_1"]
fromFederatorChan := make(chan messages.FromFederator, 10)
ctx := createTestContext(receivingAccount, requestingAccount, fromFederatorChan)
create := suite.testActivities["dm_for_zork"].Activity
err := suite.federatingDB.Create(ctx, create)
suite.NoError(err)
// should be a message heading to the processor now, which we can intercept here
msg := <-fromFederatorChan
suite.Equal(ap.ObjectNote, msg.APObjectType)
suite.Equal(ap.ActivityCreate, msg.APActivityType)
// shiny new status should be defined on the message
suite.NotNil(msg.GTSModel)
status := msg.GTSModel.(*gtsmodel.Status)
// status should have some expected values
suite.Equal(requestingAccount.ID, status.AccountID)
suite.Equal("hey zork here's a new private note for you", status.Content)
// status should be in the database
_, err = suite.db.GetStatusByID(context.Background(), status.ID)
suite.NoError(err)
}
func (suite *CreateTestSuite) TestCreateNoteForward() {
receivingAccount := suite.testAccounts["local_account_1"]
requestingAccount := suite.testAccounts["remote_account_1"]
fromFederatorChan := make(chan messages.FromFederator, 10)
ctx := createTestContext(receivingAccount, requestingAccount, fromFederatorChan)
create := suite.testActivities["forwarded_message"].Activity
err := suite.federatingDB.Create(ctx, create)
suite.NoError(err)
// should be a message heading to the processor now, which we can intercept here
msg := <-fromFederatorChan
suite.Equal(ap.ObjectNote, msg.APObjectType)
suite.Equal(ap.ActivityCreate, msg.APActivityType)
// nothing should be set as the model since this is a forward
suite.Nil(msg.GTSModel)
// but we should have a uri set
suite.Equal("http://example.org/users/some_user/statuses/afaba698-5740-4e32-a702-af61aa543bc1", msg.APIri.String())
}
func TestCreateTestSuite(t *testing.T) {
suite.Run(t, &CreateTestSuite{})
}

View file

@ -44,12 +44,9 @@ func (f *federatingDB) Delete(ctx context.Context, id *url.URL) error {
)
l.Debug("entering Delete")
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, _, 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
@ -68,7 +65,7 @@ func (f *federatingDB) Delete(ctx context.Context, id *url.URL) error {
APObjectType: ap.ObjectNote,
APActivityType: ap.ActivityDelete,
GTSModel: s,
ReceivingAccount: targetAcct,
ReceivingAccount: receivingAccount,
}
}
@ -80,7 +77,7 @@ func (f *federatingDB) Delete(ctx context.Context, id *url.URL) error {
APObjectType: ap.ObjectProfile,
APActivityType: ap.ActivityDelete,
GTSModel: a,
ReceivingAccount: targetAcct,
ReceivingAccount: receivingAccount,
}
}

View file

@ -19,13 +19,17 @@
package federatingdb_test
import (
"context"
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/messages"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
"github.com/superseriousbusiness/gotosocial/internal/util"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@ -45,6 +49,7 @@ type FederatingDBTestSuite struct {
testAttachments map[string]*gtsmodel.MediaAttachment
testStatuses map[string]*gtsmodel.Status
testBlocks map[string]*gtsmodel.Block
testActivities map[string]testrig.ActivityWithSignature
}
func (suite *FederatingDBTestSuite) SetupSuite() {
@ -56,6 +61,7 @@ func (suite *FederatingDBTestSuite) SetupSuite() {
suite.testAttachments = testrig.NewTestAttachments()
suite.testStatuses = testrig.NewTestStatuses()
suite.testBlocks = testrig.NewTestBlocks()
suite.testActivities = testrig.NewTestActivities(suite.testAccounts)
}
func (suite *FederatingDBTestSuite) SetupTest() {
@ -70,3 +76,11 @@ func (suite *FederatingDBTestSuite) SetupTest() {
func (suite *FederatingDBTestSuite) TearDownTest() {
testrig.StandardDBTeardown(suite.db)
}
func createTestContext(receivingAccount *gtsmodel.Account, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) context.Context {
ctx := context.Background()
ctx = context.WithValue(ctx, util.APReceivingAccount, receivingAccount)
ctx = context.WithValue(ctx, util.APRequestingAccount, requestingAccount)
ctx = context.WithValue(ctx, util.APFromFederatorChanKey, fromFederatorChan)
return ctx
}

View file

@ -46,12 +46,9 @@ func (f *federatingDB) Undo(ctx context.Context, undo vocab.ActivityStreamsUndo)
l.Debug("entering Undo")
}
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, _, 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
@ -83,7 +80,7 @@ func (f *federatingDB) Undo(ctx context.Context, undo vocab.ActivityStreamsUndo)
return fmt.Errorf("UNDO: error converting asfollow to gtsfollow: %s", err)
}
// make sure the addressee of the original follow is the same as whatever inbox this landed in
if gtsFollow.TargetAccountID != targetAcct.ID {
if gtsFollow.TargetAccountID != receivingAccount.ID {
return errors.New("UNDO: follow object account and inbox account were not the same")
}
// delete any existing FOLLOW
@ -116,7 +113,7 @@ func (f *federatingDB) Undo(ctx context.Context, undo vocab.ActivityStreamsUndo)
return fmt.Errorf("UNDO: error converting asblock to gtsblock: %s", err)
}
// make sure the addressee of the original block is the same as whatever inbox this landed in
if gtsBlock.TargetAccountID != targetAcct.ID {
if gtsBlock.TargetAccountID != receivingAccount.ID {
return errors.New("UNDO: block object account and inbox account were not the same")
}
// delete any existing BLOCK

View file

@ -56,12 +56,9 @@ func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error {
l.Debug("entering Update")
}
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, _, 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
@ -153,7 +150,7 @@ func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error {
APObjectType: ap.ObjectProfile,
APActivityType: ap.ActivityUpdate,
GTSModel: updatedAcct,
ReceivingAccount: targetAcct,
ReceivingAccount: receivingAccount,
}
}

View file

@ -289,29 +289,26 @@ func (f *federatingDB) collectIRIs(ctx context.Context, iris []*url.URL) (vocab.
// extractFromCtx extracts some useful values from a context passed into the federatingDB via the API:
// - The target account that owns the inbox or URI being interacted with.
// - The requesting account that posted to the inbox.
// - A channel that messages for the processor can be placed into.
func extractFromCtx(ctx context.Context) (*gtsmodel.Account, chan messages.FromFederator, error) {
var targetAcct *gtsmodel.Account
targetAcctI := ctx.Value(util.APAccount)
if targetAcctI != nil {
var ok bool
targetAcct, ok = targetAcctI.(*gtsmodel.Account)
if !ok {
return nil, nil, errors.New("extractFromCtx: account value in context not parseable")
}
// If a value is not present, nil will be returned for it. It's up to the caller to check this and respond appropriately.
func extractFromCtx(ctx context.Context) (receivingAccount, requestingAccount *gtsmodel.Account, fromFederatorChan chan messages.FromFederator) {
receivingAccountI := ctx.Value(util.APReceivingAccount)
if receivingAccountI != nil {
receivingAccount = receivingAccountI.(*gtsmodel.Account)
}
requestingAcctI := ctx.Value(util.APRequestingAccount)
if requestingAcctI != nil {
requestingAccount = requestingAcctI.(*gtsmodel.Account)
}
var fromFederatorChan chan messages.FromFederator
fromFederatorChanI := ctx.Value(util.APFromFederatorChanKey)
if fromFederatorChanI != nil {
var ok bool
fromFederatorChan, ok = fromFederatorChanI.(chan messages.FromFederator)
if !ok {
return nil, nil, errors.New("extractFromCtx: fromFederatorChan value in context not parseable")
}
fromFederatorChan = fromFederatorChanI.(chan messages.FromFederator)
}
return targetAcct, fromFederatorChan, nil
return
}
func marshalItem(item vocab.Type) (string, error) {

View file

@ -113,12 +113,12 @@ func (f *federator) AuthenticatePostInbox(ctx context.Context, w http.ResponseWr
return nil, false, errors.New("username was empty")
}
requestedAccount, err := f.db.GetLocalAccountByUsername(ctx, username)
receivingAccount, err := f.db.GetLocalAccountByUsername(ctx, username)
if err != nil {
return nil, false, fmt.Errorf("could not fetch requested account with username %s: %s", username, err)
return nil, false, fmt.Errorf("could not fetch receiving account with username %s: %s", username, err)
}
publicKeyOwnerURI, authenticated, err := f.AuthenticateFederatedRequest(ctx, requestedAccount.Username)
publicKeyOwnerURI, authenticated, err := f.AuthenticateFederatedRequest(ctx, receivingAccount.Username)
if err != nil {
l.Debugf("request not authenticated: %s", err)
return ctx, false, err
@ -154,12 +154,12 @@ func (f *federator) AuthenticatePostInbox(ctx context.Context, w http.ResponseWr
requestingAccount, _, err := f.GetRemoteAccount(ctx, username, publicKeyOwnerURI, false)
if err != nil {
return nil, false, fmt.Errorf("couldn't get remote account: %s", err)
return nil, false, fmt.Errorf("couldn't get requesting account %s: %s", publicKeyOwnerURI, err)
}
withRequester := context.WithValue(ctx, util.APRequestingAccount, requestingAccount)
withRequested := context.WithValue(withRequester, util.APAccount, requestedAccount)
return withRequested, true, nil
withRequesting := context.WithValue(ctx, util.APRequestingAccount, requestingAccount)
withReceiving := context.WithValue(withRequesting, util.APReceivingAccount, receivingAccount)
return withReceiving, true, nil
}
// Blocked should determine whether to permit a set of actors given by
@ -182,11 +182,11 @@ func (f *federator) Blocked(ctx context.Context, actorIRIs []*url.URL) (bool, er
})
l.Debugf("entering BLOCKED function with IRI list: %+v", actorIRIs)
requestedAccountI := ctx.Value(util.APAccount)
requestedAccount, ok := requestedAccountI.(*gtsmodel.Account)
receivingAccountI := ctx.Value(util.APReceivingAccount)
receivingAccount, ok := receivingAccountI.(*gtsmodel.Account)
if !ok {
f.log.Errorf("requested account not set on request context")
return false, errors.New("requested account not set on request context, so couldn't determine blocks")
f.log.Errorf("receiving account not set on request context")
return false, errors.New("receiving account not set on request context, so couldn't determine blocks")
}
blocked, err := f.db.AreURIsBlocked(ctx, actorIRIs)
@ -209,12 +209,12 @@ func (f *federator) Blocked(ctx context.Context, actorIRIs []*url.URL) (bool, er
return false, fmt.Errorf("error getting account with uri %s: %s", uri.String(), err)
}
blocked, err = f.db.IsBlocked(ctx, requestedAccount.ID, requestingAccount.ID, false)
blocked, err = f.db.IsBlocked(ctx, receivingAccount.ID, requestingAccount.ID, false)
if err != nil {
return false, fmt.Errorf("error checking account block: %s", err)
}
if blocked {
l.Tracef("local account %s blocks account with uri %s", requestedAccount.Username, uri)
l.Tracef("local account %s blocks account with uri %s", receivingAccount.Username, uri)
return true, nil
}
}

View file

@ -125,7 +125,7 @@ func (suite *ProtocolTestSuite) TestAuthenticatePostInbox() {
ctx := context.Background()
// by the time AuthenticatePostInbox is called, PostInboxRequestBodyHook should have already been called,
// which should have set the account and username onto the request. We can replicate that behavior here:
ctxWithAccount := context.WithValue(ctx, util.APAccount, inboxAccount)
ctxWithAccount := context.WithValue(ctx, util.APReceivingAccount, inboxAccount)
ctxWithActivity := context.WithValue(ctxWithAccount, util.APActivity, activity)
ctxWithVerifier := context.WithValue(ctxWithActivity, util.APRequestingPublicKeyVerifier, verifier)
ctxWithSignature := context.WithValue(ctxWithVerifier, util.APRequestingPublicKeySignature, activity.SignatureHeader)