[chore] de-interface{} the federator and dereferencer structs (#2285)

* de-interface{} the federator and dereferencer structs

* fix broken type signatures
This commit is contained in:
kim 2023-10-23 10:58:13 +01:00 committed by GitHub
parent 3dcc94940d
commit 69ba9a79a1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 151 additions and 199 deletions

View file

@ -45,7 +45,7 @@ type EmojiGetTestSuite struct {
db db.DB db db.DB
tc *typeutils.Converter tc *typeutils.Converter
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
emailSender email.Sender emailSender email.Sender
processor *processing.Processor processor *processing.Processor
storage *storage.Driver storage *storage.Driver

View file

@ -41,7 +41,7 @@ type UserStandardTestSuite struct {
db db.DB db db.DB
tc *typeutils.Converter tc *typeutils.Converter
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
emailSender email.Sender emailSender email.Sender
processor *processing.Processor processor *processing.Processor
storage *storage.Driver storage *storage.Driver

View file

@ -47,7 +47,7 @@ type AuthStandardTestSuite struct {
storage *storage.Driver storage *storage.Driver
state state.State state state.State
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
processor *processing.Processor processor *processing.Processor
emailSender email.Sender emailSender email.Sender
idp oidc.IDP idp oidc.IDP

View file

@ -47,7 +47,7 @@ type AccountStandardTestSuite struct {
db db.DB db db.DB
storage *storage.Driver storage *storage.Driver
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
processor *processing.Processor processor *processing.Processor
emailSender email.Sender emailSender email.Sender
sentEmails map[string]string sentEmails map[string]string

View file

@ -47,7 +47,7 @@ type AdminStandardTestSuite struct {
db db.DB db db.DB
storage *storage.Driver storage *storage.Driver
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
processor *processing.Processor processor *processing.Processor
emailSender email.Sender emailSender email.Sender
sentEmails map[string]string sentEmails map[string]string

View file

@ -52,7 +52,7 @@ type BookmarkTestSuite struct {
db db.DB db db.DB
tc *typeutils.Converter tc *typeutils.Converter
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
emailSender email.Sender emailSender email.Sender
processor *processing.Processor processor *processing.Processor
storage *storage.Driver storage *storage.Driver

View file

@ -39,7 +39,7 @@ type FavouritesStandardTestSuite struct {
db db.DB db db.DB
tc *typeutils.Converter tc *typeutils.Converter
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
emailSender email.Sender emailSender email.Sender
processor *processing.Processor processor *processing.Processor
storage *storage.Driver storage *storage.Driver

View file

@ -45,7 +45,7 @@ type FollowRequestStandardTestSuite struct {
db db.DB db db.DB
storage *storage.Driver storage *storage.Driver
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
processor *processing.Processor processor *processing.Processor
emailSender email.Sender emailSender email.Sender
state state.State state state.State

View file

@ -46,7 +46,7 @@ type InstanceStandardTestSuite struct {
db db.DB db db.DB
storage *storage.Driver storage *storage.Driver
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
processor *processing.Processor processor *processing.Processor
emailSender email.Sender emailSender email.Sender
sentEmails map[string]string sentEmails map[string]string

View file

@ -39,7 +39,7 @@ type ListsStandardTestSuite struct {
db db.DB db db.DB
storage *storage.Driver storage *storage.Driver
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
processor *processing.Processor processor *processing.Processor
emailSender email.Sender emailSender email.Sender
state state.State state state.State

View file

@ -55,7 +55,7 @@ type MediaCreateTestSuite struct {
db db.DB db db.DB
storage *storage.Driver storage *storage.Driver
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
tc *typeutils.Converter tc *typeutils.Converter
oauthServer oauth.Server oauthServer oauth.Server
emailSender email.Sender emailSender email.Sender

View file

@ -51,7 +51,7 @@ type MediaUpdateTestSuite struct {
suite.Suite suite.Suite
db db.DB db db.DB
storage *storage.Driver storage *storage.Driver
federator federation.Federator federator *federation.Federator
tc *typeutils.Converter tc *typeutils.Converter
mediaManager *media.Manager mediaManager *media.Manager
oauthServer oauth.Server oauthServer oauth.Server

View file

@ -38,7 +38,7 @@ type ReportsStandardTestSuite struct {
db db.DB db db.DB
storage *storage.Driver storage *storage.Driver
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
processor *processing.Processor processor *processing.Processor
emailSender email.Sender emailSender email.Sender
sentEmails map[string]string sentEmails map[string]string

View file

@ -46,7 +46,7 @@ type SearchStandardTestSuite struct {
db db.DB db db.DB
storage *storage.Driver storage *storage.Driver
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
processor *processing.Processor processor *processing.Processor
emailSender email.Sender emailSender email.Sender
sentEmails map[string]string sentEmails map[string]string

View file

@ -39,7 +39,7 @@ type StatusStandardTestSuite struct {
db db.DB db db.DB
tc *typeutils.Converter tc *typeutils.Converter
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
emailSender email.Sender emailSender email.Sender
processor *processing.Processor processor *processing.Processor
storage *storage.Driver storage *storage.Driver

View file

@ -51,7 +51,7 @@ type StreamingTestSuite struct {
db db.DB db db.DB
tc *typeutils.Converter tc *typeutils.Converter
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
emailSender email.Sender emailSender email.Sender
processor *processing.Processor processor *processing.Processor
storage *storage.Driver storage *storage.Driver

View file

@ -38,7 +38,7 @@ type UserStandardTestSuite struct {
db db.DB db db.DB
tc *typeutils.Converter tc *typeutils.Converter
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
emailSender email.Sender emailSender email.Sender
processor *processing.Processor processor *processing.Processor
storage *storage.Driver storage *storage.Driver

View file

@ -41,7 +41,7 @@ type FileserverTestSuite struct {
db db.DB db db.DB
storage *storage.Driver storage *storage.Driver
state state.State state state.State
federator federation.Federator federator *federation.Federator
tc *typeutils.Converter tc *typeutils.Converter
processor *processing.Processor processor *processing.Processor
mediaManager *media.Manager mediaManager *media.Manager

View file

@ -41,7 +41,7 @@ type WebfingerStandardTestSuite struct {
state state.State state state.State
tc *typeutils.Converter tc *typeutils.Converter
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
emailSender email.Sender emailSender email.Sender
processor *processing.Processor processor *processing.Processor
storage *storage.Driver storage *storage.Driver

View file

@ -109,7 +109,7 @@ type PubKeyAuth struct {
// Also note that this function *does not* dereference the remote account that // Also note that this function *does not* dereference the remote account that
// the signature key is associated with. Other functions should use the returned // the signature key is associated with. Other functions should use the returned
// URL to dereference the remote account, if required. // URL to dereference the remote account, if required.
func (f *federator) AuthenticateFederatedRequest(ctx context.Context, requestedUsername string) (*PubKeyAuth, gtserror.WithCode) { func (f *Federator) AuthenticateFederatedRequest(ctx context.Context, requestedUsername string) (*PubKeyAuth, gtserror.WithCode) {
// Thanks to the signature check middleware, // Thanks to the signature check middleware,
// we should already have an http signature // we should already have an http signature
// verifier set on the context. If we don't, // verifier set on the context. If we don't,
@ -215,7 +215,7 @@ func (f *federator) AuthenticateFederatedRequest(ctx context.Context, requestedU
// //
// In case an entry for the pubKey owner just doesn't // In case an entry for the pubKey owner just doesn't
// exist in the db (yet), will return nil, nil. // exist in the db (yet), will return nil, nil.
func (f *federator) derefPubKeyDBOnly( func (f *Federator) derefPubKeyDBOnly(
ctx context.Context, ctx context.Context,
pubKeyIDStr string, pubKeyIDStr string,
) (*PubKeyAuth, gtserror.WithCode) { ) (*PubKeyAuth, gtserror.WithCode) {
@ -248,7 +248,7 @@ func (f *federator) derefPubKeyDBOnly(
// checking in the database, and then (if no entry found, or entry // checking in the database, and then (if no entry found, or entry
// found but pubKey expired) calling the remote pub key URI and // found but pubKey expired) calling the remote pub key URI and
// extracting the key. // extracting the key.
func (f *federator) derefPubKey( func (f *Federator) derefPubKey(
ctx context.Context, ctx context.Context,
requestedUsername string, requestedUsername string,
pubKeyIDStr string, pubKeyIDStr string,
@ -363,7 +363,7 @@ func (f *federator) derefPubKey(
// callForPubKey handles the nitty gritty of actually // callForPubKey handles the nitty gritty of actually
// making a request for the given pubKeyID with a // making a request for the given pubKeyID with a
// transport created on behalf of requestedUsername. // transport created on behalf of requestedUsername.
func (f *federator) callForPubKey( func (f *Federator) callForPubKey(
ctx context.Context, ctx context.Context,
requestedUsername string, requestedUsername string,
pubKeyID *url.URL, pubKeyID *url.URL,

View file

@ -51,7 +51,7 @@ import (
// Finally, if the authentication and authorization succeeds, then // Finally, if the authentication and authorization succeeds, then
// authenticated must be true and error nil. The request will continue // authenticated must be true and error nil. The request will continue
// to be processed. // to be processed.
func (f *federator) AuthenticateGetInbox(ctx context.Context, w http.ResponseWriter, r *http.Request) (context.Context, bool, error) { func (f *Federator) AuthenticateGetInbox(ctx context.Context, w http.ResponseWriter, r *http.Request) (context.Context, bool, error) {
// IMPLEMENTATION NOTE: For GoToSocial, we serve GETS to outboxes and inboxes through // IMPLEMENTATION NOTE: For GoToSocial, we serve GETS to outboxes and inboxes through
// the CLIENT API, not through the federation API, so we just do nothing here. // the CLIENT API, not through the federation API, so we just do nothing here.
return ctx, false, nil return ctx, false, nil
@ -76,7 +76,7 @@ func (f *federator) AuthenticateGetInbox(ctx context.Context, w http.ResponseWri
// Finally, if the authentication and authorization succeeds, then // Finally, if the authentication and authorization succeeds, then
// authenticated must be true and error nil. The request will continue // authenticated must be true and error nil. The request will continue
// to be processed. // to be processed.
func (f *federator) AuthenticateGetOutbox(ctx context.Context, w http.ResponseWriter, r *http.Request) (context.Context, bool, error) { func (f *Federator) AuthenticateGetOutbox(ctx context.Context, w http.ResponseWriter, r *http.Request) (context.Context, bool, error) {
// IMPLEMENTATION NOTE: For GoToSocial, we serve GETS to outboxes and inboxes through // IMPLEMENTATION NOTE: For GoToSocial, we serve GETS to outboxes and inboxes through
// the CLIENT API, not through the federation API, so we just do nothing here. // the CLIENT API, not through the federation API, so we just do nothing here.
return ctx, false, nil return ctx, false, nil
@ -90,7 +90,7 @@ func (f *federator) AuthenticateGetOutbox(ctx context.Context, w http.ResponseWr
// //
// Always called, regardless whether the Federated Protocol or Social // Always called, regardless whether the Federated Protocol or Social
// API is enabled. // API is enabled.
func (f *federator) GetOutbox(ctx context.Context, r *http.Request) (vocab.ActivityStreamsOrderedCollectionPage, error) { func (f *Federator) GetOutbox(ctx context.Context, r *http.Request) (vocab.ActivityStreamsOrderedCollectionPage, error) {
// IMPLEMENTATION NOTE: For GoToSocial, we serve GETS to outboxes and inboxes through // IMPLEMENTATION NOTE: For GoToSocial, we serve GETS to outboxes and inboxes through
// the CLIENT API, not through the federation API, so we just do nothing here. // the CLIENT API, not through the federation API, so we just do nothing here.
return streams.NewActivityStreamsOrderedCollectionPage(), nil return streams.NewActivityStreamsOrderedCollectionPage(), nil

View file

@ -60,8 +60,10 @@ func accountUpToDate(account *gtsmodel.Account) bool {
return false return false
} }
// GetAccountByURI: implements Dereferencer{}.GetAccountByURI. // GetAccountByURI will attempt to fetch an accounts by its URI, first checking the database. In the case of a newly-met remote model, or a remote model
func (d *deref) GetAccountByURI(ctx context.Context, requestUser string, uri *url.URL) (*gtsmodel.Account, ap.Accountable, error) { // whose last_fetched date is beyond a certain interval, the account will be dereferenced. In the case of dereferencing, some low-priority account information
// may be enqueued for asynchronous fetching, e.g. featured account statuses (pins). An ActivityPub object indicates the account was dereferenced.
func (d *Dereferencer) GetAccountByURI(ctx context.Context, requestUser string, uri *url.URL) (*gtsmodel.Account, ap.Accountable, error) {
// Fetch and dereference account if necessary. // Fetch and dereference account if necessary.
account, apubAcc, err := d.getAccountByURI(ctx, account, apubAcc, err := d.getAccountByURI(ctx,
requestUser, requestUser,
@ -84,7 +86,7 @@ func (d *deref) GetAccountByURI(ctx context.Context, requestUser string, uri *ur
} }
// getAccountByURI is a package internal form of .GetAccountByURI() that doesn't bother dereferencing featured posts on update. // getAccountByURI is a package internal form of .GetAccountByURI() that doesn't bother dereferencing featured posts on update.
func (d *deref) getAccountByURI(ctx context.Context, requestUser string, uri *url.URL) (*gtsmodel.Account, ap.Accountable, error) { func (d *Dereferencer) getAccountByURI(ctx context.Context, requestUser string, uri *url.URL) (*gtsmodel.Account, ap.Accountable, error) {
var ( var (
account *gtsmodel.Account account *gtsmodel.Account
uriStr = uri.String() uriStr = uri.String()
@ -157,8 +159,10 @@ func (d *deref) getAccountByURI(ctx context.Context, requestUser string, uri *ur
return latest, apubAcc, nil return latest, apubAcc, nil
} }
// GetAccountByUsernameDomain: implements Dereferencer{}.GetAccountByUsernameDomain. // GetAccountByUsernameDomain will attempt to fetch an accounts by its username@domain, first checking the database. In the case of a newly-met remote model,
func (d *deref) GetAccountByUsernameDomain(ctx context.Context, requestUser string, username string, domain string) (*gtsmodel.Account, ap.Accountable, error) { // or a remote model whose last_fetched date is beyond a certain interval, the account will be dereferenced. In the case of dereferencing, some low-priority
// account information may be enqueued for asynchronous fetching, e.g. featured account statuses (pins). An ActivityPub object indicates the account was dereferenced.
func (d *Dereferencer) GetAccountByUsernameDomain(ctx context.Context, requestUser string, username string, domain string) (*gtsmodel.Account, ap.Accountable, error) {
if domain == config.GetHost() || domain == config.GetAccountDomain() { if domain == config.GetHost() || domain == config.GetAccountDomain() {
// We do local lookups using an empty domain, // We do local lookups using an empty domain,
// else it will fail the db search below. // else it will fail the db search below.
@ -224,8 +228,10 @@ func (d *deref) GetAccountByUsernameDomain(ctx context.Context, requestUser stri
return latest, apubAcc, nil return latest, apubAcc, nil
} }
// RefreshAccount: implements Dereferencer{}.RefreshAccount. // RefreshAccount updates the given account if remote and last_fetched is beyond fetch interval, or if force is set. An updated account model is returned,
func (d *deref) RefreshAccount(ctx context.Context, requestUser string, account *gtsmodel.Account, apubAcc ap.Accountable, force bool) (*gtsmodel.Account, ap.Accountable, error) { // but in the case of dereferencing, some low-priority account information may be enqueued for asynchronous fetching, e.g. featured account statuses (pins).
// An ActivityPub object indicates the account was dereferenced (i.e. updated).
func (d *Dereferencer) RefreshAccount(ctx context.Context, requestUser string, account *gtsmodel.Account, apubAcc ap.Accountable, force bool) (*gtsmodel.Account, ap.Accountable, error) {
// Check whether needs update (and not forced). // Check whether needs update (and not forced).
if accountUpToDate(account) && !force { if accountUpToDate(account) && !force {
return account, nil, nil return account, nil, nil
@ -264,8 +270,9 @@ func (d *deref) RefreshAccount(ctx context.Context, requestUser string, account
return latest, apubAcc, nil return latest, apubAcc, nil
} }
// RefreshAccountAsync: implements Dereferencer{}.RefreshAccountAsync. // RefreshAccountAsync enqueues the given account for an asychronous update fetching, if last_fetched is beyond fetch interval, or if forcc is set.
func (d *deref) RefreshAccountAsync(ctx context.Context, requestUser string, account *gtsmodel.Account, apubAcc ap.Accountable, force bool) { // This is a more optimized form of manually enqueueing .UpdateAccount() to the federation worker, since it only enqueues update if necessary.
func (d *Dereferencer) RefreshAccountAsync(ctx context.Context, requestUser string, account *gtsmodel.Account, apubAcc ap.Accountable, force bool) {
// Check whether needs update (and not forced). // Check whether needs update (and not forced).
if accountUpToDate(account) && !force { if accountUpToDate(account) && !force {
return return
@ -294,7 +301,7 @@ func (d *deref) RefreshAccountAsync(ctx context.Context, requestUser string, acc
} }
// enrichAccount will enrich the given account, whether a new barebones model, or existing model from the database. It handles necessary dereferencing, webfingering etc. // enrichAccount will enrich the given account, whether a new barebones model, or existing model from the database. It handles necessary dereferencing, webfingering etc.
func (d *deref) enrichAccount(ctx context.Context, requestUser string, uri *url.URL, account *gtsmodel.Account, apubAcc ap.Accountable) (*gtsmodel.Account, ap.Accountable, error) { func (d *Dereferencer) enrichAccount(ctx context.Context, requestUser string, uri *url.URL, account *gtsmodel.Account, apubAcc ap.Accountable) (*gtsmodel.Account, ap.Accountable, error) {
// Pre-fetch a transport for requesting username, used by later deref procedures. // Pre-fetch a transport for requesting username, used by later deref procedures.
tsport, err := d.transportController.NewTransportForUsername(ctx, requestUser) tsport, err := d.transportController.NewTransportForUsername(ctx, requestUser)
if err != nil { if err != nil {
@ -472,7 +479,7 @@ func (d *deref) enrichAccount(ctx context.Context, requestUser string, uri *url.
return latestAcc, apubAcc, nil return latestAcc, apubAcc, nil
} }
func (d *deref) fetchRemoteAccountAvatar(ctx context.Context, tsport transport.Transport, existing, latestAcc *gtsmodel.Account) error { func (d *Dereferencer) fetchRemoteAccountAvatar(ctx context.Context, tsport transport.Transport, existing, latestAcc *gtsmodel.Account) error {
if latestAcc.AvatarRemoteURL == "" { if latestAcc.AvatarRemoteURL == "" {
// No avatar set on newest model, leave // No avatar set on newest model, leave
// latest avatar attachment ID empty. // latest avatar attachment ID empty.
@ -562,7 +569,7 @@ func (d *deref) fetchRemoteAccountAvatar(ctx context.Context, tsport transport.T
return nil return nil
} }
func (d *deref) fetchRemoteAccountHeader(ctx context.Context, tsport transport.Transport, existing, latestAcc *gtsmodel.Account) error { func (d *Dereferencer) fetchRemoteAccountHeader(ctx context.Context, tsport transport.Transport, existing, latestAcc *gtsmodel.Account) error {
if latestAcc.HeaderRemoteURL == "" { if latestAcc.HeaderRemoteURL == "" {
// No header set on newest model, leave // No header set on newest model, leave
// latest header attachment ID empty. // latest header attachment ID empty.
@ -652,7 +659,7 @@ func (d *deref) fetchRemoteAccountHeader(ctx context.Context, tsport transport.T
return nil return nil
} }
func (d *deref) fetchRemoteAccountEmojis(ctx context.Context, targetAccount *gtsmodel.Account, requestingUsername string) (bool, error) { func (d *Dereferencer) fetchRemoteAccountEmojis(ctx context.Context, targetAccount *gtsmodel.Account, requestingUsername string) (bool, error) {
maybeEmojis := targetAccount.Emojis maybeEmojis := targetAccount.Emojis
maybeEmojiIDs := targetAccount.EmojiIDs maybeEmojiIDs := targetAccount.EmojiIDs
@ -766,7 +773,7 @@ func (d *deref) fetchRemoteAccountEmojis(ctx context.Context, targetAccount *gts
// dereferenceAccountFeatured dereferences an account's featuredCollectionURI (if not empty). For each discovered status, this status will // dereferenceAccountFeatured dereferences an account's featuredCollectionURI (if not empty). For each discovered status, this status will
// be dereferenced (if necessary) and marked as pinned (if necessary). Then, old pins will be removed if they're not included in new pins. // be dereferenced (if necessary) and marked as pinned (if necessary). Then, old pins will be removed if they're not included in new pins.
func (d *deref) dereferenceAccountFeatured(ctx context.Context, requestUser string, account *gtsmodel.Account) error { func (d *Dereferencer) dereferenceAccountFeatured(ctx context.Context, requestUser string, account *gtsmodel.Account) error {
uri, err := url.Parse(account.FeaturedCollectionURI) uri, err := url.Parse(account.FeaturedCollectionURI)
if err != nil { if err != nil {
return err return err

View file

@ -27,7 +27,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
func (d *deref) DereferenceAnnounce(ctx context.Context, announce *gtsmodel.Status, requestingUsername string) error { func (d *Dereferencer) DereferenceAnnounce(ctx context.Context, announce *gtsmodel.Status, requestingUsername string) error {
if announce.BoostOf == nil { if announce.BoostOf == nil {
// we can't do anything unfortunately // we can't do anything unfortunately
return errors.New("DereferenceAnnounce: no URI to dereference") return errors.New("DereferenceAnnounce: no URI to dereference")

View file

@ -30,7 +30,7 @@ import (
) )
// dereferenceCollectionPage returns the activitystreams CollectionPage at the specified IRI, or an error if something goes wrong. // dereferenceCollectionPage returns the activitystreams CollectionPage at the specified IRI, or an error if something goes wrong.
func (d *deref) dereferenceCollectionPage(ctx context.Context, username string, pageIRI *url.URL) (ap.CollectionPageIterator, error) { func (d *Dereferencer) dereferenceCollectionPage(ctx context.Context, username string, pageIRI *url.URL) (ap.CollectionPageIterator, error) {
if blocked, err := d.state.DB.IsDomainBlocked(ctx, pageIRI.Host); blocked || err != nil { if blocked, err := d.state.DB.IsDomainBlocked(ctx, pageIRI.Host); blocked || err != nil {
return nil, gtserror.Newf("domain %s is blocked", pageIRI.Host) return nil, gtserror.Newf("domain %s is blocked", pageIRI.Host)
} }

View file

@ -18,72 +18,19 @@
package dereferencing package dereferencing
import ( import (
"context"
"net/url" "net/url"
"sync" "sync"
"codeberg.org/gruf/go-mutexes" "codeberg.org/gruf/go-mutexes"
"github.com/superseriousbusiness/gotosocial/internal/ap"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/transport"
"github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/internal/typeutils"
) )
// Dereferencer wraps logic and functionality for doing dereferencing of remote accounts, statuses, etc, from federated instances. // Dereferencer wraps logic and functionality for doing dereferencing
type Dereferencer interface { // of remote accounts, statuses, etc, from federated instances.
// GetAccountByURI will attempt to fetch an accounts by its URI, first checking the database. In the case of a newly-met remote model, or a remote model type Dereferencer struct {
// whose last_fetched date is beyond a certain interval, the account will be dereferenced. In the case of dereferencing, some low-priority account information
// may be enqueued for asynchronous fetching, e.g. featured account statuses (pins). An ActivityPub object indicates the account was dereferenced.
GetAccountByURI(ctx context.Context, requestUser string, uri *url.URL) (*gtsmodel.Account, ap.Accountable, error)
// GetAccountByUsernameDomain will attempt to fetch an accounts by its username@domain, first checking the database. In the case of a newly-met remote model,
// or a remote model whose last_fetched date is beyond a certain interval, the account will be dereferenced. In the case of dereferencing, some low-priority
// account information may be enqueued for asynchronous fetching, e.g. featured account statuses (pins). An ActivityPub object indicates the account was dereferenced.
GetAccountByUsernameDomain(ctx context.Context, requestUser string, username string, domain string) (*gtsmodel.Account, ap.Accountable, error)
// RefreshAccount updates the given account if remote and last_fetched is beyond fetch interval, or if force is set. An updated account model is returned,
// but in the case of dereferencing, some low-priority account information may be enqueued for asynchronous fetching, e.g. featured account statuses (pins).
// An ActivityPub object indicates the account was dereferenced (i.e. updated).
RefreshAccount(ctx context.Context, requestUser string, account *gtsmodel.Account, apubAcc ap.Accountable, force bool) (*gtsmodel.Account, ap.Accountable, error)
// RefreshAccountAsync enqueues the given account for an asychronous update fetching, if last_fetched is beyond fetch interval, or if forcc is set.
// This is a more optimized form of manually enqueueing .UpdateAccount() to the federation worker, since it only enqueues update if necessary.
RefreshAccountAsync(ctx context.Context, requestUser string, account *gtsmodel.Account, apubAcc ap.Accountable, force bool)
// GetStatusByURI will attempt to fetch a status by its URI, first checking the database. In the case of a newly-met remote model, or a remote model
// whose last_fetched date is beyond a certain interval, the status will be dereferenced. In the case of dereferencing, some low-priority status information
// may be enqueued for asynchronous fetching, e.g. dereferencing the remainder of the status thread. An ActivityPub object indicates the status was dereferenced.
GetStatusByURI(ctx context.Context, requestUser string, uri *url.URL) (*gtsmodel.Status, ap.Statusable, error)
// RefreshStatus updates the given status if remote and last_fetched is beyond fetch interval, or if force is set. An updated status model is returned,
// but in the case of dereferencing, some low-priority status information may be enqueued for asynchronous fetching, e.g. dereferencing the remainder of the
// status thread. An ActivityPub object indicates the status was dereferenced (i.e. updated).
RefreshStatus(ctx context.Context, requestUser string, status *gtsmodel.Status, apubStatus ap.Statusable, force bool) (*gtsmodel.Status, ap.Statusable, error)
// RefreshStatusAsync enqueues the given status for an asychronous update fetching, if last_fetched is beyond fetch interval, or if force is set.
// This is a more optimized form of manually enqueueing .UpdateStatus() to the federation worker, since it only enqueues update if necessary.
RefreshStatusAsync(ctx context.Context, requestUser string, status *gtsmodel.Status, apubStatus ap.Statusable, force bool)
// DereferenceStatusAncestors iterates upwards from the given status, using InReplyToURI, to ensure that as many parent statuses as possible are dereferenced.
DereferenceStatusAncestors(ctx context.Context, requestUser string, status *gtsmodel.Status) error
// DereferenceStatusDescendents iterates downwards from the given status, using its replies, to ensure that as many children statuses as possible are dereferenced.
DereferenceStatusDescendants(ctx context.Context, requestUser string, statusIRI *url.URL, parent ap.Statusable) error
GetRemoteInstance(ctx context.Context, username string, remoteInstanceURI *url.URL) (*gtsmodel.Instance, error)
DereferenceAnnounce(ctx context.Context, announce *gtsmodel.Status, requestingUsername string) error
GetRemoteMedia(ctx context.Context, requestingUsername string, accountID string, remoteURL string, ai *media.AdditionalMediaInfo) (*media.ProcessingMedia, error)
GetRemoteEmoji(ctx context.Context, requestingUsername string, remoteURL string, shortcode string, domain string, id string, emojiURI string, ai *media.AdditionalEmojiInfo, refresh bool) (*media.ProcessingEmoji, error)
Handshaking(username string, remoteAccountID *url.URL) bool
}
type deref struct {
state *state.State state *state.State
converter *typeutils.Converter converter *typeutils.Converter
transportController transport.Controller transportController transport.Controller
@ -99,8 +46,13 @@ type deref struct {
} }
// NewDereferencer returns a Dereferencer initialized with the given parameters. // NewDereferencer returns a Dereferencer initialized with the given parameters.
func NewDereferencer(state *state.State, converter *typeutils.Converter, transportController transport.Controller, mediaManager *media.Manager) Dereferencer { func NewDereferencer(
return &deref{ state *state.State,
converter *typeutils.Converter,
transportController transport.Controller,
mediaManager *media.Manager,
) Dereferencer {
return Dereferencer{
state: state, state: state,
converter: converter, converter: converter,
transportController: transportController, transportController: transportController,

View file

@ -30,7 +30,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/media"
) )
func (d *deref) GetRemoteEmoji(ctx context.Context, requestingUsername string, remoteURL string, shortcode string, domain string, id string, emojiURI string, ai *media.AdditionalEmojiInfo, refresh bool) (*media.ProcessingEmoji, error) { func (d *Dereferencer) GetRemoteEmoji(ctx context.Context, requestingUsername string, remoteURL string, shortcode string, domain string, id string, emojiURI string, ai *media.AdditionalEmojiInfo, refresh bool) (*media.ProcessingEmoji, error) {
var ( var (
shortcodeDomain = shortcode + "@" + domain shortcodeDomain = shortcode + "@" + domain
processingEmoji *media.ProcessingEmoji processingEmoji *media.ProcessingEmoji
@ -88,7 +88,7 @@ func (d *deref) GetRemoteEmoji(ctx context.Context, requestingUsername string, r
return processingEmoji, nil return processingEmoji, nil
} }
func (d *deref) populateEmojis(ctx context.Context, rawEmojis []*gtsmodel.Emoji, requestingUsername string) ([]*gtsmodel.Emoji, error) { func (d *Dereferencer) populateEmojis(ctx context.Context, rawEmojis []*gtsmodel.Emoji, requestingUsername string) ([]*gtsmodel.Emoji, error) {
// At this point we should know: // At this point we should know:
// * the AP uri of the emoji // * the AP uri of the emoji
// * the domain of the emoji // * the domain of the emoji

View file

@ -30,7 +30,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/util"
) )
func (d *deref) fingerRemoteAccount(ctx context.Context, transport transport.Transport, targetUsername string, targetHost string) (accountDomain string, accountURI *url.URL, err error) { func (d *Dereferencer) fingerRemoteAccount(ctx context.Context, transport transport.Transport, targetUsername string, targetHost string) (accountDomain string, accountURI *url.URL, err error) {
b, err := transport.Finger(ctx, targetUsername, targetHost) b, err := transport.Finger(ctx, targetUsername, targetHost)
if err != nil { if err != nil {
err = fmt.Errorf("fingerRemoteAccount: error fingering @%s@%s: %s", targetUsername, targetHost, err) err = fmt.Errorf("fingerRemoteAccount: error fingering @%s@%s: %s", targetUsername, targetHost, err)

View file

@ -21,7 +21,7 @@ import (
"net/url" "net/url"
) )
func (d *deref) Handshaking(username string, remoteAccountID *url.URL) bool { func (d *Dereferencer) Handshaking(username string, remoteAccountID *url.URL) bool {
d.handshakesMu.Lock() d.handshakesMu.Lock()
defer d.handshakesMu.Unlock() defer d.handshakesMu.Unlock()
@ -51,7 +51,7 @@ func (d *deref) Handshaking(username string, remoteAccountID *url.URL) bool {
return false return false
} }
func (d *deref) startHandshake(username string, remoteAccountID *url.URL) { func (d *Dereferencer) startHandshake(username string, remoteAccountID *url.URL) {
d.handshakesMu.Lock() d.handshakesMu.Lock()
defer d.handshakesMu.Unlock() defer d.handshakesMu.Unlock()
@ -68,7 +68,7 @@ func (d *deref) startHandshake(username string, remoteAccountID *url.URL) {
d.handshakes[username] = remoteIDs d.handshakes[username] = remoteIDs
} }
func (d *deref) stopHandshake(username string, remoteAccountID *url.URL) { func (d *Dereferencer) stopHandshake(username string, remoteAccountID *url.URL) {
d.handshakesMu.Lock() d.handshakesMu.Lock()
defer d.handshakesMu.Unlock() defer d.handshakesMu.Unlock()

View file

@ -25,7 +25,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
) )
func (d *deref) GetRemoteInstance(ctx context.Context, username string, remoteInstanceURI *url.URL) (*gtsmodel.Instance, error) { func (d *Dereferencer) GetRemoteInstance(ctx context.Context, username string, remoteInstanceURI *url.URL) (*gtsmodel.Instance, error) {
if blocked, err := d.state.DB.IsDomainBlocked(ctx, remoteInstanceURI.Host); blocked || err != nil { if blocked, err := d.state.DB.IsDomainBlocked(ctx, remoteInstanceURI.Host); blocked || err != nil {
return nil, fmt.Errorf("GetRemoteInstance: domain %s is blocked", remoteInstanceURI.Host) return nil, fmt.Errorf("GetRemoteInstance: domain %s is blocked", remoteInstanceURI.Host)
} }

View file

@ -26,7 +26,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/media"
) )
func (d *deref) GetRemoteMedia(ctx context.Context, requestingUsername string, accountID string, remoteURL string, ai *media.AdditionalMediaInfo) (*media.ProcessingMedia, error) { func (d *Dereferencer) GetRemoteMedia(ctx context.Context, requestingUsername string, accountID string, remoteURL string, ai *media.AdditionalMediaInfo) (*media.ProcessingMedia, error) {
if accountID == "" { if accountID == "" {
return nil, fmt.Errorf("GetRemoteMedia: account ID was empty") return nil, fmt.Errorf("GetRemoteMedia: account ID was empty")
} }

View file

@ -52,8 +52,10 @@ func statusUpToDate(status *gtsmodel.Status) bool {
return false return false
} }
// GetStatus: implements Dereferencer{}.GetStatus(). // GetStatusByURI will attempt to fetch a status by its URI, first checking the database. In the case of a newly-met remote model, or a remote model
func (d *deref) GetStatusByURI(ctx context.Context, requestUser string, uri *url.URL) (*gtsmodel.Status, ap.Statusable, error) { // whose last_fetched date is beyond a certain interval, the status will be dereferenced. In the case of dereferencing, some low-priority status information
// may be enqueued for asynchronous fetching, e.g. dereferencing the remainder of the status thread. An ActivityPub object indicates the status was dereferenced.
func (d *Dereferencer) GetStatusByURI(ctx context.Context, requestUser string, uri *url.URL) (*gtsmodel.Status, ap.Statusable, error) {
// Fetch and dereference status if necessary. // Fetch and dereference status if necessary.
status, apubStatus, err := d.getStatusByURI(ctx, status, apubStatus, err := d.getStatusByURI(ctx,
requestUser, requestUser,
@ -74,7 +76,7 @@ func (d *deref) GetStatusByURI(ctx context.Context, requestUser string, uri *url
} }
// getStatusByURI is a package internal form of .GetStatusByURI() that doesn't bother dereferencing the whole thread on update. // getStatusByURI is a package internal form of .GetStatusByURI() that doesn't bother dereferencing the whole thread on update.
func (d *deref) getStatusByURI(ctx context.Context, requestUser string, uri *url.URL) (*gtsmodel.Status, ap.Statusable, error) { func (d *Dereferencer) getStatusByURI(ctx context.Context, requestUser string, uri *url.URL) (*gtsmodel.Status, ap.Statusable, error) {
var ( var (
status *gtsmodel.Status status *gtsmodel.Status
uriStr = uri.String() uriStr = uri.String()
@ -146,8 +148,10 @@ func (d *deref) getStatusByURI(ctx context.Context, requestUser string, uri *url
return latest, apubStatus, nil return latest, apubStatus, nil
} }
// RefreshStatus: implements Dereferencer{}.RefreshStatus(). // RefreshStatus updates the given status if remote and last_fetched is beyond fetch interval, or if force is set. An updated status model is returned,
func (d *deref) RefreshStatus(ctx context.Context, requestUser string, status *gtsmodel.Status, apubStatus ap.Statusable, force bool) (*gtsmodel.Status, ap.Statusable, error) { // but in the case of dereferencing, some low-priority status information may be enqueued for asynchronous fetching, e.g. dereferencing the remainder of the
// status thread. An ActivityPub object indicates the status was dereferenced (i.e. updated).
func (d *Dereferencer) RefreshStatus(ctx context.Context, requestUser string, status *gtsmodel.Status, apubStatus ap.Statusable, force bool) (*gtsmodel.Status, ap.Statusable, error) {
// Check whether needs update. // Check whether needs update.
if !force && statusUpToDate(status) { if !force && statusUpToDate(status) {
return status, nil, nil return status, nil, nil
@ -178,8 +182,9 @@ func (d *deref) RefreshStatus(ctx context.Context, requestUser string, status *g
return latest, apubStatus, nil return latest, apubStatus, nil
} }
// RefreshStatusAsync: implements Dereferencer{}.RefreshStatusAsync(). // RefreshStatusAsync enqueues the given status for an asychronous update fetching, if last_fetched is beyond fetch interval, or if force is set.
func (d *deref) RefreshStatusAsync(ctx context.Context, requestUser string, status *gtsmodel.Status, apubStatus ap.Statusable, force bool) { // This is a more optimized form of manually enqueueing .UpdateStatus() to the federation worker, since it only enqueues update if necessary.
func (d *Dereferencer) RefreshStatusAsync(ctx context.Context, requestUser string, status *gtsmodel.Status, apubStatus ap.Statusable, force bool) {
// Check whether needs update. // Check whether needs update.
if statusUpToDate(status) { if statusUpToDate(status) {
return return
@ -208,7 +213,7 @@ func (d *deref) RefreshStatusAsync(ctx context.Context, requestUser string, stat
// enrichStatus will enrich the given status, whether a new // enrichStatus will enrich the given status, whether a new
// barebones model, or existing model from the database. // barebones model, or existing model from the database.
// It handles necessary dereferencing, database updates, etc. // It handles necessary dereferencing, database updates, etc.
func (d *deref) enrichStatus( func (d *Dereferencer) enrichStatus(
ctx context.Context, ctx context.Context,
requestUser string, requestUser string,
uri *url.URL, uri *url.URL,
@ -329,7 +334,7 @@ func (d *deref) enrichStatus(
return latestStatus, apubStatus, nil return latestStatus, apubStatus, nil
} }
func (d *deref) fetchStatusMentions(ctx context.Context, requestUser string, existing, status *gtsmodel.Status) error { func (d *Dereferencer) fetchStatusMentions(ctx context.Context, requestUser string, existing, status *gtsmodel.Status) error {
// Allocate new slice to take the yet-to-be created mention IDs. // Allocate new slice to take the yet-to-be created mention IDs.
status.MentionIDs = make([]string, len(status.Mentions)) status.MentionIDs = make([]string, len(status.Mentions))
@ -405,7 +410,7 @@ func (d *deref) fetchStatusMentions(ctx context.Context, requestUser string, exi
return nil return nil
} }
func (d *deref) fetchStatusTags(ctx context.Context, status *gtsmodel.Status) error { func (d *Dereferencer) fetchStatusTags(ctx context.Context, status *gtsmodel.Status) error {
// Allocate new slice to take the yet-to-be determined tag IDs. // Allocate new slice to take the yet-to-be determined tag IDs.
status.TagIDs = make([]string, len(status.Tags)) status.TagIDs = make([]string, len(status.Tags))
@ -455,7 +460,7 @@ func (d *deref) fetchStatusTags(ctx context.Context, status *gtsmodel.Status) er
return nil return nil
} }
func (d *deref) fetchStatusAttachments(ctx context.Context, tsport transport.Transport, existing, status *gtsmodel.Status) error { func (d *Dereferencer) fetchStatusAttachments(ctx context.Context, tsport transport.Transport, existing, status *gtsmodel.Status) error {
// Allocate new slice to take the yet-to-be fetched attachment IDs. // Allocate new slice to take the yet-to-be fetched attachment IDs.
status.AttachmentIDs = make([]string, len(status.Attachments)) status.AttachmentIDs = make([]string, len(status.Attachments))
@ -519,7 +524,7 @@ func (d *deref) fetchStatusAttachments(ctx context.Context, tsport transport.Tra
return nil return nil
} }
func (d *deref) fetchStatusEmojis(ctx context.Context, requestUser string, status *gtsmodel.Status) error { func (d *Dereferencer) fetchStatusEmojis(ctx context.Context, requestUser string, status *gtsmodel.Status) error {
// Fetch the full-fleshed-out emoji objects for our status. // Fetch the full-fleshed-out emoji objects for our status.
emojis, err := d.populateEmojis(ctx, status.Emojis, requestUser) emojis, err := d.populateEmojis(ctx, status.Emojis, requestUser)
if err != nil { if err != nil {

View file

@ -38,7 +38,7 @@ import (
// ancesters we are willing to follow before returning error. // ancesters we are willing to follow before returning error.
const maxIter = 1000 const maxIter = 1000
func (d *deref) dereferenceThread(ctx context.Context, username string, statusIRI *url.URL, status *gtsmodel.Status, statusable ap.Statusable) { func (d *Dereferencer) dereferenceThread(ctx context.Context, username string, statusIRI *url.URL, status *gtsmodel.Status, statusable ap.Statusable) {
// Ensure that ancestors have been fully dereferenced // Ensure that ancestors have been fully dereferenced
if err := d.DereferenceStatusAncestors(ctx, username, status); err != nil { if err := d.DereferenceStatusAncestors(ctx, username, status); err != nil {
log.Error(ctx, err) log.Error(ctx, err)
@ -50,11 +50,8 @@ func (d *deref) dereferenceThread(ctx context.Context, username string, statusIR
} }
} }
func (d *deref) DereferenceStatusAncestors( // DereferenceStatusAncestors iterates upwards from the given status, using InReplyToURI, to ensure that as many parent statuses as possible are dereferenced.
ctx context.Context, func (d *Dereferencer) DereferenceStatusAncestors(ctx context.Context, username string, status *gtsmodel.Status) error {
username string,
status *gtsmodel.Status,
) error {
// Start log entry with fields // Start log entry with fields
l := log.WithContext(ctx). l := log.WithContext(ctx).
WithFields(kv.Fields{ WithFields(kv.Fields{
@ -220,7 +217,8 @@ func (d *deref) DereferenceStatusAncestors(
return gtserror.Newf("reached %d ancestor iterations for %q", maxIter, status.URI) return gtserror.Newf("reached %d ancestor iterations for %q", maxIter, status.URI)
} }
func (d *deref) DereferenceStatusDescendants(ctx context.Context, username string, statusIRI *url.URL, parent ap.Statusable) error { // DereferenceStatusDescendents iterates downwards from the given status, using its replies, to ensure that as many children statuses as possible are dereferenced.
func (d *Dereferencer) DereferenceStatusDescendants(ctx context.Context, username string, statusIRI *url.URL, parent ap.Statusable) error {
statusIRIStr := statusIRI.String() statusIRIStr := statusIRI.String()
// Start log entry with fields // Start log entry with fields

View file

@ -93,7 +93,7 @@ func newErrOtherIRIBlocked(
// In this case, the DelegateActor implementation must not write a response // In this case, the DelegateActor implementation must not write a response
// to the ResponseWriter as is expected that the caller to PostInbox will // to the ResponseWriter as is expected that the caller to PostInbox will
// do so when handling the error. // do so when handling the error.
func (f *federator) PostInboxRequestBodyHook(ctx context.Context, r *http.Request, activity pub.Activity) (context.Context, error) { func (f *Federator) PostInboxRequestBodyHook(ctx context.Context, r *http.Request, activity pub.Activity) (context.Context, error) {
// Extract any other IRIs involved in this activity. // Extract any other IRIs involved in this activity.
otherIRIs := []*url.URL{} otherIRIs := []*url.URL{}
@ -186,7 +186,7 @@ func (f *federator) PostInboxRequestBodyHook(ctx context.Context, r *http.Reques
// Finally, if the authentication and authorization succeeds, then // Finally, if the authentication and authorization succeeds, then
// authenticated must be true and error nil. The request will continue // authenticated must be true and error nil. The request will continue
// to be processed. // to be processed.
func (f *federator) AuthenticatePostInbox(ctx context.Context, w http.ResponseWriter, r *http.Request) (context.Context, bool, error) { func (f *Federator) AuthenticatePostInbox(ctx context.Context, w http.ResponseWriter, r *http.Request) (context.Context, bool, error) {
log.Tracef(ctx, "received request to authenticate inbox %s", r.URL.String()) log.Tracef(ctx, "received request to authenticate inbox %s", r.URL.String())
// Ensure this is an inbox path, and fetch the inbox owner // Ensure this is an inbox path, and fetch the inbox owner
@ -298,7 +298,7 @@ func (f *federator) AuthenticatePostInbox(ctx context.Context, w http.ResponseWr
// Blocked should determine whether to permit a set of actors given by // Blocked should determine whether to permit a set of actors given by
// their ids are able to interact with this particular end user due to // their ids are able to interact with this particular end user due to
// being blocked or other application-specific logic. // being blocked or other application-specific logic.
func (f *federator) Blocked(ctx context.Context, actorIRIs []*url.URL) (bool, error) { func (f *Federator) Blocked(ctx context.Context, actorIRIs []*url.URL) (bool, error) {
// Fetch relevant items from request context. // Fetch relevant items from request context.
// These should have been set further up the flow. // These should have been set further up the flow.
receivingAccount := gtscontext.ReceivingAccount(ctx) receivingAccount := gtscontext.ReceivingAccount(ctx)
@ -498,7 +498,7 @@ func (f *federator) Blocked(ctx context.Context, actorIRIs []*url.URL) (bool, er
// //
// Applications are not expected to handle every single ActivityStreams // Applications are not expected to handle every single ActivityStreams
// type and extension. The unhandled ones are passed to DefaultCallback. // type and extension. The unhandled ones are passed to DefaultCallback.
func (f *federator) FederatingCallbacks(ctx context.Context) (wrapped pub.FederatingWrappedCallbacks, other []interface{}, err error) { func (f *Federator) FederatingCallbacks(ctx context.Context) (wrapped pub.FederatingWrappedCallbacks, other []interface{}, err error) {
wrapped = pub.FederatingWrappedCallbacks{ wrapped = pub.FederatingWrappedCallbacks{
// OnFollow determines what action to take for this // OnFollow determines what action to take for this
// particular callback if a Follow Activity is handled. // particular callback if a Follow Activity is handled.
@ -537,7 +537,7 @@ func (f *federator) FederatingCallbacks(ctx context.Context) (wrapped pub.Federa
// Applications are not expected to handle every single ActivityStreams // Applications are not expected to handle every single ActivityStreams
// type and extension, so the unhandled ones are passed to // type and extension, so the unhandled ones are passed to
// DefaultCallback. // DefaultCallback.
func (f *federator) DefaultCallback(ctx context.Context, activity pub.Activity) error { func (f *Federator) DefaultCallback(ctx context.Context, activity pub.Activity) error {
log.Debugf(ctx, "received unhandle-able activity type (%s) so ignoring it", activity.GetTypeName()) log.Debugf(ctx, "received unhandle-able activity type (%s) so ignoring it", activity.GetTypeName())
return nil return nil
} }
@ -546,7 +546,7 @@ func (f *federator) DefaultCallback(ctx context.Context, activity pub.Activity)
// an activity to determine if inbox forwarding needs to occur. // an activity to determine if inbox forwarding needs to occur.
// //
// Zero or negative numbers indicate infinite recursion. // Zero or negative numbers indicate infinite recursion.
func (f *federator) MaxInboxForwardingRecursionDepth(ctx context.Context) int { func (f *Federator) MaxInboxForwardingRecursionDepth(ctx context.Context) int {
// TODO // TODO
return 4 return 4
} }
@ -556,7 +556,7 @@ func (f *federator) MaxInboxForwardingRecursionDepth(ctx context.Context) int {
// delivery. // delivery.
// //
// Zero or negative numbers indicate infinite recursion. // Zero or negative numbers indicate infinite recursion.
func (f *federator) MaxDeliveryRecursionDepth(ctx context.Context) int { func (f *Federator) MaxDeliveryRecursionDepth(ctx context.Context) int {
// TODO // TODO
return 4 return 4
} }
@ -568,7 +568,7 @@ func (f *federator) MaxDeliveryRecursionDepth(ctx context.Context) int {
// //
// The activity is provided as a reference for more intelligent // The activity is provided as a reference for more intelligent
// logic to be used, but the implementation must not modify it. // logic to be used, but the implementation must not modify it.
func (f *federator) FilterForwarding(ctx context.Context, potentialRecipients []*url.URL, a pub.Activity) ([]*url.URL, error) { func (f *Federator) FilterForwarding(ctx context.Context, potentialRecipients []*url.URL, a pub.Activity) ([]*url.URL, error) {
// TODO // TODO
return []*url.URL{}, nil return []*url.URL{}, nil
} }
@ -581,7 +581,7 @@ func (f *federator) FilterForwarding(ctx context.Context, potentialRecipients []
// //
// Always called, regardless whether the Federated Protocol or Social // Always called, regardless whether the Federated Protocol or Social
// API is enabled. // API is enabled.
func (f *federator) GetInbox(ctx context.Context, r *http.Request) (vocab.ActivityStreamsOrderedCollectionPage, error) { func (f *Federator) GetInbox(ctx context.Context, r *http.Request) (vocab.ActivityStreamsOrderedCollectionPage, error) {
// IMPLEMENTATION NOTE: For GoToSocial, we serve GETS to outboxes and inboxes through // IMPLEMENTATION NOTE: For GoToSocial, we serve GETS to outboxes and inboxes through
// the CLIENT API, not through the federation API, so we just do nothing here. // the CLIENT API, not through the federation API, so we just do nothing here.
return streams.NewActivityStreamsOrderedCollectionPage(), nil return streams.NewActivityStreamsOrderedCollectionPage(), nil

View file

@ -18,44 +18,22 @@
package federation package federation
import ( import (
"context"
"github.com/superseriousbusiness/activity/pub" "github.com/superseriousbusiness/activity/pub"
"github.com/superseriousbusiness/gotosocial/internal/db" "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/federation/dereferencing" "github.com/superseriousbusiness/gotosocial/internal/federation/dereferencing"
"github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb" "github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/media" "github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/state" "github.com/superseriousbusiness/gotosocial/internal/state"
"github.com/superseriousbusiness/gotosocial/internal/transport" "github.com/superseriousbusiness/gotosocial/internal/transport"
"github.com/superseriousbusiness/gotosocial/internal/typeutils" "github.com/superseriousbusiness/gotosocial/internal/typeutils"
) )
// Federator wraps various interfaces and functions to manage activitypub federation from gotosocial var _ interface {
type Federator interface {
// FederatingActor returns the underlying pub.FederatingActor, which can be used to send activities, and serve actors at inboxes/outboxes.
FederatingActor() pub.FederatingActor
// FederatingDB returns the underlying FederatingDB interface.
FederatingDB() federatingdb.DB
// TransportController returns the underlying transport controller.
TransportController() transport.Controller
// AuthenticateFederatedRequest can be used to check the authenticity of incoming http-signed requests for federating resources.
// The given username will be used to create a transport for making outgoing requests. See the implementation for more detailed comments.
//
// If the request is valid and passes authentication, the URL of the key owner ID will be returned, as well as true, and nil.
//
// If the request does not pass authentication, or there's a domain block, nil, false, nil will be returned.
//
// If something goes wrong during authentication, nil, false, and an error will be returned.
AuthenticateFederatedRequest(ctx context.Context, username string) (*PubKeyAuth, gtserror.WithCode)
pub.CommonBehavior pub.CommonBehavior
pub.FederatingProtocol pub.FederatingProtocol
dereferencing.Dereferencer } = &Federator{}
}
type federator struct { type Federator struct {
db db.DB db db.DB
federatingDB federatingdb.DB federatingDB federatingdb.DB
clock pub.Clock clock pub.Clock
@ -66,33 +44,40 @@ type federator struct {
dereferencing.Dereferencer dereferencing.Dereferencer
} }
// NewFederator returns a new federator // NewFederator returns a new federator instance.
func NewFederator(state *state.State, federatingDB federatingdb.DB, transportController transport.Controller, converter *typeutils.Converter, mediaManager *media.Manager) Federator { func NewFederator(
dereferencer := dereferencing.NewDereferencer(state, converter, transportController, mediaManager) state *state.State,
federatingDB federatingdb.DB,
transportController transport.Controller,
converter *typeutils.Converter,
mediaManager *media.Manager,
) *Federator {
clock := &Clock{} clock := &Clock{}
f := &federator{ f := &Federator{
db: state.DB, db: state.DB,
federatingDB: federatingDB, federatingDB: federatingDB,
clock: &Clock{}, clock: clock,
converter: converter, converter: converter,
transportController: transportController, transportController: transportController,
mediaManager: mediaManager, mediaManager: mediaManager,
Dereferencer: dereferencer, Dereferencer: dereferencing.NewDereferencer(state, converter, transportController, mediaManager),
} }
actor := newFederatingActor(f, f, federatingDB, clock) actor := newFederatingActor(f, f, federatingDB, clock)
f.actor = actor f.actor = actor
return f return f
} }
func (f *federator) FederatingActor() pub.FederatingActor { // FederatingActor returns the underlying pub.FederatingActor, which can be used to send activities, and serve actors at inboxes/outboxes.
func (f *Federator) FederatingActor() pub.FederatingActor {
return f.actor return f.actor
} }
func (f *federator) FederatingDB() federatingdb.DB { // FederatingDB returns the underlying FederatingDB interface.
func (f *Federator) FederatingDB() federatingdb.DB {
return f.federatingDB return f.federatingDB
} }
func (f *federator) TransportController() transport.Controller { // TransportController returns the underlying transport controller.
func (f *Federator) TransportController() transport.Controller {
return f.transportController return f.transportController
} }

View file

@ -39,7 +39,7 @@ type FederatorStandardTestSuite struct {
typeconverter *typeutils.Converter typeconverter *typeutils.Converter
transportController transport.Controller transportController transport.Controller
httpClient *testrig.MockHTTPClient httpClient *testrig.MockHTTPClient
federator federation.Federator federator *federation.Federator
testAccounts map[string]*gtsmodel.Account testAccounts map[string]*gtsmodel.Account
testStatuses map[string]*gtsmodel.Status testStatuses map[string]*gtsmodel.Status

View file

@ -26,12 +26,12 @@ import (
) )
// CheckGone checks if a tombstone exists in the database for AP Actor or Object with the given uri. // CheckGone checks if a tombstone exists in the database for AP Actor or Object with the given uri.
func (f *federator) CheckGone(ctx context.Context, uri *url.URL) (bool, error) { func (f *Federator) CheckGone(ctx context.Context, uri *url.URL) (bool, error) {
return f.db.TombstoneExistsWithURI(ctx, uri.String()) return f.db.TombstoneExistsWithURI(ctx, uri.String())
} }
// HandleGone puts a tombstone in the database, which marks an AP Actor or Object with the given uri as gone. // HandleGone puts a tombstone in the database, which marks an AP Actor or Object with the given uri as gone.
func (f *federator) HandleGone(ctx context.Context, uri *url.URL) error { func (f *Federator) HandleGone(ctx context.Context, uri *url.URL) error {
tombstone := &gtsmodel.Tombstone{ tombstone := &gtsmodel.Tombstone{
ID: id.NewULID(), ID: id.NewULID(),
Domain: uri.Host, Domain: uri.Host,

View file

@ -49,7 +49,7 @@ import (
// Note that the library will not maintain a long-lived pointer to the // Note that the library will not maintain a long-lived pointer to the
// returned Transport so that any private credentials are able to be // returned Transport so that any private credentials are able to be
// garbage collected. // garbage collected.
func (f *federator) NewTransport(ctx context.Context, actorBoxIRI *url.URL, gofedAgent string) (pub.Transport, error) { func (f *Federator) NewTransport(ctx context.Context, actorBoxIRI *url.URL, gofedAgent string) (pub.Transport, error) {
var username string var username string
var err error var err error

View file

@ -41,7 +41,12 @@ type Server interface {
// NewServer returns a new gotosocial server, initialized with the given configuration. // NewServer returns a new gotosocial server, initialized with the given configuration.
// An error will be returned the caller if something goes wrong during initialization // An error will be returned the caller if something goes wrong during initialization
// eg., no db or storage connection, port for router already in use, etc. // eg., no db or storage connection, port for router already in use, etc.
func NewServer(db db.DB, apiRouter router.Router, federator federation.Federator, mediaManager *media.Manager) (Server, error) { func NewServer(
db db.DB,
apiRouter router.Router,
federator *federation.Federator,
mediaManager *media.Manager,
) (Server, error) {
return &gotosocial{ return &gotosocial{
db: db, db: db,
apiRouter: apiRouter, apiRouter: apiRouter,
@ -54,7 +59,7 @@ func NewServer(db db.DB, apiRouter router.Router, federator federation.Federator
type gotosocial struct { type gotosocial struct {
db db.DB db db.DB
apiRouter router.Router apiRouter router.Router
federator federation.Federator federator *federation.Federator
mediaManager *media.Manager mediaManager *media.Manager
} }

View file

@ -42,7 +42,7 @@ type Processor struct {
oauthServer oauth.Server oauthServer oauth.Server
filter *visibility.Filter filter *visibility.Filter
formatter *text.Formatter formatter *text.Formatter
federator federation.Federator federator *federation.Federator
parseMention gtsmodel.ParseMentionFunc parseMention gtsmodel.ParseMentionFunc
} }
@ -53,7 +53,7 @@ func New(
converter *typeutils.Converter, converter *typeutils.Converter,
mediaManager *media.Manager, mediaManager *media.Manager,
oauthServer oauth.Server, oauthServer oauth.Server,
federator federation.Federator, federator *federation.Federator,
filter *visibility.Filter, filter *visibility.Filter,
parseMention gtsmodel.ParseMentionFunc, parseMention gtsmodel.ParseMentionFunc,
) Processor { ) Processor {

View file

@ -50,7 +50,7 @@ type AccountStandardTestSuite struct {
oauthServer oauth.Server oauthServer oauth.Server
fromClientAPIChan chan messages.FromClientAPI fromClientAPIChan chan messages.FromClientAPI
transportController transport.Controller transportController transport.Controller
federator federation.Federator federator *federation.Federator
emailSender email.Sender emailSender email.Sender
sentEmails map[string]string sentEmails map[string]string

View file

@ -47,7 +47,7 @@ type AdminStandardTestSuite struct {
oauthServer oauth.Server oauthServer oauth.Server
fromClientAPIChan chan messages.FromClientAPI fromClientAPIChan chan messages.FromClientAPI
transportController transport.Controller transportController transport.Controller
federator federation.Federator federator *federation.Federator
emailSender email.Sender emailSender email.Sender
sentEmails map[string]string sentEmails map[string]string
processor *processing.Processor processor *processing.Processor

View file

@ -30,7 +30,7 @@ import (
type Processor struct { type Processor struct {
state *state.State state *state.State
converter *typeutils.Converter converter *typeutils.Converter
federator federation.Federator federator *federation.Federator
filter *visibility.Filter filter *visibility.Filter
} }
@ -38,7 +38,7 @@ type Processor struct {
func New( func New(
state *state.State, state *state.State,
converter *typeutils.Converter, converter *typeutils.Converter,
federator federation.Federator, federator *federation.Federator,
filter *visibility.Filter, filter *visibility.Filter,
) Processor { ) Processor {
return Processor{ return Processor{

View file

@ -26,13 +26,13 @@ import (
type Processor struct { type Processor struct {
state *state.State state *state.State
federator federation.Federator federator *federation.Federator
converter *typeutils.Converter converter *typeutils.Converter
filter *visibility.Filter filter *visibility.Filter
} }
// New returns a new fedi processor. // New returns a new fedi processor.
func New(state *state.State, converter *typeutils.Converter, federator federation.Federator, filter *visibility.Filter) Processor { func New(state *state.State, converter *typeutils.Converter, federator *federation.Federator, filter *visibility.Filter) Processor {
return Processor{ return Processor{
state: state, state: state,
federator: federator, federator: federator,

View file

@ -127,7 +127,7 @@ func (p *Processor) Workers() *workers.Processor {
// NewProcessor returns a new Processor. // NewProcessor returns a new Processor.
func NewProcessor( func NewProcessor(
converter *typeutils.Converter, converter *typeutils.Converter,
federator federation.Federator, federator *federation.Federator,
oauthServer oauth.Server, oauthServer oauth.Server,
mediaManager *mm.Manager, mediaManager *mm.Manager,
state *state.State, state *state.State,

View file

@ -47,7 +47,7 @@ type ProcessingStandardTestSuite struct {
typeconverter *typeutils.Converter typeconverter *typeutils.Converter
httpClient *testrig.MockHTTPClient httpClient *testrig.MockHTTPClient
transportController transport.Controller transportController transport.Controller
federator federation.Federator federator *federation.Federator
oauthServer oauth.Server oauthServer oauth.Server
emailSender email.Sender emailSender email.Sender

View file

@ -26,13 +26,13 @@ import (
type Processor struct { type Processor struct {
state *state.State state *state.State
federator federation.Federator federator *federation.Federator
converter *typeutils.Converter converter *typeutils.Converter
filter *visibility.Filter filter *visibility.Filter
} }
// New returns a new status processor. // New returns a new status processor.
func New(state *state.State, federator federation.Federator, converter *typeutils.Converter, filter *visibility.Filter) Processor { func New(state *state.State, federator *federation.Federator, converter *typeutils.Converter, filter *visibility.Filter) Processor {
return Processor{ return Processor{
state: state, state: state,
federator: federator, federator: federator,

View file

@ -28,7 +28,7 @@ import (
type Processor struct { type Processor struct {
state *state.State state *state.State
federator federation.Federator federator *federation.Federator
converter *typeutils.Converter converter *typeutils.Converter
filter *visibility.Filter filter *visibility.Filter
formatter *text.Formatter formatter *text.Formatter
@ -36,7 +36,7 @@ type Processor struct {
} }
// New returns a new status processor. // New returns a new status processor.
func New(state *state.State, federator federation.Federator, converter *typeutils.Converter, filter *visibility.Filter, parseMention gtsmodel.ParseMentionFunc) Processor { func New(state *state.State, federator *federation.Federator, converter *typeutils.Converter, filter *visibility.Filter, parseMention gtsmodel.ParseMentionFunc) Processor {
return Processor{ return Processor{
state: state, state: state,
federator: federator, federator: federator,

View file

@ -41,7 +41,7 @@ type StatusStandardTestSuite struct {
storage *storage.Driver storage *storage.Driver
state state.State state state.State
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
// standard suite models // standard suite models
testTokens map[string]*gtsmodel.Token testTokens map[string]*gtsmodel.Token

View file

@ -30,7 +30,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/util" "github.com/superseriousbusiness/gotosocial/internal/util"
) )
func GetParseMentionFunc(dbConn db.DB, federator federation.Federator) gtsmodel.ParseMentionFunc { func GetParseMentionFunc(dbConn db.DB, federator *federation.Federator) gtsmodel.ParseMentionFunc {
return func(ctx context.Context, targetAccount string, originAccountID string, statusID string) (*gtsmodel.Mention, error) { return func(ctx context.Context, targetAccount string, originAccountID string, statusID string) (*gtsmodel.Mention, error) {
// get the origin account first since we'll need it to create the mention // get the origin account first since we'll need it to create the mention
originAccount, err := dbConn.GetAccountByID(ctx, originAccountID) originAccount, err := dbConn.GetAccountByID(ctx, originAccountID)

View file

@ -36,7 +36,7 @@ import (
type federate struct { type federate struct {
// Embed federator to give access // Embed federator to give access
// to send and retrieve functions. // to send and retrieve functions.
federation.Federator *federation.Federator
state *state.State state *state.State
converter *typeutils.Converter converter *typeutils.Converter
} }

View file

@ -37,7 +37,7 @@ type Processor struct {
func New( func New(
state *state.State, state *state.State,
federator federation.Federator, federator *federation.Federator,
converter *typeutils.Converter, converter *typeutils.Converter,
filter *visibility.Filter, filter *visibility.Filter,
emailSender email.Sender, emailSender email.Sender,

View file

@ -47,7 +47,7 @@ type WorkersTestSuite struct {
typeconverter *typeutils.Converter typeconverter *typeutils.Converter
httpClient *testrig.MockHTTPClient httpClient *testrig.MockHTTPClient
transportController transport.Controller transportController transport.Controller
federator federation.Federator federator *federation.Federator
oauthServer oauth.Server oauthServer oauth.Server
emailSender email.Sender emailSender email.Sender

View file

@ -41,7 +41,7 @@ type TransportTestSuite struct {
db db.DB db db.DB
storage *storage.Driver storage *storage.Driver
mediaManager *media.Manager mediaManager *media.Manager
federator federation.Federator federator *federation.Federator
processor *processing.Processor processor *processing.Processor
emailSender email.Sender emailSender email.Sender
sentEmails map[string]string sentEmails map[string]string

View file

@ -26,6 +26,6 @@ import (
) )
// NewTestFederator returns a federator with the given database and (mock!!) transport controller. // NewTestFederator returns a federator with the given database and (mock!!) transport controller.
func NewTestFederator(state *state.State, tc transport.Controller, mediaManager *media.Manager) federation.Federator { func NewTestFederator(state *state.State, tc transport.Controller, mediaManager *media.Manager) *federation.Federator {
return federation.NewFederator(state, NewTestFederatingDB(state), tc, typeutils.NewConverter(state), mediaManager) return federation.NewFederator(state, NewTestFederatingDB(state), tc, typeutils.NewConverter(state), mediaManager)
} }

View file

@ -27,7 +27,7 @@ import (
) )
// NewTestProcessor returns a Processor suitable for testing purposes // NewTestProcessor returns a Processor suitable for testing purposes
func NewTestProcessor(state *state.State, federator federation.Federator, emailSender email.Sender, mediaManager *media.Manager) *processing.Processor { func NewTestProcessor(state *state.State, federator *federation.Federator, emailSender email.Sender, mediaManager *media.Manager) *processing.Processor {
p := processing.NewProcessor(typeutils.NewConverter(state), federator, NewTestOauthServer(state.DB), mediaManager, state, emailSender) p := processing.NewProcessor(typeutils.NewConverter(state), federator, NewTestOauthServer(state.DB), mediaManager, state, emailSender)
state.Workers.EnqueueClientAPI = p.Workers().EnqueueClientAPI state.Workers.EnqueueClientAPI = p.Workers().EnqueueClientAPI
state.Workers.EnqueueFediAPI = p.Workers().EnqueueFediAPI state.Workers.EnqueueFediAPI = p.Workers().EnqueueFediAPI