commit cb54324430e7c4640a7aa5aac24932b5e91b71c4
parent 87177d840b9703f572392ef4bd0f5013fd5c3a77
Author: tsmethurst <tobi.smethurst@klarrio.com>
Date: Fri, 28 May 2021 22:47:18 +0200
federate account updates
Diffstat:
7 files changed, 135 insertions(+), 3 deletions(-)
diff --git a/internal/federation/federatingdb/update.go b/internal/federation/federatingdb/update.go
@@ -120,6 +120,12 @@ func (f *federatingDB) Update(ctx context.Context, asType vocab.Type) error {
return fmt.Errorf("error converting to account: %s", err)
}
+ if updatedAcct.Domain == f.config.Host {
+ // no need to update local accounts
+ // in fact, if we do this will break the shit out of things so do NOT
+ return nil
+ }
+
if requestingAcct.URI != updatedAcct.URI {
return fmt.Errorf("update for account %s was requested by account %s, this is not valid", updatedAcct.URI, requestingAcct.URI)
}
diff --git a/internal/federation/federatingdb/util.go b/internal/federation/federatingdb/util.go
@@ -143,6 +143,19 @@ func (f *federatingDB) NewID(c context.Context, t vocab.Type) (id *url.URL, err
return idProp.GetIRI(), nil
}
}
+ case gtsmodel.ActivityStreamsUpdate:
+ // UPDATE
+ // ID might already be set on an update we've created, so check it here and return it if it is
+ update, ok := t.(vocab.ActivityStreamsUpdate)
+ if !ok {
+ return nil, errors.New("newid: fave couldn't be parsed into vocab.ActivityStreamsUpdate")
+ }
+ idProp := update.GetJSONLDId()
+ if idProp != nil {
+ if idProp.IsIRI() {
+ return idProp.GetIRI(), nil
+ }
+ }
}
// fallback default behavior: just return a random UUID after our protocol and host
diff --git a/internal/message/accountprocess.go b/internal/message/accountprocess.go
@@ -188,6 +188,13 @@ func (p *processor) AccountUpdate(authed *oauth.Auth, form *apimodel.UpdateCrede
return nil, fmt.Errorf("could not fetch updated account %s: %s", authed.Account.ID, err)
}
+ p.fromClientAPI <- gtsmodel.FromClientAPI{
+ APObjectType: gtsmodel.ActivityStreamsProfile,
+ APActivityType: gtsmodel.ActivityStreamsUpdate,
+ GTSModel: updatedAccount,
+ OriginAccount: updatedAccount,
+ }
+
acctSensitive, err := p.tc.AccountToMastoSensitive(updatedAccount)
if err != nil {
return nil, fmt.Errorf("could not convert account into mastosensitive account: %s", err)
diff --git a/internal/message/fromclientapiprocess.go b/internal/message/fromclientapiprocess.go
@@ -88,6 +88,16 @@ func (p *processor) processFromClientAPI(clientMsg gtsmodel.FromClientAPI) error
}
case gtsmodel.ActivityStreamsUpdate:
// UPDATE
+ switch clientMsg.APObjectType {
+ case gtsmodel.ActivityStreamsProfile, gtsmodel.ActivityStreamsPerson:
+ // UPDATE ACCOUNT/PROFILE
+ account, ok := clientMsg.GTSModel.(*gtsmodel.Account)
+ if !ok {
+ return errors.New("account was not parseable as *gtsmodel.Account")
+ }
+
+ return p.federateAccountUpdate(account, clientMsg.OriginAccount)
+ }
case gtsmodel.ActivityStreamsAccept:
// ACCEPT
switch clientMsg.APObjectType {
@@ -277,7 +287,27 @@ func (p *processor) federateAnnounce(boostWrapperStatus *gtsmodel.Status, boosti
if err != nil {
return fmt.Errorf("federateAnnounce: error parsing outboxURI %s: %s", boostingAccount.OutboxURI, err)
}
-
+
_, err = p.federator.FederatingActor().Send(context.Background(), outboxIRI, announce)
return err
}
+
+func (p *processor) federateAccountUpdate(updatedAccount *gtsmodel.Account, originAccount *gtsmodel.Account) error {
+ person, err := p.tc.AccountToAS(updatedAccount)
+ if err != nil {
+ return fmt.Errorf("federateAccountUpdate: error converting account to person: %s", err)
+ }
+
+ update, err := p.tc.WrapPersonInUpdate(person, originAccount)
+ if err != nil {
+ return fmt.Errorf("federateAccountUpdate: error wrapping person in update: %s", err)
+ }
+
+ outboxIRI, err := url.Parse(originAccount.OutboxURI)
+ if err != nil {
+ return fmt.Errorf("federateAnnounce: error parsing outboxURI %s: %s", originAccount.OutboxURI, err)
+ }
+
+ _, err = p.federator.FederatingActor().Send(context.Background(), outboxIRI, update)
+ return err
+}
diff --git a/internal/typeutils/converter.go b/internal/typeutils/converter.go
@@ -141,6 +141,13 @@ type TypeConverter interface {
FollowRequestToFollow(f *gtsmodel.FollowRequest) *gtsmodel.Follow
// StatusToBoost wraps the given status into a boosting status.
StatusToBoost(s *gtsmodel.Status, boostingAccount *gtsmodel.Account) (*gtsmodel.Status, error)
+
+ /*
+ WRAPPER CONVENIENCE FUNCTIONS
+ */
+
+ // WrapPersonInUpdate
+ WrapPersonInUpdate(person vocab.ActivityStreamsPerson, originAccount *gtsmodel.Account) (vocab.ActivityStreamsUpdate, error)
}
type converter struct {
diff --git a/internal/typeutils/wrap.go b/internal/typeutils/wrap.go
@@ -0,0 +1,61 @@
+package typeutils
+
+import (
+ "fmt"
+ "net/url"
+
+ "github.com/go-fed/activity/streams"
+ "github.com/go-fed/activity/streams/vocab"
+ "github.com/google/uuid"
+ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
+ "github.com/superseriousbusiness/gotosocial/internal/util"
+)
+
+func (c *converter) WrapPersonInUpdate(person vocab.ActivityStreamsPerson, originAccount *gtsmodel.Account) (vocab.ActivityStreamsUpdate, error) {
+
+ update := streams.NewActivityStreamsUpdate()
+
+ // set the actor
+ actorURI, err := url.Parse(originAccount.URI)
+ if err != nil {
+ return nil, fmt.Errorf("WrapPersonInUpdate: error parsing url %s: %s", originAccount.URI, err)
+ }
+ actorProp := streams.NewActivityStreamsActorProperty()
+ actorProp.AppendIRI(actorURI)
+ update.SetActivityStreamsActor(actorProp)
+
+ // set the ID
+ idString := util.GenerateURIForUpdate(originAccount.Username, c.config.Protocol, c.config.Host, uuid.NewString())
+ idURI, err := url.Parse(idString)
+ if err != nil {
+ return nil, fmt.Errorf("WrapPersonInUpdate: error parsing url %s: %s", idString, err)
+ }
+ idProp := streams.NewJSONLDIdProperty()
+ idProp.SetIRI(idURI)
+ update.SetJSONLDId(idProp)
+
+ // set the person as the object here
+ objectProp := streams.NewActivityStreamsObjectProperty()
+ objectProp.AppendActivityStreamsPerson(person)
+ update.SetActivityStreamsObject(objectProp)
+
+ // to should be public
+ toURI, err := url.Parse(asPublicURI)
+ if err != nil {
+ return nil, fmt.Errorf("WrapPersonInUpdate: error parsing url %s: %s", asPublicURI, err)
+ }
+ toProp := streams.NewActivityStreamsToProperty()
+ toProp.AppendIRI(toURI)
+ update.SetActivityStreamsTo(toProp)
+
+ // bcc followers
+ followersURI, err := url.Parse(originAccount.FollowersURI)
+ if err != nil {
+ return nil, fmt.Errorf("WrapPersonInUpdate: error parsing url %s: %s", originAccount.FollowersURI, err)
+ }
+ bccProp := streams.NewActivityStreamsBccProperty()
+ bccProp.AppendIRI(followersURI)
+ update.SetActivityStreamsBcc(bccProp)
+
+ return update, nil
+}
diff --git a/internal/util/uri.go b/internal/util/uri.go
@@ -49,6 +49,8 @@ const (
PublicKeyPath = "main-key"
// FollowPath used to generate the URI for an individual follow or follow request
FollowPath = "follow"
+ // UpdatePath is used to generate the URI for an account update
+ UpdatePath = "updates"
)
// APContextKey is a type used specifically for settings values on contexts within go-fed AP request chains
@@ -108,13 +110,19 @@ type UserURIs struct {
// GenerateURIForFollow returns the AP URI for a new follow -- something like:
// https://example.org/users/whatever_user/follow/41c7f33f-1060-48d9-84df-38dcb13cf0d8
func GenerateURIForFollow(username string, protocol string, host string, thisFollowID string) string {
- return fmt.Sprintf("%s://%s/%s/%s/%s", protocol, host, UsersPath, FollowPath, thisFollowID)
+ return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, FollowPath, thisFollowID)
}
// GenerateURIForFollow returns the AP URI for a new like/fave -- something like:
// https://example.org/users/whatever_user/liked/41c7f33f-1060-48d9-84df-38dcb13cf0d8
func GenerateURIForLike(username string, protocol string, host string, thisFavedID string) string {
- return fmt.Sprintf("%s://%s/%s/%s/%s", protocol, host, UsersPath, LikedPath, thisFavedID)
+ return fmt.Sprintf("%s://%s/%s/%s/%s/%s", protocol, host, UsersPath, username, LikedPath, thisFavedID)
+}
+
+// GenerateURIForUpdate returns the AP URI for a new update activity -- something like:
+// https://example.org/users/whatever_user#updates/41c7f33f-1060-48d9-84df-38dcb13cf0d8
+func GenerateURIForUpdate(username string, protocol string, host string, thisUpdateID string) string {
+ return fmt.Sprintf("%s://%s/%s/%s#%s/%s", protocol, host, UsersPath, username, UpdatePath, thisUpdateID)
}
// GenerateURIsForAccount throws together a bunch of URIs for the given username, with the given protocol and host.