From b5a7e1ba323a2493873dedf3a3835c2e9564cb24 Mon Sep 17 00:00:00 2001 From: tobi <31960611+tsmethurst@users.noreply.github.com> Date: Tue, 28 Sep 2021 15:21:59 +0200 Subject: [PATCH] Account update issue (#250) * start poking around * tests * notes and fiddling --- internal/api/s2s/user/inboxpost_test.go | 130 ++++++++++++++++++ internal/db/bundb/account_test.go | 32 +++++ ...rating_db_test.go => federatingdb_test.go} | 0 internal/federation/federatingdb/update.go | 12 +- internal/processing/fromfederator.go | 10 +- 5 files changed, 172 insertions(+), 12 deletions(-) rename internal/federation/federatingdb/{federating_db_test.go => federatingdb_test.go} (100%) diff --git a/internal/api/s2s/user/inboxpost_test.go b/internal/api/s2s/user/inboxpost_test.go index 47f5295f0..904586b98 100644 --- a/internal/api/s2s/user/inboxpost_test.go +++ b/internal/api/s2s/user/inboxpost_test.go @@ -223,6 +223,136 @@ func (suite *InboxPostTestSuite) TestPostUnblock() { suite.Nil(block) } +func (suite *InboxPostTestSuite) TestPostUpdate() { + updatedAccount := *suite.testAccounts["remote_account_1"] + updatedAccount.DisplayName = "updated display name!" + + asAccount, err := suite.tc.AccountToAS(context.Background(), &updatedAccount) + suite.NoError(err) + + receivingAccount := suite.testAccounts["local_account_1"] + + // create an update + update := streams.NewActivityStreamsUpdate() + + // set the appropriate actor on it + updateActor := streams.NewActivityStreamsActorProperty() + updateActor.AppendIRI(testrig.URLMustParse(updatedAccount.URI)) + update.SetActivityStreamsActor(updateActor) + + // Set the account as the 'object' property. + updateObject := streams.NewActivityStreamsObjectProperty() + updateObject.AppendActivityStreamsPerson(asAccount) + update.SetActivityStreamsObject(updateObject) + + // Set the To of the update as public + updateTo := streams.NewActivityStreamsToProperty() + updateTo.AppendIRI(testrig.URLMustParse("https://www.w3.org/ns/activitystreams#Public")) + update.SetActivityStreamsTo(updateTo) + + // set the cc of the update to the receivingAccount + updateCC := streams.NewActivityStreamsCcProperty() + updateCC.AppendIRI(testrig.URLMustParse(receivingAccount.URI)) + update.SetActivityStreamsCc(updateCC) + + // set some random-ass ID for the activity + undoID := streams.NewJSONLDIdProperty() + undoID.SetIRI(testrig.URLMustParse("http://fossbros-anonymous.io/d360613a-dc8d-4563-8f0b-b6161caf0f2b")) + update.SetJSONLDId(undoID) + + targetURI := testrig.URLMustParse(receivingAccount.InboxURI) + + signature, digestHeader, dateHeader := testrig.GetSignatureForActivity(update, updatedAccount.PublicKeyURI, updatedAccount.PrivateKey, targetURI) + bodyI, err := streams.Serialize(update) + suite.NoError(err) + + bodyJson, err := json.Marshal(bodyI) + suite.NoError(err) + body := bytes.NewReader(bodyJson) + + tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db) + federator := testrig.NewTestFederator(suite.db, tc, suite.storage) + processor := testrig.NewTestProcessor(suite.db, suite.storage, federator) + userModule := user.New(suite.config, processor, suite.log).(*user.Module) + + // setup request + recorder := httptest.NewRecorder() + ctx, _ := gin.CreateTestContext(recorder) + ctx.Request = httptest.NewRequest(http.MethodPost, targetURI.String(), body) // the endpoint we're hitting + ctx.Request.Header.Set("Signature", signature) + ctx.Request.Header.Set("Date", dateHeader) + ctx.Request.Header.Set("Digest", digestHeader) + ctx.Request.Header.Set("Content-Type", "application/activity+json") + + // we need to pass the context through signature check first to set appropriate values on it + suite.securityModule.SignatureCheck(ctx) + + // normally the router would populate these params from the path values, + // but because we're calling the function directly, we need to set them manually. + ctx.Params = gin.Params{ + gin.Param{ + Key: user.UsernameKey, + Value: receivingAccount.Username, + }, + } + + // trigger the function being tested + userModule.InboxPOSTHandler(ctx) + + result := recorder.Result() + defer result.Body.Close() + b, err := ioutil.ReadAll(result.Body) + suite.NoError(err) + suite.Empty(b) + suite.Equal(http.StatusOK, result.StatusCode) + + // account should be changed in the database now + dbUpdatedAccount, err := suite.db.GetAccountByID(context.Background(), updatedAccount.ID) + suite.NoError(err) + + // displayName should be updated + suite.Equal("updated display name!", dbUpdatedAccount.DisplayName) + + // everything else should be the same as it was before + suite.EqualValues(updatedAccount.Username, dbUpdatedAccount.Username) + suite.EqualValues(updatedAccount.Domain, dbUpdatedAccount.Domain) + suite.EqualValues(updatedAccount.AvatarMediaAttachmentID, dbUpdatedAccount.AvatarMediaAttachmentID) + suite.EqualValues(updatedAccount.AvatarMediaAttachment, dbUpdatedAccount.AvatarMediaAttachment) + suite.EqualValues(updatedAccount.AvatarRemoteURL, dbUpdatedAccount.AvatarRemoteURL) + suite.EqualValues(updatedAccount.HeaderMediaAttachmentID, dbUpdatedAccount.HeaderMediaAttachmentID) + suite.EqualValues(updatedAccount.HeaderMediaAttachment, dbUpdatedAccount.HeaderMediaAttachment) + suite.EqualValues(updatedAccount.HeaderRemoteURL, dbUpdatedAccount.HeaderRemoteURL) + // suite.EqualValues(updatedAccount.Fields, dbUpdatedAccount.Fields) + suite.EqualValues(updatedAccount.Note, dbUpdatedAccount.Note) + suite.EqualValues(updatedAccount.Memorial, dbUpdatedAccount.Memorial) + suite.EqualValues(updatedAccount.AlsoKnownAs, dbUpdatedAccount.AlsoKnownAs) + suite.EqualValues(updatedAccount.MovedToAccountID, dbUpdatedAccount.MovedToAccountID) + suite.EqualValues(updatedAccount.Bot, dbUpdatedAccount.Bot) + suite.EqualValues(updatedAccount.Reason, dbUpdatedAccount.Reason) + suite.EqualValues(updatedAccount.Locked, dbUpdatedAccount.Locked) + suite.EqualValues(updatedAccount.Discoverable, dbUpdatedAccount.Discoverable) + suite.EqualValues(updatedAccount.Privacy, dbUpdatedAccount.Privacy) + suite.EqualValues(updatedAccount.Sensitive, dbUpdatedAccount.Sensitive) + suite.EqualValues(updatedAccount.Language, dbUpdatedAccount.Language) + suite.EqualValues(updatedAccount.URI, dbUpdatedAccount.URI) + suite.EqualValues(updatedAccount.URL, dbUpdatedAccount.URL) + suite.EqualValues(updatedAccount.LastWebfingeredAt, dbUpdatedAccount.LastWebfingeredAt) + suite.EqualValues(updatedAccount.InboxURI, dbUpdatedAccount.InboxURI) + suite.EqualValues(updatedAccount.OutboxURI, dbUpdatedAccount.OutboxURI) + suite.EqualValues(updatedAccount.FollowingURI, dbUpdatedAccount.FollowingURI) + suite.EqualValues(updatedAccount.FollowersURI, dbUpdatedAccount.FollowersURI) + suite.EqualValues(updatedAccount.FeaturedCollectionURI, dbUpdatedAccount.FeaturedCollectionURI) + suite.EqualValues(updatedAccount.ActorType, dbUpdatedAccount.ActorType) + // suite.EqualValues(updatedAccount.PrivateKey, dbUpdatedAccount.PrivateKey) + suite.EqualValues(updatedAccount.PublicKey, dbUpdatedAccount.PublicKey) + suite.EqualValues(updatedAccount.PublicKeyURI, dbUpdatedAccount.PublicKeyURI) + suite.EqualValues(updatedAccount.SensitizedAt, dbUpdatedAccount.SensitizedAt) + suite.EqualValues(updatedAccount.SilencedAt, dbUpdatedAccount.SilencedAt) + suite.EqualValues(updatedAccount.SuspendedAt, dbUpdatedAccount.SuspendedAt) + suite.EqualValues(updatedAccount.HideCollections, dbUpdatedAccount.HideCollections) + suite.EqualValues(updatedAccount.SuspensionOrigin, dbUpdatedAccount.SuspensionOrigin) +} + func TestInboxPostTestSuite(t *testing.T) { suite.Run(t, &InboxPostTestSuite{}) } diff --git a/internal/db/bundb/account_test.go b/internal/db/bundb/account_test.go index 02f94ad40..11e31b731 100644 --- a/internal/db/bundb/account_test.go +++ b/internal/db/bundb/account_test.go @@ -20,10 +20,14 @@ import ( "context" + "crypto/rand" + "crypto/rsa" "testing" "time" "github.com/stretchr/testify/suite" + "github.com/superseriousbusiness/gotosocial/internal/ap" + "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" ) type AccountTestSuite struct { @@ -56,6 +60,34 @@ func (suite *AccountTestSuite) TestUpdateAccount() { suite.WithinDuration(time.Now(), updated.UpdatedAt, 5*time.Second) } +func (suite *AccountTestSuite) TestInsertAccountWithDefaults() { + key, err := rsa.GenerateKey(rand.Reader, 2048) + suite.NoError(err) + + newAccount := >smodel.Account{ + ID: "01FGP5P4VJ9SPFB0T3E36Q60DW", + Username: "test_service", + Domain: "example.org", + URI: "https://example.org/users/test_service", + URL: "https://example.org/@test_service", + ActorType: ap.ActorService, + PublicKey: &key.PublicKey, + PublicKeyURI: "https://example.org/users/test_service#main-key", + } + + err = suite.db.Put(context.Background(), newAccount) + suite.NoError(err) + + suite.Equal("en", newAccount.Language) + suite.WithinDuration(time.Now(), newAccount.CreatedAt, 30*time.Second) + suite.WithinDuration(time.Now(), newAccount.UpdatedAt, 30*time.Second) + suite.False(newAccount.Memorial) + suite.False(newAccount.Bot) + suite.False(newAccount.Discoverable) + suite.False(newAccount.Sensitive) + suite.False(newAccount.HideCollections) +} + func TestAccountTestSuite(t *testing.T) { suite.Run(t, new(AccountTestSuite)) } diff --git a/internal/federation/federatingdb/federating_db_test.go b/internal/federation/federatingdb/federatingdb_test.go similarity index 100% rename from internal/federation/federatingdb/federating_db_test.go rename to internal/federation/federatingdb/federatingdb_test.go diff --git a/internal/federation/federatingdb/update.go b/internal/federation/federatingdb/update.go index 2bcf2533c..5dec2bd69 100644 --- a/internal/federation/federatingdb/update.go +++ b/internal/federation/federatingdb/update.go @@ -98,8 +98,7 @@ func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error { typeName == ap.ActorService { // it's an UPDATE to some kind of account var accountable ap.Accountable - - switch asType.GetTypeName() { + switch typeName { case ap.ActorApplication: l.Debug("got update for APPLICATION") i, ok := asType.(vocab.ActivityStreamsApplication) @@ -152,19 +151,24 @@ func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error { return fmt.Errorf("UPDATE: update for account %s was requested by account %s, this is not valid", updatedAcct.URI, requestingAcct.URI) } - updatedAcct.ID = requestingAcct.ID // set this here so the db will update properly instead of trying to PUT this and getting constraint issues + // set some fields here on the updatedAccount representation so we don't run into db issues + updatedAcct.CreatedAt = requestingAcct.CreatedAt + updatedAcct.ID = requestingAcct.ID + updatedAcct.Language = requestingAcct.Language + + // do the update updatedAcct, err = f.db.UpdateAccount(ctx, updatedAcct) if err != nil { return fmt.Errorf("UPDATE: database error inserting updated account: %s", err) } + // pass to the processor for further processing of eg., avatar/header fromFederatorChan <- messages.FromFederator{ APObjectType: ap.ObjectProfile, APActivityType: ap.ActivityUpdate, GTSModel: updatedAcct, ReceivingAccount: targetAcct, } - } return nil diff --git a/internal/processing/fromfederator.go b/internal/processing/fromfederator.go index d0e340b5e..feb22b2ba 100644 --- a/internal/processing/fromfederator.go +++ b/internal/processing/fromfederator.go @@ -22,7 +22,6 @@ "context" "errors" "fmt" - "net/url" "github.com/sirupsen/logrus" "github.com/superseriousbusiness/gotosocial/internal/ap" @@ -141,13 +140,8 @@ func (p *processor) ProcessFromFederator(ctx context.Context, federatorMsg messa return errors.New("profile was not parseable as *gtsmodel.Account") } - incomingAccountURI, err := url.Parse(incomingAccount.URI) - if err != nil { - return err - } - - if _, _, err := p.federator.GetRemoteAccount(ctx, federatorMsg.ReceivingAccount.Username, incomingAccountURI, true); err != nil { - return fmt.Errorf("error dereferencing account from federator: %s", err) + if _, err := p.federator.EnrichRemoteAccount(ctx, federatorMsg.ReceivingAccount.Username, incomingAccount); err != nil { + return fmt.Errorf("error enriching updated account from federator: %s", err) } } case ap.ActivityDelete: