diff --git a/internal/api/apimodule.go b/internal/api/apimodule.go
index 5d9fc8d59..096203251 100644
--- a/internal/api/apimodule.go
+++ b/internal/api/apimodule.go
@@ -34,5 +34,5 @@ type ClientModule interface {
// of functionalities and/or side effects to a router, by mapping routes and/or middlewares onto it--in other words, a REST API ;)
// Unlike ClientAPIModule, federation API module is not intended to be interacted with by clients directly -- it is primarily a server-to-server interface.
type FederationModule interface {
- Route(s router.Router) error
+ Route(s router.Router) error
}
diff --git a/internal/api/client/auth/auth.go b/internal/api/client/auth/auth.go
index 471383a75..793c19f4e 100644
--- a/internal/api/client/auth/auth.go
+++ b/internal/api/client/auth/auth.go
@@ -24,7 +24,8 @@ import (
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
- "github.com/superseriousbusiness/gotosocial/internal/message"
+ "github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/oauth"
"github.com/superseriousbusiness/gotosocial/internal/router"
)
@@ -39,17 +40,19 @@ const (
// Module implements the ClientAPIModule interface for
type Module struct {
- config *config.Config
- processor message.Processor
- log *logrus.Logger
+ config *config.Config
+ db db.DB
+ server oauth.Server
+ log *logrus.Logger
}
// New returns a new auth module
-func New(config *config.Config, processor message.Processor, log *logrus.Logger) api.ClientModule {
+func New(config *config.Config, db db.DB, server oauth.Server, log *logrus.Logger) api.ClientModule {
return &Module{
- config: config,
- processor: processor,
- log: log,
+ config: config,
+ db: db,
+ server: server,
+ log: log,
}
}
diff --git a/internal/api/client/auth/middleware.go b/internal/api/client/auth/middleware.go
index 8ae3b49ca..c42ba77fc 100644
--- a/internal/api/client/auth/middleware.go
+++ b/internal/api/client/auth/middleware.go
@@ -30,7 +30,7 @@ import (
// If user or account can't be found, then the handler won't *fail*, in case the server wants to allow
// public requests that don't have a Bearer token set (eg., for public instance information and so on).
func (m *Module) OauthTokenMiddleware(c *gin.Context) {
- l := m.log.WithField("func", "ValidatePassword")
+ l := m.log.WithField("func", "OauthTokenMiddleware")
l.Trace("entering OauthTokenMiddleware")
ti, err := m.server.ValidationBearerToken(c.Request)
diff --git a/internal/api/client/fileserver/servefile.go b/internal/api/client/fileserver/servefile.go
index ef8787661..9823eb387 100644
--- a/internal/api/client/fileserver/servefile.go
+++ b/internal/api/client/fileserver/servefile.go
@@ -85,6 +85,7 @@ func (m *FileServer) ServeFile(c *gin.Context) {
FileName: fileName,
})
if err != nil {
+ l.Debug(err)
c.String(http.StatusNotFound, "404 page not found")
return
}
diff --git a/internal/api/client/fileserver/servefile_test.go b/internal/api/client/fileserver/servefile_test.go
index 62849ec9a..09fd8ea43 100644
--- a/internal/api/client/fileserver/servefile_test.go
+++ b/internal/api/client/fileserver/servefile_test.go
@@ -33,6 +33,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/api/client/fileserver"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/federation"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/message"
@@ -49,6 +50,7 @@ type ServeFileTestSuite struct {
db db.DB
log *logrus.Logger
storage storage.Storage
+ federator federation.Federator
tc typeutils.TypeConverter
processor message.Processor
mediaHandler media.Handler
@@ -76,7 +78,8 @@ func (suite *ServeFileTestSuite) SetupSuite() {
suite.db = testrig.NewTestDB()
suite.log = testrig.NewTestLog()
suite.storage = testrig.NewTestStorage()
- suite.processor = testrig.NewTestProcessor(suite.db, suite.storage)
+ suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil)))
+ suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator)
suite.tc = testrig.NewTestTypeConverter(suite.db)
suite.mediaHandler = testrig.NewTestMediaHandler(suite.db, suite.storage)
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
diff --git a/internal/api/client/media/mediacreate_test.go b/internal/api/client/media/mediacreate_test.go
index 18823ad6c..e86c66021 100644
--- a/internal/api/client/media/mediacreate_test.go
+++ b/internal/api/client/media/mediacreate_test.go
@@ -36,6 +36,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/federation"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/message"
@@ -52,6 +53,7 @@ type MediaCreateTestSuite struct {
db db.DB
log *logrus.Logger
storage storage.Storage
+ federator federation.Federator
tc typeutils.TypeConverter
mediaHandler media.Handler
oauthServer oauth.Server
@@ -82,7 +84,8 @@ func (suite *MediaCreateTestSuite) SetupSuite() {
suite.tc = testrig.NewTestTypeConverter(suite.db)
suite.mediaHandler = testrig.NewTestMediaHandler(suite.db, suite.storage)
suite.oauthServer = testrig.NewTestOauthServer(suite.db)
- suite.processor = testrig.NewTestProcessor(suite.db, suite.storage)
+ suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil)))
+ suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator)
// setup module being tested
suite.mediaModule = mediamodule.New(suite.config, suite.processor, suite.log).(*mediamodule.Module)
diff --git a/internal/api/client/status/status_test.go b/internal/api/client/status/status_test.go
index 1cc3231a0..a18787aef 100644
--- a/internal/api/client/status/status_test.go
+++ b/internal/api/client/status/status_test.go
@@ -1,3 +1,21 @@
+/*
+ GoToSocial
+ Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
package status_test
import (
@@ -6,6 +24,7 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/api/client/status"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/federation"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/message"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -20,6 +39,7 @@ type StatusStandardTestSuite struct {
db db.DB
log *logrus.Logger
tc typeutils.TypeConverter
+ federator federation.Federator
processor message.Processor
storage storage.Storage
diff --git a/internal/api/client/status/statuscreate.go b/internal/api/client/status/statuscreate.go
index daf8922b5..02080b042 100644
--- a/internal/api/client/status/statuscreate.go
+++ b/internal/api/client/status/statuscreate.go
@@ -68,7 +68,7 @@ func (m *Module) StatusCreatePOSTHandler(c *gin.Context) {
mastoStatus, err := m.processor.StatusCreate(authed, form)
if err != nil {
l.Debugf("error processing status create: %s", err)
- c.JSON(http.StatusBadRequest, gin.H{"error":"bad request"})
+ c.JSON(http.StatusBadRequest, gin.H{"error": "bad request"})
return
}
diff --git a/internal/api/client/status/statuscreate_test.go b/internal/api/client/status/statuscreate_test.go
index 58e301a04..fb9b48f8a 100644
--- a/internal/api/client/status/statuscreate_test.go
+++ b/internal/api/client/status/statuscreate_test.go
@@ -56,7 +56,8 @@ func (suite *StatusCreateTestSuite) SetupTest() {
suite.db = testrig.NewTestDB()
suite.storage = testrig.NewTestStorage()
suite.log = testrig.NewTestLog()
- suite.processor = testrig.NewTestProcessor(suite.db, suite.storage)
+ suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil)))
+ suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator)
suite.statusModule = status.New(suite.config, suite.processor, suite.log).(*status.Module)
testrig.StandardDBSetup(suite.db)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
diff --git a/internal/api/client/status/statusfave_test.go b/internal/api/client/status/statusfave_test.go
index 6c6403988..2f779baed 100644
--- a/internal/api/client/status/statusfave_test.go
+++ b/internal/api/client/status/statusfave_test.go
@@ -55,7 +55,8 @@ func (suite *StatusFaveTestSuite) SetupTest() {
suite.db = testrig.NewTestDB()
suite.storage = testrig.NewTestStorage()
suite.log = testrig.NewTestLog()
- suite.processor = testrig.NewTestProcessor(suite.db, suite.storage)
+ suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil)))
+ suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator)
suite.statusModule = status.New(suite.config, suite.processor, suite.log).(*status.Module)
testrig.StandardDBSetup(suite.db)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
diff --git a/internal/api/client/status/statusfavedby_test.go b/internal/api/client/status/statusfavedby_test.go
index 62c027a39..7b72df7bc 100644
--- a/internal/api/client/status/statusfavedby_test.go
+++ b/internal/api/client/status/statusfavedby_test.go
@@ -55,7 +55,8 @@ func (suite *StatusFavedByTestSuite) SetupTest() {
suite.db = testrig.NewTestDB()
suite.storage = testrig.NewTestStorage()
suite.log = testrig.NewTestLog()
- suite.processor = testrig.NewTestProcessor(suite.db, suite.storage)
+ suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil)))
+ suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator)
suite.statusModule = status.New(suite.config, suite.processor, suite.log).(*status.Module)
testrig.StandardDBSetup(suite.db)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
diff --git a/internal/api/client/status/statusget_test.go b/internal/api/client/status/statusget_test.go
index 9bc12d250..b31acebca 100644
--- a/internal/api/client/status/statusget_test.go
+++ b/internal/api/client/status/statusget_test.go
@@ -45,7 +45,8 @@ func (suite *StatusGetTestSuite) SetupTest() {
suite.db = testrig.NewTestDB()
suite.storage = testrig.NewTestStorage()
suite.log = testrig.NewTestLog()
- suite.processor = testrig.NewTestProcessor(suite.db, suite.storage)
+ suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil)))
+ suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator)
suite.statusModule = status.New(suite.config, suite.processor, suite.log).(*status.Module)
testrig.StandardDBSetup(suite.db)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
@@ -56,7 +57,6 @@ func (suite *StatusGetTestSuite) TearDownTest() {
testrig.StandardStorageTeardown(suite.storage)
}
-
// Post a new status with some custom visibility settings
func (suite *StatusGetTestSuite) TestPostNewStatus() {
diff --git a/internal/api/client/status/statusunfave_test.go b/internal/api/client/status/statusunfave_test.go
index daf4b0696..44b1dd3a6 100644
--- a/internal/api/client/status/statusunfave_test.go
+++ b/internal/api/client/status/statusunfave_test.go
@@ -55,7 +55,8 @@ func (suite *StatusUnfaveTestSuite) SetupTest() {
suite.db = testrig.NewTestDB()
suite.storage = testrig.NewTestStorage()
suite.log = testrig.NewTestLog()
- suite.processor = testrig.NewTestProcessor(suite.db, suite.storage)
+ suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil)))
+ suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator)
suite.statusModule = status.New(suite.config, suite.processor, suite.log).(*status.Module)
testrig.StandardDBSetup(suite.db)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
diff --git a/internal/api/federation/users.go b/internal/api/federation/users.go
deleted file mode 100644
index f43842664..000000000
--- a/internal/api/federation/users.go
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- GoToSocial
- Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see .
-*/
-
-package federation
-
-import (
- "github.com/gin-gonic/gin"
-)
-
-// UsersGETHandler should be served at https://example.org/users/:username.
-//
-// The goal here is to return the activitypub representation of an account
-// in the form of a vocab.ActivityStreamsPerson. This should only be served
-// to REMOTE SERVERS that present a valid signature on the GET request, on
-// behalf of a user, otherwise we risk leaking information about users publicly.
-//
-// And of course, the request should be refused if the account or server making the
-// request is blocked.
-func (m *Module) UsersGETHandler(c *gin.Context) {
- // l := m.log.WithFields(logrus.Fields{
- // "func": "UsersGETHandler",
- // "url": c.Request.RequestURI,
- // })
- // requestedUsername := c.Param(UsernameKey)
- // if requestedUsername == "" {
- // err := errors.New("no username specified in request")
- // l.Debug(err)
- // c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
- // return
- // }
-
- // // make sure this actually an AP request
- // format := c.NegotiateFormat(ActivityPubAcceptHeaders...)
- // if format == "" {
- // err := errors.New("could not negotiate format with given Accept header(s)")
- // l.Debug(err)
- // c.JSON(http.StatusNotAcceptable, gin.H{"error": err.Error()})
- // return
- // }
- // l.Tracef("negotiated format: %s", format)
-
- // // get the account the request is referring to
- // requestedAccount := >smodel.Account{}
- // if err := m.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil {
- // l.Errorf("database error getting account with username %s: %s", requestedUsername, err)
- // // we'll just return not authorized here to avoid giving anything away
- // c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
- // return
- // }
-
- // // and create a transport for it
- // transport, err := m.federator.TransportController().NewTransport(requestedAccount.PublicKeyURI, requestedAccount.PrivateKey)
- // if err != nil {
- // l.Errorf("error creating transport for username %s: %s", requestedUsername, err)
- // // we'll just return not authorized here to avoid giving anything away
- // c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
- // return
- // }
-
- // // authenticate the request
- // authentication, err := federation.AuthenticateFederatedRequest(transport, c.Request)
- // if err != nil {
- // l.Errorf("error authenticating GET user request: %s", err)
- // c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
- // return
- // }
-
- // if !authentication.Authenticated {
- // l.Debug("request not authorized")
- // c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
- // return
- // }
-
- // requestingAccount := >smodel.Account{}
- // if authentication.RequestingPublicKeyID != nil {
- // if err := m.db.GetWhere("public_key_uri", authentication.RequestingPublicKeyID.String(), requestingAccount); err != nil {
-
- // }
- // }
-
- // authorization, err := federation.AuthorizeFederatedRequest
-
- // person, err := m.tc.AccountToAS(requestedAccount)
- // if err != nil {
- // l.Errorf("error converting account to ap person: %s", err)
- // c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
- // return
- // }
-
- // data, err := person.Serialize()
- // if err != nil {
- // l.Errorf("error serializing user: %s", err)
- // c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
- // return
- // }
-
- // c.JSON(http.StatusOK, data)
-}
diff --git a/internal/api/federation/federation.go b/internal/api/s2s/user/user.go
similarity index 81%
rename from internal/api/federation/federation.go
rename to internal/api/s2s/user/user.go
index cc3b33599..74e3b555e 100644
--- a/internal/api/federation/federation.go
+++ b/internal/api/s2s/user/user.go
@@ -16,7 +16,7 @@
along with this program. If not, see .
*/
-package federation
+package user
import (
"net/http"
@@ -24,10 +24,8 @@ import (
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/api"
"github.com/superseriousbusiness/gotosocial/internal/config"
- "github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/federation"
+ "github.com/superseriousbusiness/gotosocial/internal/message"
"github.com/superseriousbusiness/gotosocial/internal/router"
- "github.com/superseriousbusiness/gotosocial/internal/typeutils"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
@@ -51,20 +49,16 @@ var ActivityPubAcceptHeaders = []string{
// Module implements the FederationAPIModule interface
type Module struct {
- federator federation.Federator
config *config.Config
- db db.DB
- tc typeutils.TypeConverter
+ processor message.Processor
log *logrus.Logger
}
// New returns a new auth module
-func New(db db.DB, federator federation.Federator, tc typeutils.TypeConverter, config *config.Config, log *logrus.Logger) api.FederationModule {
+func New(config *config.Config, processor message.Processor, log *logrus.Logger) api.FederationModule {
return &Module{
- federator: federator,
config: config,
- db: db,
- tc: tc,
+ processor: processor,
log: log,
}
}
diff --git a/internal/api/s2s/user/userget.go b/internal/api/s2s/user/userget.go
new file mode 100644
index 000000000..d62b4b5e6
--- /dev/null
+++ b/internal/api/s2s/user/userget.go
@@ -0,0 +1,114 @@
+/*
+ GoToSocial
+ Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+package user
+
+import (
+ "net/http"
+
+ "github.com/gin-gonic/gin"
+ "github.com/sirupsen/logrus"
+ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
+)
+
+// UsersGETHandler should be served at https://example.org/users/:username.
+//
+// The goal here is to return the activitypub representation of an account
+// in the form of a vocab.ActivityStreamsPerson. This should only be served
+// to REMOTE SERVERS that present a valid signature on the GET request, on
+// behalf of a user, otherwise we risk leaking information about users publicly.
+//
+// And of course, the request should be refused if the account or server making the
+// request is blocked.
+func (m *Module) UsersGETHandler(c *gin.Context) {
+ l := m.log.WithFields(logrus.Fields{
+ "func": "UsersGETHandler",
+ "url": c.Request.RequestURI,
+ })
+
+ requestedUsername := c.Param(UsernameKey)
+ if requestedUsername == "" {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "no username specified in request"})
+ return
+ }
+
+ // make sure this actually an AP request
+ format := c.NegotiateFormat(ActivityPubAcceptHeaders...)
+ if format == "" {
+ c.JSON(http.StatusNotAcceptable, gin.H{"error": "could not negotiate format with given Accept header(s)"})
+ return
+ }
+ l.Tracef("negotiated format: %s", format)
+
+ // get the account the request is referring to
+ requestedAccount := >smodel.Account{}
+ if err := m.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil {
+ l.Errorf("database error getting account with username %s: %s", requestedUsername, err)
+ // we'll just return not authorized here to avoid giving anything away
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
+ return
+ }
+
+ // and create a transport for it
+ transport, err := m.federator.TransportController().NewTransport(requestedAccount.PublicKeyURI, requestedAccount.PrivateKey)
+ if err != nil {
+ l.Errorf("error creating transport for username %s: %s", requestedUsername, err)
+ // we'll just return not authorized here to avoid giving anything away
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
+ return
+ }
+
+ // authenticate the request
+ authentication, err := federation.AuthenticateFederatedRequest(transport, c.Request)
+ if err != nil {
+ l.Errorf("error authenticating GET user request: %s", err)
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
+ return
+ }
+
+ if !authentication.Authenticated {
+ l.Debug("request not authorized")
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
+ return
+ }
+
+ requestingAccount := >smodel.Account{}
+ if authentication.RequestingPublicKeyID != nil {
+ if err := m.db.GetWhere("public_key_uri", authentication.RequestingPublicKeyID.String(), requestingAccount); err != nil {
+
+ }
+ }
+
+ authorization, err := federation.AuthorizeFederatedRequest
+
+ person, err := m.tc.AccountToAS(requestedAccount)
+ if err != nil {
+ l.Errorf("error converting account to ap person: %s", err)
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
+ return
+ }
+
+ data, err := person.Serialize()
+ if err != nil {
+ l.Errorf("error serializing user: %s", err)
+ c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
+ return
+ }
+
+ c.JSON(http.StatusOK, data)
+}
diff --git a/internal/federation/clock.go b/internal/federation/clock.go
index 2bd82e34e..f0d6f5e84 100644
--- a/internal/federation/clock.go
+++ b/internal/federation/clock.go
@@ -38,5 +38,5 @@ func (c *Clock) Now() time.Time {
}
func NewClock() pub.Clock {
- return &Clock{}
+ return &Clock{}
}
diff --git a/internal/federation/commonbehavior.go b/internal/federation/commonbehavior.go
index 51db2f640..9274e78b4 100644
--- a/internal/federation/commonbehavior.go
+++ b/internal/federation/commonbehavior.go
@@ -26,33 +26,10 @@ import (
"github.com/go-fed/activity/pub"
"github.com/go-fed/activity/streams/vocab"
- "github.com/sirupsen/logrus"
- "github.com/superseriousbusiness/gotosocial/internal/config"
- "github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/transport"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
-// commonBehavior implements the GTSCommonBehavior interface
-type commonBehavior struct {
- db db.DB
- log *logrus.Logger
- config *config.Config
- transportController transport.Controller
-}
-
-// newCommonBehavior returns an implementation of the GTSCommonBehavior interface that uses the given db, log, config, and transportController.
-// This interface is a superset of the pub.CommonBehavior interface, so it can be used anywhere that interface would be used.
-func newCommonBehavior(db db.DB, log *logrus.Logger, config *config.Config, transportController transport.Controller) pub.CommonBehavior {
- return &commonBehavior{
- db: db,
- log: log,
- config: config,
- transportController: transportController,
- }
-}
-
/*
GOFED COMMON BEHAVIOR INTERFACE
Contains functions required for both the Social API and Federating Protocol.
@@ -79,7 +56,7 @@ func newCommonBehavior(db db.DB, log *logrus.Logger, config *config.Config, tran
// Finally, if the authentication and authorization succeeds, then
// authenticated must be true and error nil. The request will continue
// to be processed.
-func (c *commonBehavior) 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 outboxes and inboxes through
// the CLIENT API, not through the federation API, so we just do nothing here.
return nil, false, nil
@@ -104,7 +81,7 @@ func (c *commonBehavior) AuthenticateGetInbox(ctx context.Context, w http.Respon
// Finally, if the authentication and authorization succeeds, then
// authenticated must be true and error nil. The request will continue
// to be processed.
-func (c *commonBehavior) 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 outboxes and inboxes through
// the CLIENT API, not through the federation API, so we just do nothing here.
return nil, false, nil
@@ -118,7 +95,7 @@ func (c *commonBehavior) AuthenticateGetOutbox(ctx context.Context, w http.Respo
//
// Always called, regardless whether the Federated Protocol or Social
// API is enabled.
-func (c *commonBehavior) 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 outboxes and inboxes through
// the CLIENT API, not through the federation API, so we just do nothing here.
return nil, nil
@@ -147,7 +124,7 @@ func (c *commonBehavior) GetOutbox(ctx context.Context, r *http.Request) (vocab.
// Note that the library will not maintain a long-lived pointer to the
// returned Transport so that any private credentials are able to be
// garbage collected.
-func (c *commonBehavior) 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 err error
@@ -167,16 +144,9 @@ func (c *commonBehavior) NewTransport(ctx context.Context, actorBoxIRI *url.URL,
}
account := >smodel.Account{}
- if err := c.db.GetLocalAccountByUsername(username, account); err != nil {
+ if err := f.db.GetLocalAccountByUsername(username, account); err != nil {
return nil, fmt.Errorf("error getting account with username %s from the db: %s", username, err)
}
- return c.transportController.NewTransport(account.PublicKeyURI, account.PrivateKey)
-}
-
-// GetUser returns the activitypub representation of the user specified in the path of r, eg https://example.org/users/example_user.
-// AuthenticateGetUser should be called first, to make sure the requester has permission to view the requested user.
-// The returned user should be a translation from a *gtsmodel.Account to a serializable ActivityStreamsPerson.
-func (c *commonBehavior) GetUser(ctx context.Context, r *http.Request) (vocab.ActivityStreamsPerson, error) {
- return nil, nil
+ return f.transportController.NewTransport(account.PublicKeyURI, account.PrivateKey)
}
diff --git a/internal/federation/federatingprotocol.go b/internal/federation/federatingprotocol.go
index 657cd8081..299b82586 100644
--- a/internal/federation/federatingprotocol.go
+++ b/internal/federation/federatingprotocol.go
@@ -28,34 +28,11 @@ import (
"github.com/go-fed/activity/pub"
"github.com/go-fed/activity/streams/vocab"
"github.com/sirupsen/logrus"
- "github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/transport"
- "github.com/superseriousbusiness/gotosocial/internal/typeutils"
"github.com/superseriousbusiness/gotosocial/internal/util"
)
-// federatingProtocol implements the go-fed federating protocol interface
-type federatingProtocol struct {
- db db.DB
- log *logrus.Logger
- config *config.Config
- transportController transport.Controller
- typeConverter typeutils.TypeConverter
-}
-
-// newFederatingProtocol returns the gotosocial implementation of the GTSFederatingProtocol interface
-func newFederatingProtocol(db db.DB, log *logrus.Logger, config *config.Config, transportController transport.Controller, typeConverter typeutils.TypeConverter) pub.FederatingProtocol {
- return &federatingProtocol{
- db: db,
- log: log,
- config: config,
- transportController: transportController,
- typeConverter: typeConverter,
- }
-}
-
/*
GO FED FEDERATING PROTOCOL INTERFACE
FederatingProtocol contains behaviors an application needs to satisfy for the
@@ -82,7 +59,7 @@ func newFederatingProtocol(db db.DB, log *logrus.Logger, config *config.Config,
// PostInbox. In this case, the DelegateActor implementation must not
// write a response to the ResponseWriter as is expected that the caller
// to PostInbox will do so when handling the error.
-func (f *federatingProtocol) 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) {
l := f.log.WithFields(logrus.Fields{
"func": "PostInboxRequestBodyHook",
"useragent": r.UserAgent(),
@@ -115,7 +92,7 @@ func (f *federatingProtocol) PostInboxRequestBodyHook(ctx context.Context, r *ht
// Finally, if the authentication and authorization succeeds, then
// authenticated must be true and error nil. The request will continue
// to be processed.
-func (f *federatingProtocol) 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) {
l := f.log.WithFields(logrus.Fields{
"func": "AuthenticatePostInbox",
"useragent": r.UserAgent(),
@@ -133,12 +110,7 @@ func (f *federatingProtocol) AuthenticatePostInbox(ctx context.Context, w http.R
return ctx, false, errors.New("requested account not parsebale from context")
}
- transport, err := f.transportController.NewTransport(requestedAccount.PublicKeyURI, requestedAccount.PrivateKey)
- if err != nil {
- return ctx, false, fmt.Errorf("error creating transport: %s", err)
- }
-
- publicKeyOwnerURI, err := AuthenticateFederatedRequest(transport, r)
+ publicKeyOwnerURI, err := f.AuthenticateFederatedRequest(requestedAccount.Username, r)
if err != nil {
l.Debugf("request not authenticated: %s", err)
return ctx, false, fmt.Errorf("not authenticated: %s", err)
@@ -151,9 +123,9 @@ func (f *federatingProtocol) AuthenticatePostInbox(ctx context.Context, w http.R
return ctx, false, fmt.Errorf("error getting requesting account with public key id %s: %s", publicKeyOwnerURI.String(), err)
}
- // we just don't know this account (yet) so try to dereference it
+ // we don't know this account (yet) so let's dereference it right now
// TODO: slow-fed
- person, err := DereferenceAccount(transport, publicKeyOwnerURI)
+ person, err := f.DereferenceRemoteAccount(requestedAccount.Username, publicKeyOwnerURI)
if err != nil {
return ctx, false, fmt.Errorf("error dereferencing account with public key id %s: %s", publicKeyOwnerURI.String(), err)
}
@@ -182,7 +154,7 @@ func (f *federatingProtocol) AuthenticatePostInbox(ctx context.Context, w http.R
// Finally, if the authentication and authorization succeeds, then
// blocked must be false and error nil. The request will continue
// to be processed.
-func (f *federatingProtocol) Blocked(ctx context.Context, actorIRIs []*url.URL) (bool, error) {
+func (f *federator) Blocked(ctx context.Context, actorIRIs []*url.URL) (bool, error) {
// TODO
return false, nil
}
@@ -206,7 +178,7 @@ func (f *federatingProtocol) Blocked(ctx context.Context, actorIRIs []*url.URL)
//
// Applications are not expected to handle every single ActivityStreams
// type and extension. The unhandled ones are passed to DefaultCallback.
-func (f *federatingProtocol) FederatingCallbacks(ctx context.Context) (pub.FederatingWrappedCallbacks, []interface{}, error) {
+func (f *federator) FederatingCallbacks(ctx context.Context) (pub.FederatingWrappedCallbacks, []interface{}, error) {
// TODO
return pub.FederatingWrappedCallbacks{}, nil, nil
}
@@ -218,7 +190,7 @@ func (f *federatingProtocol) FederatingCallbacks(ctx context.Context) (pub.Feder
// Applications are not expected to handle every single ActivityStreams
// type and extension, so the unhandled ones are passed to
// DefaultCallback.
-func (f *federatingProtocol) DefaultCallback(ctx context.Context, activity pub.Activity) error {
+func (f *federator) DefaultCallback(ctx context.Context, activity pub.Activity) error {
l := f.log.WithFields(logrus.Fields{
"func": "DefaultCallback",
"aptype": activity.GetTypeName(),
@@ -231,7 +203,7 @@ func (f *federatingProtocol) DefaultCallback(ctx context.Context, activity pub.A
// an activity to determine if inbox forwarding needs to occur.
//
// Zero or negative numbers indicate infinite recursion.
-func (f *federatingProtocol) MaxInboxForwardingRecursionDepth(ctx context.Context) int {
+func (f *federator) MaxInboxForwardingRecursionDepth(ctx context.Context) int {
// TODO
return 0
}
@@ -241,7 +213,7 @@ func (f *federatingProtocol) MaxInboxForwardingRecursionDepth(ctx context.Contex
// delivery.
//
// Zero or negative numbers indicate infinite recursion.
-func (f *federatingProtocol) MaxDeliveryRecursionDepth(ctx context.Context) int {
+func (f *federator) MaxDeliveryRecursionDepth(ctx context.Context) int {
// TODO
return 0
}
@@ -253,7 +225,7 @@ func (f *federatingProtocol) MaxDeliveryRecursionDepth(ctx context.Context) int
//
// The activity is provided as a reference for more intelligent
// logic to be used, but the implementation must not modify it.
-func (f *federatingProtocol) 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
return nil, nil
}
@@ -266,7 +238,7 @@ func (f *federatingProtocol) FilterForwarding(ctx context.Context, potentialReci
//
// Always called, regardless whether the Federated Protocol or Social
// API is enabled.
-func (f *federatingProtocol) 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 outboxes and inboxes through
// the CLIENT API, not through the federation API, so we just do nothing here.
return nil, nil
diff --git a/internal/federation/federator.go b/internal/federation/federator.go
index 4db469bde..f280de828 100644
--- a/internal/federation/federator.go
+++ b/internal/federation/federator.go
@@ -19,11 +19,13 @@
package federation
import (
+ "net/http"
+ "net/url"
+
"github.com/go-fed/activity/pub"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/message"
"github.com/superseriousbusiness/gotosocial/internal/transport"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
)
@@ -32,35 +34,36 @@ import (
type Federator interface {
FederatingActor() pub.FederatingActor
TransportController() transport.Controller
- FederatingProtocol() pub.FederatingProtocol
- CommonBehavior() pub.CommonBehavior
+ AuthenticateFederatedRequest(username string, r *http.Request) (*url.URL, error)
+ pub.CommonBehavior
+ pub.FederatingProtocol
}
type federator struct {
- actor pub.FederatingActor
- processor message.Processor
- federatingProtocol pub.FederatingProtocol
- commonBehavior pub.CommonBehavior
+ config *config.Config
+ db db.DB
clock pub.Clock
+ typeConverter typeutils.TypeConverter
transportController transport.Controller
+ actor pub.FederatingActor
+ log *logrus.Logger
}
// NewFederator returns a new federator
-func NewFederator(db db.DB, transportController transport.Controller, config *config.Config, log *logrus.Logger, processor message.Processor, typeConverter typeutils.TypeConverter) Federator {
+func NewFederator(db db.DB, transportController transport.Controller, config *config.Config, log *logrus.Logger, typeConverter typeutils.TypeConverter) Federator {
clock := &Clock{}
- federatingProtocol := newFederatingProtocol(db, log, config, transportController, typeConverter)
- commonBehavior := newCommonBehavior(db, log, config, transportController)
- actor := newFederatingActor(commonBehavior, federatingProtocol, db.Federation(), clock)
-
- return &federator{
- actor: actor,
- processor: processor,
- federatingProtocol: federatingProtocol,
- commonBehavior: commonBehavior,
- clock: clock,
+ f := &federator{
+ config: config,
+ db: db,
+ clock: &Clock{},
+ typeConverter: typeConverter,
transportController: transportController,
+ log: log,
}
+ actor := newFederatingActor(f, f, db.Federation(), clock)
+ f.actor = actor
+ return f
}
func (f *federator) FederatingActor() pub.FederatingActor {
@@ -70,11 +73,3 @@ func (f *federator) FederatingActor() pub.FederatingActor {
func (f *federator) TransportController() transport.Controller {
return f.transportController
}
-
-func (f *federator) FederatingProtocol() pub.FederatingProtocol {
- return f.federatingProtocol
-}
-
-func (f *federator) CommonBehavior() pub.CommonBehavior {
- return f.commonBehavior
-}
diff --git a/internal/federation/federator_test.go b/internal/federation/federator_test.go
index e1b2db062..93989bfd8 100644
--- a/internal/federation/federator_test.go
+++ b/internal/federation/federator_test.go
@@ -39,7 +39,6 @@ import (
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/federation"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/message"
"github.com/superseriousbusiness/gotosocial/internal/storage"
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
"github.com/superseriousbusiness/gotosocial/internal/util"
@@ -51,7 +50,6 @@ type ProtocolTestSuite struct {
config *config.Config
db db.DB
log *logrus.Logger
- processor message.Processor
storage storage.Storage
typeConverter typeutils.TypeConverter
accounts map[string]*gtsmodel.Account
@@ -65,7 +63,6 @@ func (suite *ProtocolTestSuite) SetupSuite() {
suite.db = testrig.NewTestDB()
suite.log = testrig.NewTestLog()
suite.storage = testrig.NewTestStorage()
- suite.processor = testrig.NewTestProcessor(suite.db, suite.storage)
suite.typeConverter = testrig.NewTestTypeConverter(suite.db)
suite.accounts = testrig.NewTestAccounts()
suite.activities = testrig.NewTestActivities(suite.accounts)
@@ -92,7 +89,7 @@ func (suite *ProtocolTestSuite) TestPostInboxRequestBodyHook() {
return nil, nil
}))
// setup module being tested
- federator := federation.NewFederator(suite.db, tc, suite.config, suite.log, suite.processor, suite.typeConverter)
+ federator := federation.NewFederator(suite.db, tc, suite.config, suite.log, suite.typeConverter)
// setup request
ctx := context.Background()
@@ -158,7 +155,7 @@ func (suite *ProtocolTestSuite) TestAuthenticatePostInbox() {
}))
// now setup module being tested, with the mock transport controller
- federator := federation.NewFederator(suite.db, tc, suite.config, suite.log, suite.processor, suite.typeConverter)
+ federator := federation.NewFederator(suite.db, tc, suite.config, suite.log, suite.typeConverter)
// setup request
ctx := context.Background()
diff --git a/internal/federation/util.go b/internal/federation/util.go
index 28fa45826..1efaa54b5 100644
--- a/internal/federation/util.go
+++ b/internal/federation/util.go
@@ -101,15 +101,16 @@ func getPublicKeyFromResponse(c context.Context, b []byte, keyID *url.URL) (voca
// Authenticate in this case is defined as just making sure that the http request is actually signed by whoever claims
// to have signed it, by fetching the public key from the signature and checking it against the remote public key.
//
-// The provided transport will be used to dereference the public key ID of the request signature. Ideally you should pass in a transport
-// with the credentials of the user *being requested*, so that the remote server can decide how to handle the request based on who's making it.
-// Ie., if the request on this server is for https://example.org/users/some_username then you should pass in a transport that's been initialized with
-// the keys belonging to local user 'some_username'. The remote server will then know that this is the user making the
-// dereferencing request, and they can decide to allow or deny the request depending on their settings.
+// The provided username will be used to generate a transport for making remote requests/derefencing the public key ID of the request signature.
+// Ideally you should pass in the username of the user *being requested*, so that the remote server can decide how to handle the request based on who's making it.
+// Ie., if the request on this server is for https://example.org/users/some_username then you should pass in the username 'some_username'.
+// The remote server will then know that this is the user making the dereferencing request, and they can decide to allow or deny the request depending on their settings.
+//
+// Note that it is also valid to pass in an empty string here, in which case the keys of the instance account will be used.
//
// Note that this function *does not* dereference the remote account that the signature key is associated with, but it will
// return the owner of the public key, so that other functions can dereference it with that, as required.
-func AuthenticateFederatedRequest(transport pub.Transport, r *http.Request) (*url.URL, error) {
+func (f *federator) AuthenticateFederatedRequest(username string, r *http.Request) (*url.URL, error) {
verifier, err := httpsig.NewVerifier(r)
if err != nil {
return nil, fmt.Errorf("could not create http sig verifier: %s", err)
@@ -122,7 +123,12 @@ func AuthenticateFederatedRequest(transport pub.Transport, r *http.Request) (*ur
return nil, fmt.Errorf("could not parse key id into a url: %s", err)
}
- // use the new transport to fetch the requesting public key from the remote server
+ transport, err := f.GetTransportForUser(username)
+ if err != nil {
+ return nil, fmt.Errorf("transport err: %s", err)
+ }
+
+ // The actual http call to the remote server is made right here in the Dereference function.
b, err := transport.Dereference(context.Background(), requestingPublicKeyID)
if err != nil {
return nil, fmt.Errorf("error deferencing key %s: %s", requestingPublicKeyID.String(), err)
@@ -134,17 +140,13 @@ func AuthenticateFederatedRequest(transport pub.Transport, r *http.Request) (*ur
return nil, fmt.Errorf("error getting key %s from response %s: %s", requestingPublicKeyID.String(), string(b), err)
}
- pkOwnerProp := requestingPublicKey.GetW3IDSecurityV1Owner()
- if pkOwnerProp == nil || !pkOwnerProp.IsIRI() {
- return nil, errors.New("publicKeyOwner property is not provided or it is not embedded as a value")
- }
- pkOwnerURI := pkOwnerProp.GetIRI()
-
+ // we should be able to get the actual key embedded in the vocab.W3IDSecurityV1PublicKey
pkPemProp := requestingPublicKey.GetW3IDSecurityV1PublicKeyPem()
if pkPemProp == nil || !pkPemProp.IsXMLSchemaString() {
return nil, errors.New("publicKeyPem property is not provided or it is not embedded as a value")
}
+ // and decode the PEM so that we can parse it as a golang public key
pubKeyPem := pkPemProp.Get()
block, _ := pem.Decode([]byte(pubKeyPem))
if block == nil || block.Type != "PUBLIC KEY" {
@@ -162,14 +164,26 @@ func AuthenticateFederatedRequest(transport pub.Transport, r *http.Request) (*ur
return nil, fmt.Errorf("error verifying key %s: %s", requestingPublicKeyID.String(), err)
}
- // all good!
+ // all good! we just need the URI of the key owner to return
+ pkOwnerProp := requestingPublicKey.GetW3IDSecurityV1Owner()
+ if pkOwnerProp == nil || !pkOwnerProp.IsIRI() {
+ return nil, errors.New("publicKeyOwner property is not provided or it is not embedded as a value")
+ }
+ pkOwnerURI := pkOwnerProp.GetIRI()
+
return pkOwnerURI, nil
}
-func DereferenceAccount(transport pub.Transport, id *url.URL) (vocab.ActivityStreamsPerson, error) {
- b, err := transport.Dereference(context.Background(), id)
+func (f *federator) DereferenceRemoteAccount(username string, remoteAccountID *url.URL) (vocab.ActivityStreamsPerson, error) {
+
+ transport, err := f.GetTransportForUser(username)
if err != nil {
- return nil, fmt.Errorf("error deferencing %s: %s", id.String(), err)
+ return nil, fmt.Errorf("transport err: %s", err)
+ }
+
+ b, err := transport.Dereference(context.Background(), remoteAccountID)
+ if err != nil {
+ return nil, fmt.Errorf("error deferencing %s: %s", remoteAccountID.String(), err)
}
m := make(map[string]interface{})
@@ -195,3 +209,25 @@ func DereferenceAccount(transport pub.Transport, id *url.URL) (vocab.ActivityStr
return nil, fmt.Errorf("type name %s not supported", t.GetTypeName())
}
+
+func (f *federator) GetTransportForUser(username string) (pub.Transport, error) {
+ // We need an account to use to create a transport for dereferecing the signature.
+ // If a username has been given, we can fetch the account with that username and use it.
+ // Otherwise, we can take the instance account and use those credentials to make the request.
+ ourAccount := >smodel.Account{}
+ var u string
+ if username == "" {
+ u = f.config.Host
+ } else {
+ u = username
+ }
+ if err := f.db.GetLocalAccountByUsername(u, ourAccount); err != nil {
+ return nil, fmt.Errorf("error getting account %s from db: %s", username, err)
+ }
+
+ transport, err := f.TransportController().NewTransport(ourAccount.PublicKeyURI, ourAccount.PrivateKey)
+ if err != nil {
+ return nil, fmt.Errorf("error creating transport for user %s: %s", username, err)
+ }
+ return transport, nil
+}
diff --git a/internal/gotosocial/actions.go b/internal/gotosocial/actions.go
index e38e12234..8d3142f84 100644
--- a/internal/gotosocial/actions.go
+++ b/internal/gotosocial/actions.go
@@ -72,15 +72,15 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr
// build backend handlers
mediaHandler := media.New(c, dbService, storageBackend, log)
oauthServer := oauth.New(dbService, log)
- processor := message.NewProcessor(c, typeConverter, oauthServer, mediaHandler, storageBackend, dbService, log)
+ transportController := transport.NewController(c, &federation.Clock{}, http.DefaultClient, log)
+ federator := federation.NewFederator(dbService, transportController, c, log, typeConverter)
+ processor := message.NewProcessor(c, typeConverter, federator, oauthServer, mediaHandler, storageBackend, dbService, log)
if err := processor.Start(); err != nil {
return fmt.Errorf("error starting processor: %s", err)
}
- transportController := transport.NewController(c, &federation.Clock{}, http.DefaultClient, log)
- federator := federation.NewFederator(dbService, transportController, c, log, processor, typeConverter)
// build client api modules
- authModule := auth.New(c, processor, log)
+ authModule := auth.New(c, dbService, oauthServer, log)
accountModule := account.New(c, processor, log)
appsModule := app.New(c, processor, log)
mm := mediaModule.New(c, processor, log)
diff --git a/internal/message/apuserprocess.go b/internal/message/apuserprocess.go
new file mode 100644
index 000000000..ac85b22e4
--- /dev/null
+++ b/internal/message/apuserprocess.go
@@ -0,0 +1,63 @@
+package message
+
+import (
+ "net/http"
+)
+
+func (p *processor) GetAPUser(requestHeaders http.Header, username string) (interface{}, error) {
+
+ // // get the account the request is referring to
+ // requestedAccount := >smodel.Account{}
+ // if err := m.db.GetLocalAccountByUsername(username, requestedAccount); err != nil {
+ // return nil, NewErrorNotAuthorized(fmt.Errorf("database error getting account with username %s: %s", username, err))
+ // }
+
+ // // and create a transport for it
+ // transport, err := p.federator.TransportController().NewTransport(requestedAccount.PublicKeyURI, requestedAccount.PrivateKey)
+ // if err != nil {
+ // l.Errorf("error creating transport for username %s: %s", requestedUsername, err)
+ // // we'll just return not authorized here to avoid giving anything away
+ // c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
+ // return
+ // }
+
+ // // authenticate the request
+ // authentication, err := federation.AuthenticateFederatedRequest(transport, c.Request)
+ // if err != nil {
+ // l.Errorf("error authenticating GET user request: %s", err)
+ // c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
+ // return
+ // }
+
+ // if !authentication.Authenticated {
+ // l.Debug("request not authorized")
+ // c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
+ // return
+ // }
+
+ // requestingAccount := >smodel.Account{}
+ // if authentication.RequestingPublicKeyID != nil {
+ // if err := m.db.GetWhere("public_key_uri", authentication.RequestingPublicKeyID.String(), requestingAccount); err != nil {
+
+ // }
+ // }
+
+ // authorization, err := federation.AuthorizeFederatedRequest
+
+ // person, err := m.tc.AccountToAS(requestedAccount)
+ // if err != nil {
+ // l.Errorf("error converting account to ap person: %s", err)
+ // c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
+ // return
+ // }
+
+ // data, err := person.Serialize()
+ // if err != nil {
+ // l.Errorf("error serializing user: %s", err)
+ // c.JSON(http.StatusUnauthorized, gin.H{"error": "not authorized"})
+ // return
+ // }
+
+ // c.JSON(http.StatusOK, data)
+ return nil, nil
+}
diff --git a/internal/message/mediaprocess.go b/internal/message/mediaprocess.go
index 65181bef4..77b387df3 100644
--- a/internal/message/mediaprocess.go
+++ b/internal/message/mediaprocess.go
@@ -130,10 +130,12 @@ func (p *processor) MediaGet(authed *oauth.Auth, form *apimodel.GetContentReques
return nil, NewErrorNotFound(fmt.Errorf("block status could not be established between accounts %s and %s: %s", form.AccountID, authed.Account.ID, err))
}
if blocked {
- return nil, NewErrorNotFound(fmt.Errorf("block exists between accounts %s and %s: %s", form.AccountID, authed.Account.ID))
+ return nil, NewErrorNotFound(fmt.Errorf("block exists between accounts %s and %s", form.AccountID, authed.Account.ID))
}
}
+ // the way we store emojis is a little different from the way we store other attachments,
+ // so we need to take different steps depending on the media type being requested
content := &apimodel.Content{}
var storagePath string
switch mediaType {
@@ -155,7 +157,7 @@ func (p *processor) MediaGet(authed *oauth.Auth, form *apimodel.GetContentReques
default:
return nil, NewErrorNotFound(fmt.Errorf("media size %s not recognized for emoji", mediaSize))
}
- case media.Attachment:
+ case media.Attachment, media.Header, media.Avatar:
a := >smodel.MediaAttachment{}
if err := p.db.GetByID(wantedMediaID, a); err != nil {
return nil, NewErrorNotFound(fmt.Errorf("attachment %s could not be taken from the db: %s", wantedMediaID, err))
diff --git a/internal/message/processor.go b/internal/message/processor.go
index 69728a6a7..2e59a0231 100644
--- a/internal/message/processor.go
+++ b/internal/message/processor.go
@@ -23,6 +23,7 @@ import (
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/federation"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/media"
"github.com/superseriousbusiness/gotosocial/internal/oauth"
@@ -39,8 +40,12 @@ import (
type Processor interface {
// ToClientAPI returns a channel for putting in messages that need to go to the gts client API.
ToClientAPI() chan ToClientAPI
+ // FromClientAPI returns a channel for putting messages in that come from the client api going to the processor
+ FromClientAPI() chan FromClientAPI
// ToFederator returns a channel for putting in messages that need to go to the federator (activitypub).
ToFederator() chan ToFederator
+ // FromFederator returns a channel for putting messages in that come from the federator going into the processor
+ FromFederator() chan FromFederator
/*
API-FACING PROCESSING FUNCTIONS
@@ -86,53 +91,67 @@ type Processor interface {
// processor just implements the Processor interface
type processor struct {
// federator pub.FederatingActor
- toClientAPI chan ToClientAPI
- toFederator chan ToFederator
- stop chan interface{}
- log *logrus.Logger
- config *config.Config
- tc typeutils.TypeConverter
- oauthServer oauth.Server
- mediaHandler media.Handler
- storage storage.Storage
- db db.DB
+ toClientAPI chan ToClientAPI
+ fromClientAPI chan FromClientAPI
+ toFederator chan ToFederator
+ fromFederator chan FromFederator
+ federator federation.Federator
+ stop chan interface{}
+ log *logrus.Logger
+ config *config.Config
+ tc typeutils.TypeConverter
+ oauthServer oauth.Server
+ mediaHandler media.Handler
+ storage storage.Storage
+ db db.DB
}
// NewProcessor returns a new Processor that uses the given federator and logger
-func NewProcessor(config *config.Config, tc typeutils.TypeConverter, oauthServer oauth.Server, mediaHandler media.Handler, storage storage.Storage, db db.DB, log *logrus.Logger) Processor {
+func NewProcessor(config *config.Config, tc typeutils.TypeConverter, federator federation.Federator, oauthServer oauth.Server, mediaHandler media.Handler, storage storage.Storage, db db.DB, log *logrus.Logger) Processor {
return &processor{
- toClientAPI: make(chan ToClientAPI, 100),
- toFederator: make(chan ToFederator, 100),
- stop: make(chan interface{}),
- log: log,
- config: config,
- tc: tc,
- oauthServer: oauthServer,
- mediaHandler: mediaHandler,
- storage: storage,
- db: db,
+ toClientAPI: make(chan ToClientAPI, 100),
+ fromClientAPI: make(chan FromClientAPI, 100),
+ toFederator: make(chan ToFederator, 100),
+ fromFederator: make(chan FromFederator, 100),
+ federator: federator,
+ stop: make(chan interface{}),
+ log: log,
+ config: config,
+ tc: tc,
+ oauthServer: oauthServer,
+ mediaHandler: mediaHandler,
+ storage: storage,
+ db: db,
}
}
-func (d *processor) ToClientAPI() chan ToClientAPI {
- return d.toClientAPI
+func (p *processor) ToClientAPI() chan ToClientAPI {
+ return p.toClientAPI
}
-func (d *processor) ToFederator() chan ToFederator {
- return d.toFederator
+func (p *processor) FromClientAPI() chan FromClientAPI {
+ return p.fromClientAPI
+}
+
+func (p *processor) ToFederator() chan ToFederator {
+ return p.toFederator
+}
+
+func (p *processor) FromFederator() chan FromFederator {
+ return p.fromFederator
}
// Start starts the Processor, reading from its channels and passing messages back and forth.
-func (d *processor) Start() error {
+func (p *processor) Start() error {
go func() {
DistLoop:
for {
select {
- case clientMsg := <-d.toClientAPI:
- d.log.Infof("received message TO client API: %+v", clientMsg)
- case federatorMsg := <-d.toFederator:
- d.log.Infof("received message TO federator: %+v", federatorMsg)
- case <-d.stop:
+ case clientMsg := <-p.toClientAPI:
+ p.log.Infof("received message TO client API: %+v", clientMsg)
+ case federatorMsg := <-p.toFederator:
+ p.log.Infof("received message TO federator: %+v", federatorMsg)
+ case <-p.stop:
break DistLoop
}
}
@@ -142,8 +161,8 @@ func (d *processor) Start() error {
// Stop stops the processor cleanly, finishing handling any remaining messages before closing down.
// TODO: empty message buffer properly before stopping otherwise we'll lose federating messages.
-func (d *processor) Stop() error {
- close(d.stop)
+func (p *processor) Stop() error {
+ close(p.stop)
return nil
}
@@ -154,9 +173,23 @@ type ToClientAPI struct {
Activity interface{}
}
+// FromClientAPI wraps a message that travels from client API into the processor
+type FromClientAPI struct {
+ APObjectType gtsmodel.ActivityStreamsObject
+ APActivityType gtsmodel.ActivityStreamsActivity
+ Activity interface{}
+}
+
// ToFederator wraps a message that travels from the processor into the federator
type ToFederator struct {
APObjectType gtsmodel.ActivityStreamsObject
APActivityType gtsmodel.ActivityStreamsActivity
Activity interface{}
}
+
+// FromFederator wraps a message that travels from the federator into the processor
+type FromFederator struct {
+ APObjectType gtsmodel.ActivityStreamsObject
+ APActivityType gtsmodel.ActivityStreamsActivity
+ Activity interface{}
+}
diff --git a/internal/oauth/server.go b/internal/oauth/server.go
index ca749aece..7877d667e 100644
--- a/internal/oauth/server.go
+++ b/internal/oauth/server.go
@@ -23,10 +23,8 @@ import (
"fmt"
"net/http"
- "github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"github.com/superseriousbusiness/gotosocial/internal/db"
- "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/oauth2/v4"
"github.com/superseriousbusiness/oauth2/v4/errors"
"github.com/superseriousbusiness/oauth2/v4/manage"
@@ -66,82 +64,53 @@ type s struct {
log *logrus.Logger
}
-// Auth wraps an authorized token, application, user, and account.
-// It is used in the functions GetAuthed and MustAuth.
-// Because the user might *not* be authed, any of the fields in this struct
-// might be nil, so make sure to check that when you're using this struct anywhere.
-type Auth struct {
- Token oauth2.TokenInfo
- Application *gtsmodel.Application
- User *gtsmodel.User
- Account *gtsmodel.Account
-}
+// New returns a new oauth server that implements the Server interface
+func New(database db.DB, log *logrus.Logger) Server {
+ ts := newTokenStore(context.Background(), database, log)
+ cs := NewClientStore(database)
-// Authed is a convenience function for returning an Authed struct from a gin context.
-// In essence, it tries to extract a token, application, user, and account from the context,
-// and then sets them on a struct for convenience.
-//
-// If any are not present in the context, they will be set to nil on the returned Authed struct.
-//
-// If *ALL* are not present, then nil and an error will be returned.
-//
-// If something goes wrong during parsing, then nil and an error will be returned (consider this not authed).
-// Authed is like GetAuthed, but will fail if one of the requirements is not met.
-func Authed(c *gin.Context, requireToken bool, requireApp bool, requireUser bool, requireAccount bool) (*Auth, error) {
- ctx := c.Copy()
- a := &Auth{}
- var i interface{}
- var ok bool
+ manager := manage.NewDefaultManager()
+ manager.MapTokenStorage(ts)
+ manager.MapClientStorage(cs)
+ manager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg)
+ sc := &server.Config{
+ TokenType: "Bearer",
+ // Must follow the spec.
+ AllowGetAccessRequest: false,
+ // Support only the non-implicit flow.
+ AllowedResponseTypes: []oauth2.ResponseType{oauth2.Code},
+ // Allow:
+ // - Authorization Code (for first & third parties)
+ // - Client Credentials (for applications)
+ AllowedGrantTypes: []oauth2.GrantType{
+ oauth2.AuthorizationCode,
+ oauth2.ClientCredentials,
+ },
+ AllowedCodeChallengeMethods: []oauth2.CodeChallengeMethod{oauth2.CodeChallengePlain},
+ }
- i, ok = ctx.Get(SessionAuthorizedToken)
- if ok {
- parsed, ok := i.(oauth2.TokenInfo)
- if !ok {
- return nil, errors.New("could not parse token from session context")
+ srv := server.NewServer(sc, manager)
+ srv.SetInternalErrorHandler(func(err error) *errors.Response {
+ log.Errorf("internal oauth error: %s", err)
+ return nil
+ })
+
+ srv.SetResponseErrorHandler(func(re *errors.Response) {
+ log.Errorf("internal response error: %s", re.Error)
+ })
+
+ srv.SetUserAuthorizationHandler(func(w http.ResponseWriter, r *http.Request) (string, error) {
+ userID := r.FormValue("userid")
+ if userID == "" {
+ return "", errors.New("userid was empty")
}
- a.Token = parsed
+ return userID, nil
+ })
+ srv.SetClientInfoHandler(server.ClientFormHandler)
+ return &s{
+ server: srv,
+ log: log,
}
-
- i, ok = ctx.Get(SessionAuthorizedApplication)
- if ok {
- parsed, ok := i.(*gtsmodel.Application)
- if !ok {
- return nil, errors.New("could not parse application from session context")
- }
- a.Application = parsed
- }
-
- i, ok = ctx.Get(SessionAuthorizedUser)
- if ok {
- parsed, ok := i.(*gtsmodel.User)
- if !ok {
- return nil, errors.New("could not parse user from session context")
- }
- a.User = parsed
- }
-
- i, ok = ctx.Get(SessionAuthorizedAccount)
- if ok {
- parsed, ok := i.(*gtsmodel.Account)
- if !ok {
- return nil, errors.New("could not parse account from session context")
- }
- a.Account = parsed
- }
-
- if requireToken && a.Token == nil {
- return nil, errors.New("token not supplied")
- }
- if requireApp && a.Application == nil {
- return nil, errors.New("application not supplied")
- }
- if requireUser && a.User == nil {
- return nil, errors.New("user not supplied")
- }
- if requireAccount && a.Account == nil {
- return nil, errors.New("account not supplied")
- }
- return a, nil
}
// HandleTokenRequest wraps the oauth2 library's HandleTokenRequest function
@@ -199,52 +168,3 @@ func (s *s) GenerateUserAccessToken(ti oauth2.TokenInfo, clientSecret string, us
s.log.Tracef("obtained user-level access token: %+v", accessToken)
return accessToken, nil
}
-
-// New returns a new oauth server that implements the Server interface
-func New(database db.DB, log *logrus.Logger) Server {
- ts := newTokenStore(context.Background(), database, log)
- cs := NewClientStore(database)
-
- manager := manage.NewDefaultManager()
- manager.MapTokenStorage(ts)
- manager.MapClientStorage(cs)
- manager.SetAuthorizeCodeTokenCfg(manage.DefaultAuthorizeCodeTokenCfg)
- sc := &server.Config{
- TokenType: "Bearer",
- // Must follow the spec.
- AllowGetAccessRequest: false,
- // Support only the non-implicit flow.
- AllowedResponseTypes: []oauth2.ResponseType{oauth2.Code},
- // Allow:
- // - Authorization Code (for first & third parties)
- // - Client Credentials (for applications)
- AllowedGrantTypes: []oauth2.GrantType{
- oauth2.AuthorizationCode,
- oauth2.ClientCredentials,
- },
- AllowedCodeChallengeMethods: []oauth2.CodeChallengeMethod{oauth2.CodeChallengePlain},
- }
-
- srv := server.NewServer(sc, manager)
- srv.SetInternalErrorHandler(func(err error) *errors.Response {
- log.Errorf("internal oauth error: %s", err)
- return nil
- })
-
- srv.SetResponseErrorHandler(func(re *errors.Response) {
- log.Errorf("internal response error: %s", re.Error)
- })
-
- srv.SetUserAuthorizationHandler(func(w http.ResponseWriter, r *http.Request) (string, error) {
- userID := r.FormValue("userid")
- if userID == "" {
- return "", errors.New("userid was empty")
- }
- return userID, nil
- })
- srv.SetClientInfoHandler(server.ClientFormHandler)
- return &s{
- server: srv,
- log: log,
- }
-}
diff --git a/internal/oauth/util.go b/internal/oauth/util.go
new file mode 100644
index 000000000..378b81450
--- /dev/null
+++ b/internal/oauth/util.go
@@ -0,0 +1,86 @@
+package oauth
+
+import (
+ "github.com/gin-gonic/gin"
+ "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
+ "github.com/superseriousbusiness/oauth2/v4"
+ "github.com/superseriousbusiness/oauth2/v4/errors"
+)
+
+// Auth wraps an authorized token, application, user, and account.
+// It is used in the functions GetAuthed and MustAuth.
+// Because the user might *not* be authed, any of the fields in this struct
+// might be nil, so make sure to check that when you're using this struct anywhere.
+type Auth struct {
+ Token oauth2.TokenInfo
+ Application *gtsmodel.Application
+ User *gtsmodel.User
+ Account *gtsmodel.Account
+}
+
+// Authed is a convenience function for returning an Authed struct from a gin context.
+// In essence, it tries to extract a token, application, user, and account from the context,
+// and then sets them on a struct for convenience.
+//
+// If any are not present in the context, they will be set to nil on the returned Authed struct.
+//
+// If *ALL* are not present, then nil and an error will be returned.
+//
+// If something goes wrong during parsing, then nil and an error will be returned (consider this not authed).
+// Authed is like GetAuthed, but will fail if one of the requirements is not met.
+func Authed(c *gin.Context, requireToken bool, requireApp bool, requireUser bool, requireAccount bool) (*Auth, error) {
+ ctx := c.Copy()
+ a := &Auth{}
+ var i interface{}
+ var ok bool
+
+ i, ok = ctx.Get(SessionAuthorizedToken)
+ if ok {
+ parsed, ok := i.(oauth2.TokenInfo)
+ if !ok {
+ return nil, errors.New("could not parse token from session context")
+ }
+ a.Token = parsed
+ }
+
+ i, ok = ctx.Get(SessionAuthorizedApplication)
+ if ok {
+ parsed, ok := i.(*gtsmodel.Application)
+ if !ok {
+ return nil, errors.New("could not parse application from session context")
+ }
+ a.Application = parsed
+ }
+
+ i, ok = ctx.Get(SessionAuthorizedUser)
+ if ok {
+ parsed, ok := i.(*gtsmodel.User)
+ if !ok {
+ return nil, errors.New("could not parse user from session context")
+ }
+ a.User = parsed
+ }
+
+ i, ok = ctx.Get(SessionAuthorizedAccount)
+ if ok {
+ parsed, ok := i.(*gtsmodel.Account)
+ if !ok {
+ return nil, errors.New("could not parse account from session context")
+ }
+ a.Account = parsed
+ }
+
+ if requireToken && a.Token == nil {
+ return nil, errors.New("token not supplied")
+ }
+ if requireApp && a.Application == nil {
+ return nil, errors.New("application not supplied")
+ }
+ if requireUser && a.User == nil {
+ return nil, errors.New("user not supplied")
+ }
+ if requireAccount && a.Account == nil {
+ return nil, errors.New("account not supplied")
+ }
+ return a, nil
+}
diff --git a/internal/storage/inmem.go b/internal/storage/inmem.go
index 2d88189db..a596c3d97 100644
--- a/internal/storage/inmem.go
+++ b/internal/storage/inmem.go
@@ -35,7 +35,7 @@ func (s *inMemStorage) RetrieveFileFrom(path string) ([]byte, error) {
l := s.log.WithField("func", "RetrieveFileFrom")
l.Debugf("retrieving from path %s", path)
d, ok := s.stored[path]
- if !ok {
+ if !ok || len(d) == 0 {
return nil, fmt.Errorf("no data found at path %s", path)
}
return d, nil
diff --git a/internal/typeutils/internaltofrontend.go b/internal/typeutils/internaltofrontend.go
index 6ec2e0509..17ae099d9 100644
--- a/internal/typeutils/internaltofrontend.go
+++ b/internal/typeutils/internaltofrontend.go
@@ -22,9 +22,9 @@ import (
"fmt"
"time"
+ "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
- "github.com/superseriousbusiness/gotosocial/internal/api/model"
)
func (c *converter) AccountToMastoSensitive(a *gtsmodel.Account) (*model.Account, error) {
diff --git a/testrig/actions.go b/testrig/actions.go
index 87b3cae7d..7ed75b18f 100644
--- a/testrig/actions.go
+++ b/testrig/actions.go
@@ -50,10 +50,7 @@ var Run action.GTSAction = func(ctx context.Context, _ *config.Config, log *logr
dbService := NewTestDB()
router := NewTestRouter()
storageBackend := NewTestStorage()
- processor := NewTestProcessor(dbService, storageBackend)
- if err := processor.Start(); err != nil {
- return fmt.Errorf("error starting processor: %s", err)
- }
+
typeConverter := NewTestTypeConverter(dbService)
transportController := NewTestTransportController(NewMockHTTPClient(func(req *http.Request) (*http.Response, error) {
r := ioutil.NopCloser(bytes.NewReader([]byte{}))
@@ -62,13 +59,17 @@ var Run action.GTSAction = func(ctx context.Context, _ *config.Config, log *logr
Body: r,
}, nil
}))
- federator := federation.NewFederator(dbService, transportController, c, log, processor, typeConverter)
+ federator := federation.NewFederator(dbService, transportController, c, log, typeConverter)
+ processor := NewTestProcessor(dbService, storageBackend, federator)
+ if err := processor.Start(); err != nil {
+ return fmt.Errorf("error starting processor: %s", err)
+ }
StandardDBSetup(dbService)
StandardStorageSetup(storageBackend, "./testrig/media")
// build client api modules
- authModule := auth.New(c, processor, log)
+ authModule := auth.New(c, dbService, NewTestOauthServer(dbService), log)
accountModule := account.New(c, processor, log)
appsModule := app.New(c, processor, log)
mm := mediaModule.New(c, processor, log)
diff --git a/testrig/federator.go b/testrig/federator.go
new file mode 100644
index 000000000..63ad520db
--- /dev/null
+++ b/testrig/federator.go
@@ -0,0 +1,29 @@
+/*
+ GoToSocial
+ Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+
+package testrig
+
+import (
+ "github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/federation"
+ "github.com/superseriousbusiness/gotosocial/internal/transport"
+)
+
+func NewTestFederator(db db.DB, tc transport.Controller) federation.Federator {
+ return federation.NewFederator(db, tc, NewTestConfig(), NewTestLog(), NewTestTypeConverter(db))
+}
diff --git a/testrig/processor.go b/testrig/processor.go
index 7589108be..9aa8e2509 100644
--- a/testrig/processor.go
+++ b/testrig/processor.go
@@ -20,11 +20,12 @@ package testrig
import (
"github.com/superseriousbusiness/gotosocial/internal/db"
+ "github.com/superseriousbusiness/gotosocial/internal/federation"
"github.com/superseriousbusiness/gotosocial/internal/message"
"github.com/superseriousbusiness/gotosocial/internal/storage"
)
// NewTestProcessor returns a Processor suitable for testing purposes
-func NewTestProcessor(db db.DB, storage storage.Storage) message.Processor {
- return message.NewProcessor(NewTestConfig(), NewTestTypeConverter(db), NewTestOauthServer(db), NewTestMediaHandler(db, storage), storage, db, NewTestLog())
+func NewTestProcessor(db db.DB, storage storage.Storage, federator federation.Federator) message.Processor {
+ return message.NewProcessor(NewTestConfig(), NewTestTypeConverter(db), federator, NewTestOauthServer(db), NewTestMediaHandler(db, storage), storage, db, NewTestLog())
}
diff --git a/testrig/testmodels.go b/testrig/testmodels.go
index 1ce1b6a90..6427630c7 100644
--- a/testrig/testmodels.go
+++ b/testrig/testmodels.go
@@ -691,25 +691,26 @@ func NewTestAttachments() map[string]*gtsmodel.MediaAttachment {
func NewTestEmojis() map[string]*gtsmodel.Emoji {
return map[string]*gtsmodel.Emoji{
"rainbow": {
- ID: "a96ec4f3-1cae-47e4-a508-f9d66a6b221b",
- Shortcode: "rainbow",
- Domain: "",
- CreatedAt: time.Now(),
- UpdatedAt: time.Now(),
- ImageRemoteURL: "",
- ImageStaticRemoteURL: "",
- ImageURL: "http://localhost:8080/fileserver/39b745a3-774d-4b65-8bb2-b63d9e20a343/emoji/original/a96ec4f3-1cae-47e4-a508-f9d66a6b221b.png",
- ImagePath: "/tmp/gotosocial/39b745a3-774d-4b65-8bb2-b63d9e20a343/emoji/original/a96ec4f3-1cae-47e4-a508-f9d66a6b221b.png",
- ImageStaticURL: "http://localhost:8080/fileserver/39b745a3-774d-4b65-8bb2-b63d9e20a343/emoji/static/a96ec4f3-1cae-47e4-a508-f9d66a6b221b.png",
- ImageStaticPath: "/tmp/gotosocial/39b745a3-774d-4b65-8bb2-b63d9e20a343/emoji/static/a96ec4f3-1cae-47e4-a508-f9d66a6b221b.png",
- ImageContentType: "image/png",
- ImageFileSize: 36702,
- ImageStaticFileSize: 10413,
- ImageUpdatedAt: time.Now(),
- Disabled: false,
- URI: "http://localhost:8080/emoji/a96ec4f3-1cae-47e4-a508-f9d66a6b221b",
- VisibleInPicker: true,
- CategoryID: "",
+ ID: "a96ec4f3-1cae-47e4-a508-f9d66a6b221b",
+ Shortcode: "rainbow",
+ Domain: "",
+ CreatedAt: time.Now(),
+ UpdatedAt: time.Now(),
+ ImageRemoteURL: "",
+ ImageStaticRemoteURL: "",
+ ImageURL: "http://localhost:8080/fileserver/39b745a3-774d-4b65-8bb2-b63d9e20a343/emoji/original/a96ec4f3-1cae-47e4-a508-f9d66a6b221b.png",
+ ImagePath: "/tmp/gotosocial/39b745a3-774d-4b65-8bb2-b63d9e20a343/emoji/original/a96ec4f3-1cae-47e4-a508-f9d66a6b221b.png",
+ ImageStaticURL: "http://localhost:8080/fileserver/39b745a3-774d-4b65-8bb2-b63d9e20a343/emoji/static/a96ec4f3-1cae-47e4-a508-f9d66a6b221b.png",
+ ImageStaticPath: "/tmp/gotosocial/39b745a3-774d-4b65-8bb2-b63d9e20a343/emoji/static/a96ec4f3-1cae-47e4-a508-f9d66a6b221b.png",
+ ImageContentType: "image/png",
+ ImageStaticContentType: "image/png",
+ ImageFileSize: 36702,
+ ImageStaticFileSize: 10413,
+ ImageUpdatedAt: time.Now(),
+ Disabled: false,
+ URI: "http://localhost:8080/emoji/a96ec4f3-1cae-47e4-a508-f9d66a6b221b",
+ VisibleInPicker: true,
+ CategoryID: "",
},
}
}
diff --git a/testrig/transportcontroller.go b/testrig/transportcontroller.go
index 942be7f3a..f2b5b93f7 100644
--- a/testrig/transportcontroller.go
+++ b/testrig/transportcontroller.go
@@ -19,6 +19,8 @@
package testrig
import (
+ "bytes"
+ "io/ioutil"
"net/http"
"github.com/go-fed/activity/pub"
@@ -41,7 +43,22 @@ func NewTestTransportController(client pub.HttpClient) transport.Controller {
// NewMockHTTPClient returns a client that conforms to the pub.HttpClient interface,
// but will always just execute the given `do` function, allowing responses to be mocked.
+//
+// If 'do' is nil, then a no-op function will be used instead, that just returns status 200.
+//
+// Note that you should never ever make ACTUAL http calls with this thing.
func NewMockHTTPClient(do func(req *http.Request) (*http.Response, error)) pub.HttpClient {
+ if do == nil {
+ return &mockHTTPClient{
+ do: func(req *http.Request) (*http.Response, error) {
+ r := ioutil.NopCloser(bytes.NewReader([]byte{}))
+ return &http.Response{
+ StatusCode: 200,
+ Body: r,
+ }, nil
+ },
+ }
+ }
return &mockHTTPClient{
do: do,
}