summaryrefslogtreecommitdiff
path: root/internal/api/s2s/user
diff options
context:
space:
mode:
Diffstat (limited to 'internal/api/s2s/user')
-rw-r--r--internal/api/s2s/user/repliesget.go186
-rw-r--r--internal/api/s2s/user/repliesget_test.go241
-rw-r--r--internal/api/s2s/user/user.go10
-rw-r--r--internal/api/s2s/user/user_test.go16
-rw-r--r--internal/api/s2s/user/userget_test.go66
5 files changed, 459 insertions, 60 deletions
diff --git a/internal/api/s2s/user/repliesget.go b/internal/api/s2s/user/repliesget.go
new file mode 100644
index 000000000..951cc428c
--- /dev/null
+++ b/internal/api/s2s/user/repliesget.go
@@ -0,0 +1,186 @@
+package user
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strconv"
+
+ "github.com/gin-gonic/gin"
+ "github.com/sirupsen/logrus"
+ "github.com/superseriousbusiness/gotosocial/internal/util"
+)
+
+// StatusRepliesGETHandler swagger:operation GET /users/{username}/statuses/{status}/replies s2sRepliesGet
+//
+// Get the replies collection for a status.
+//
+// Note that the response will be a Collection with a page as `first`, as shown below, if `page` is `false`.
+//
+// If `page` is `true`, then the response will be a single `CollectionPage` without the wrapping `Collection`.
+//
+// HTTP signature is required on the request.
+//
+// ---
+// tags:
+// - s2s/federation
+//
+// produces:
+// - application/activity+json
+//
+// parameters:
+// - name: username
+// type: string
+// description: Username of the account.
+// in: path
+// required: true
+// - name: status
+// type: string
+// description: ID of the status.
+// in: path
+// required: true
+// - name: page
+// type: boolean
+// description: Return response as a CollectionPage.
+// in: query
+// default: false
+// - name: only_other_accounts
+// type: boolean
+// description: Return replies only from accounts other than the status owner.
+// in: query
+// default: false
+// - name: min_id
+// type: string
+// description: Minimum ID of the next status, used for paging.
+// in: query
+//
+// responses:
+// '200':
+// in: body
+// schema:
+// "$ref": "#/definitions/swaggerStatusRepliesCollection"
+// '400':
+// description: bad request
+// '401':
+// description: unauthorized
+// '403':
+// description: forbidden
+// '404':
+// description: not found
+func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
+ l := m.log.WithFields(logrus.Fields{
+ "func": "StatusRepliesGETHandler",
+ "url": c.Request.RequestURI,
+ })
+
+ requestedUsername := c.Param(UsernameKey)
+ if requestedUsername == "" {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "no username specified in request"})
+ return
+ }
+
+ requestedStatusID := c.Param(StatusIDKey)
+ if requestedStatusID == "" {
+ c.JSON(http.StatusBadRequest, gin.H{"error": "no status id specified in request"})
+ return
+ }
+
+ page := false
+ pageString := c.Query(PageKey)
+ if pageString != "" {
+ i, err := strconv.ParseBool(pageString)
+ if err != nil {
+ l.Debugf("error parsing page string: %s", err)
+ c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't parse page query param"})
+ return
+ }
+ page = i
+ }
+
+ onlyOtherAccounts := false
+ onlyOtherAccountsString := c.Query(OnlyOtherAccountsKey)
+ if onlyOtherAccountsString != "" {
+ i, err := strconv.ParseBool(onlyOtherAccountsString)
+ if err != nil {
+ l.Debugf("error parsing only_other_accounts string: %s", err)
+ c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't parse only_other_accounts query param"})
+ return
+ }
+ onlyOtherAccounts = i
+ }
+
+ minID := ""
+ minIDString := c.Query(MinIDKey)
+ if minIDString != "" {
+ minID = minIDString
+ }
+
+ // 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)
+
+ // transfer the signature verifier from the gin context to the request context
+ ctx := c.Request.Context()
+ verifier, signed := c.Get(string(util.APRequestingPublicKeyVerifier))
+ if signed {
+ ctx = context.WithValue(ctx, util.APRequestingPublicKeyVerifier, verifier)
+ }
+
+ replies, err := m.processor.GetFediStatusReplies(ctx, requestedUsername, requestedStatusID, page, onlyOtherAccounts, minID, c.Request.URL)
+ if err != nil {
+ l.Info(err.Error())
+ c.JSON(err.Code(), gin.H{"error": err.Safe()})
+ return
+ }
+
+ b, mErr := json.Marshal(replies)
+ if mErr != nil {
+ err := fmt.Errorf("could not marshal json: %s", mErr)
+ l.Error(err)
+ c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
+ return
+ }
+
+ c.Data(http.StatusOK, format, b)
+}
+
+// SwaggerStatusRepliesCollection represents a response to GET /users/{username}/statuses/{status}/replies.
+// swagger:model swaggerStatusRepliesCollection
+type SwaggerStatusRepliesCollection struct {
+ // ActivityStreams context.
+ // example: https://www.w3.org/ns/activitystreams
+ Context string `json:"@context"`
+ // ActivityStreams ID.
+ // example: https://example.org/users/some_user/statuses/106717595988259568/replies
+ ID string `json:"id"`
+ // ActivityStreams type.
+ // example: Collection
+ Type string `json:"type"`
+ // ActivityStreams first property.
+ First SwaggerStatusRepliesCollectionPage `json:"first"`
+}
+
+// SwaggerStatusRepliesCollectionPage represents one page of a collection.
+// swagger:model swaggerStatusRepliesCollectionPage
+type SwaggerStatusRepliesCollectionPage struct {
+ // ActivityStreams ID.
+ // example: https://example.org/users/some_user/statuses/106717595988259568/replies?page=true
+ ID string `json:"id"`
+ // ActivityStreams type.
+ // example: CollectionPage
+ Type string `json:"type"`
+ // Link to the next page.
+ // example: https://example.org/users/some_user/statuses/106717595988259568/replies?only_other_accounts=true&page=true
+ Next string `json:"next"`
+ // Collection this page belongs to.
+ // example: https://example.org/users/some_user/statuses/106717595988259568/replies
+ PartOf string `json:"partOf"`
+ // Items on this page.
+ // example: ["https://example.org/users/some_other_user/statuses/086417595981111564", "https://another.example.com/users/another_user/statuses/01FCN8XDV3YG7B4R42QA6YQZ9R"]
+ Items []string `json:"items"`
+}
diff --git a/internal/api/s2s/user/repliesget_test.go b/internal/api/s2s/user/repliesget_test.go
new file mode 100644
index 000000000..75edbc882
--- /dev/null
+++ b/internal/api/s2s/user/repliesget_test.go
@@ -0,0 +1,241 @@
+package user_test
+
+import (
+ "context"
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/gin-gonic/gin"
+ "github.com/go-fed/activity/streams"
+ "github.com/go-fed/activity/streams/vocab"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/suite"
+ "github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
+ "github.com/superseriousbusiness/gotosocial/internal/api/security"
+ "github.com/superseriousbusiness/gotosocial/testrig"
+)
+
+type RepliesGetTestSuite struct {
+ UserStandardTestSuite
+}
+
+func (suite *RepliesGetTestSuite) SetupSuite() {
+ suite.testTokens = testrig.NewTestTokens()
+ suite.testClients = testrig.NewTestClients()
+ suite.testApplications = testrig.NewTestApplications()
+ suite.testUsers = testrig.NewTestUsers()
+ suite.testAccounts = testrig.NewTestAccounts()
+ suite.testAttachments = testrig.NewTestAttachments()
+ suite.testStatuses = testrig.NewTestStatuses()
+}
+
+func (suite *RepliesGetTestSuite) SetupTest() {
+ suite.config = testrig.NewTestConfig()
+ suite.db = testrig.NewTestDB()
+ suite.tc = testrig.NewTestTypeConverter(suite.db)
+ suite.storage = testrig.NewTestStorage()
+ suite.log = testrig.NewTestLog()
+ suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db), suite.storage)
+ suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator)
+ suite.userModule = user.New(suite.config, suite.processor, suite.log).(*user.Module)
+ suite.securityModule = security.New(suite.config, suite.db, suite.log).(*security.Module)
+ testrig.StandardDBSetup(suite.db, suite.testAccounts)
+ testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
+}
+
+func (suite *RepliesGetTestSuite) TearDownTest() {
+ testrig.StandardDBTeardown(suite.db)
+ testrig.StandardStorageTeardown(suite.storage)
+}
+
+func (suite *RepliesGetTestSuite) TestGetReplies() {
+ // the dereference we're gonna use
+ derefRequests := testrig.NewTestDereferenceRequests(suite.testAccounts)
+ signedRequest := derefRequests["foss_satan_dereference_local_account_1_status_1_replies"]
+ targetAccount := suite.testAccounts["local_account_1"]
+ targetStatus := suite.testStatuses["local_account_1_status_1"]
+
+ tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db)
+ federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
+ processor := testrig.NewTestProcessor(suite.db, suite.storage, federator)
+ userModule := user.New(suite.config, processor, suite.log).(*user.Module)
+
+ // setup request
+ recorder := httptest.NewRecorder()
+ ctx, _ := gin.CreateTestContext(recorder)
+ ctx.Request = httptest.NewRequest(http.MethodGet, targetStatus.URI+"/replies", nil) // the endpoint we're hitting
+ ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
+ ctx.Request.Header.Set("Date", signedRequest.DateHeader)
+
+ // we need to pass the context through signature check first to set appropriate values on it
+ suite.securityModule.SignatureCheck(ctx)
+
+ // normally the router would populate these params from the path values,
+ // but because we're calling the function directly, we need to set them manually.
+ ctx.Params = gin.Params{
+ gin.Param{
+ Key: user.UsernameKey,
+ Value: targetAccount.Username,
+ },
+ gin.Param{
+ Key: user.StatusIDKey,
+ Value: targetStatus.ID,
+ },
+ }
+
+ // trigger the function being tested
+ userModule.StatusRepliesGETHandler(ctx)
+
+ // check response
+ suite.EqualValues(http.StatusOK, recorder.Code)
+
+ result := recorder.Result()
+ defer result.Body.Close()
+ b, err := ioutil.ReadAll(result.Body)
+ assert.NoError(suite.T(), err)
+ assert.Equal(suite.T(), `{"@context":"https://www.w3.org/ns/activitystreams","first":{"id":"http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?page=true","next":"http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?only_other_accounts=false\u0026page=true","partOf":"http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies","type":"CollectionPage"},"id":"http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies","type":"Collection"}`, string(b))
+
+ // should be a Collection
+ m := make(map[string]interface{})
+ err = json.Unmarshal(b, &m)
+ assert.NoError(suite.T(), err)
+
+ t, err := streams.ToType(context.Background(), m)
+ assert.NoError(suite.T(), err)
+
+ _, ok := t.(vocab.ActivityStreamsCollection)
+ assert.True(suite.T(), ok)
+}
+
+func (suite *RepliesGetTestSuite) TestGetRepliesNext() {
+ // the dereference we're gonna use
+ derefRequests := testrig.NewTestDereferenceRequests(suite.testAccounts)
+ signedRequest := derefRequests["foss_satan_dereference_local_account_1_status_1_replies_next"]
+ targetAccount := suite.testAccounts["local_account_1"]
+ targetStatus := suite.testStatuses["local_account_1_status_1"]
+
+ tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db)
+ federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
+ processor := testrig.NewTestProcessor(suite.db, suite.storage, federator)
+ userModule := user.New(suite.config, processor, suite.log).(*user.Module)
+
+ // setup request
+ recorder := httptest.NewRecorder()
+ ctx, _ := gin.CreateTestContext(recorder)
+ ctx.Request = httptest.NewRequest(http.MethodGet, targetStatus.URI+"/replies?only_other_accounts=false&page=true", nil) // the endpoint we're hitting
+ ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
+ ctx.Request.Header.Set("Date", signedRequest.DateHeader)
+
+ // we need to pass the context through signature check first to set appropriate values on it
+ suite.securityModule.SignatureCheck(ctx)
+
+ // normally the router would populate these params from the path values,
+ // but because we're calling the function directly, we need to set them manually.
+ ctx.Params = gin.Params{
+ gin.Param{
+ Key: user.UsernameKey,
+ Value: targetAccount.Username,
+ },
+ gin.Param{
+ Key: user.StatusIDKey,
+ Value: targetStatus.ID,
+ },
+ }
+
+ // trigger the function being tested
+ userModule.StatusRepliesGETHandler(ctx)
+
+ // check response
+ suite.EqualValues(http.StatusOK, recorder.Code)
+
+ result := recorder.Result()
+ defer result.Body.Close()
+ b, err := ioutil.ReadAll(result.Body)
+ assert.NoError(suite.T(), err)
+
+ assert.Equal(suite.T(), `{"@context":"https://www.w3.org/ns/activitystreams","id":"http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?page=true\u0026only_other_accounts=false","items":"http://localhost:8080/users/1happyturtle/statuses/01FCQSQ667XHJ9AV9T27SJJSX5","next":"http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?only_other_accounts=false\u0026page=true\u0026min_id=01FCQSQ667XHJ9AV9T27SJJSX5","partOf":"http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies","type":"CollectionPage"}`, string(b))
+
+ // should be a Collection
+ m := make(map[string]interface{})
+ err = json.Unmarshal(b, &m)
+ assert.NoError(suite.T(), err)
+
+ t, err := streams.ToType(context.Background(), m)
+ assert.NoError(suite.T(), err)
+
+ page, ok := t.(vocab.ActivityStreamsCollectionPage)
+ assert.True(suite.T(), ok)
+
+ assert.Equal(suite.T(), page.GetActivityStreamsItems().Len(), 1)
+}
+
+func (suite *RepliesGetTestSuite) TestGetRepliesLast() {
+ // the dereference we're gonna use
+ derefRequests := testrig.NewTestDereferenceRequests(suite.testAccounts)
+ signedRequest := derefRequests["foss_satan_dereference_local_account_1_status_1_replies_last"]
+ targetAccount := suite.testAccounts["local_account_1"]
+ targetStatus := suite.testStatuses["local_account_1_status_1"]
+
+ tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db)
+ federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
+ processor := testrig.NewTestProcessor(suite.db, suite.storage, federator)
+ userModule := user.New(suite.config, processor, suite.log).(*user.Module)
+
+ // setup request
+ recorder := httptest.NewRecorder()
+ ctx, _ := gin.CreateTestContext(recorder)
+ ctx.Request = httptest.NewRequest(http.MethodGet, targetStatus.URI+"/replies?only_other_accounts=false&page=true&min_id=01FCQSQ667XHJ9AV9T27SJJSX5", nil) // the endpoint we're hitting
+ ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
+ ctx.Request.Header.Set("Date", signedRequest.DateHeader)
+
+ // we need to pass the context through signature check first to set appropriate values on it
+ suite.securityModule.SignatureCheck(ctx)
+
+ // normally the router would populate these params from the path values,
+ // but because we're calling the function directly, we need to set them manually.
+ ctx.Params = gin.Params{
+ gin.Param{
+ Key: user.UsernameKey,
+ Value: targetAccount.Username,
+ },
+ gin.Param{
+ Key: user.StatusIDKey,
+ Value: targetStatus.ID,
+ },
+ }
+
+ // trigger the function being tested
+ userModule.StatusRepliesGETHandler(ctx)
+
+ // check response
+ suite.EqualValues(http.StatusOK, recorder.Code)
+
+ result := recorder.Result()
+ defer result.Body.Close()
+ b, err := ioutil.ReadAll(result.Body)
+ assert.NoError(suite.T(), err)
+
+ fmt.Println(string(b))
+ assert.Equal(suite.T(), `{"@context":"https://www.w3.org/ns/activitystreams","id":"http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?page=true\u0026only_other_accounts=false\u0026min_id=01FCQSQ667XHJ9AV9T27SJJSX5","items":[],"next":"http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies?only_other_accounts=false\u0026page=true","partOf":"http://localhost:8080/users/the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY/replies","type":"CollectionPage"}`, string(b))
+
+ // should be a Collection
+ m := make(map[string]interface{})
+ err = json.Unmarshal(b, &m)
+ assert.NoError(suite.T(), err)
+
+ t, err := streams.ToType(context.Background(), m)
+ assert.NoError(suite.T(), err)
+
+ page, ok := t.(vocab.ActivityStreamsCollectionPage)
+ assert.True(suite.T(), ok)
+
+ assert.Equal(suite.T(), page.GetActivityStreamsItems().Len(), 0)
+}
+
+func TestRepliesGetTestSuite(t *testing.T) {
+ suite.Run(t, new(RepliesGetTestSuite))
+}
diff --git a/internal/api/s2s/user/user.go b/internal/api/s2s/user/user.go
index 0cb8e1e90..b5ff9a699 100644
--- a/internal/api/s2s/user/user.go
+++ b/internal/api/s2s/user/user.go
@@ -34,6 +34,13 @@ const (
UsernameKey = "username"
// StatusIDKey is for status IDs
StatusIDKey = "status"
+ // OnlyOtherAccountsKey is for filtering status responses.
+ OnlyOtherAccountsKey = "only_other_accounts"
+ // MinIDKey is for filtering status responses.
+ MinIDKey = "min_id"
+ // PageKey is for filtering status responses.
+ PageKey = "page"
+
// UsersBasePath is the base path for serving information about Users eg https://example.org/users
UsersBasePath = "/" + util.UsersPath
// UsersBasePathWithUsername is just the users base path with the Username key in it.
@@ -50,6 +57,8 @@ const (
UsersFollowingPath = UsersBasePathWithUsername + "/" + util.FollowingPath
// UsersStatusPath is for serving GET requests to a particular status by a user, with the given username key and status ID
UsersStatusPath = UsersBasePathWithUsername + "/" + util.StatusesPath + "/:" + StatusIDKey
+ // UsersStatusRepliesPath is for serving the replies collection of a status.
+ UsersStatusRepliesPath = UsersStatusPath + "/replies"
)
// ActivityPubAcceptHeaders represents the Accept headers mentioned here:
@@ -83,5 +92,6 @@ func (m *Module) Route(s router.Router) error {
s.AttachHandler(http.MethodGet, UsersFollowingPath, m.FollowingGETHandler)
s.AttachHandler(http.MethodGet, UsersStatusPath, m.StatusGETHandler)
s.AttachHandler(http.MethodGet, UsersPublicKeyPath, m.PublicKeyGETHandler)
+ s.AttachHandler(http.MethodGet, UsersStatusRepliesPath, m.StatusRepliesGETHandler)
return nil
}
diff --git a/internal/api/s2s/user/user_test.go b/internal/api/s2s/user/user_test.go
index 91d1ea32d..71d4395eb 100644
--- a/internal/api/s2s/user/user_test.go
+++ b/internal/api/s2s/user/user_test.go
@@ -4,6 +4,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
+ "github.com/superseriousbusiness/gotosocial/internal/api/security"
"github.com/superseriousbusiness/gotosocial/internal/blob"
"github.com/superseriousbusiness/gotosocial/internal/config"
"github.com/superseriousbusiness/gotosocial/internal/db"
@@ -18,13 +19,14 @@ import (
type UserStandardTestSuite struct {
// standard suite interfaces
suite.Suite
- config *config.Config
- db db.DB
- log *logrus.Logger
- tc typeutils.TypeConverter
- federator federation.Federator
- processor processing.Processor
- storage blob.Storage
+ config *config.Config
+ db db.DB
+ log *logrus.Logger
+ tc typeutils.TypeConverter
+ federator federation.Federator
+ processor processing.Processor
+ storage blob.Storage
+ securityModule *security.Module
// standard suite models
testTokens map[string]*oauth.Token
diff --git a/internal/api/s2s/user/userget_test.go b/internal/api/s2s/user/userget_test.go
index d20148802..ab0015c57 100644
--- a/internal/api/s2s/user/userget_test.go
+++ b/internal/api/s2s/user/userget_test.go
@@ -1,16 +1,11 @@
package user_test
import (
- "bytes"
"context"
- "crypto/x509"
"encoding/json"
- "encoding/pem"
- "fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
- "strings"
"testing"
"github.com/gin-gonic/gin"
@@ -19,6 +14,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
+ "github.com/superseriousbusiness/gotosocial/internal/api/security"
"github.com/superseriousbusiness/gotosocial/testrig"
)
@@ -42,10 +38,11 @@ func (suite *UserGetTestSuite) SetupTest() {
suite.tc = testrig.NewTestTypeConverter(suite.db)
suite.storage = testrig.NewTestStorage()
suite.log = testrig.NewTestLog()
- suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil)), suite.storage)
+ suite.federator = testrig.NewTestFederator(suite.db, testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db), suite.storage)
suite.processor = testrig.NewTestProcessor(suite.db, suite.storage, suite.federator)
suite.userModule = user.New(suite.config, suite.processor, suite.log).(*user.Module)
- testrig.StandardDBSetup(suite.db)
+ suite.securityModule = security.New(suite.config, suite.db, suite.log).(*security.Module)
+ testrig.StandardDBSetup(suite.db, suite.testAccounts)
testrig.StandardStorageSetup(suite.storage, "../../../../testrig/media")
}
@@ -56,48 +53,11 @@ func (suite *UserGetTestSuite) TearDownTest() {
func (suite *UserGetTestSuite) TestGetUser() {
// the dereference we're gonna use
- signedRequest := testrig.NewTestDereferenceRequests(suite.testAccounts)["foss_satan_dereference_zork"]
-
- requestingAccount := suite.testAccounts["remote_account_1"]
+ derefRequests := testrig.NewTestDereferenceRequests(suite.testAccounts)
+ signedRequest := derefRequests["foss_satan_dereference_zork"]
targetAccount := suite.testAccounts["local_account_1"]
- encodedPublicKey, err := x509.MarshalPKIXPublicKey(requestingAccount.PublicKey)
- assert.NoError(suite.T(), err)
- publicKeyBytes := pem.EncodeToMemory(&pem.Block{
- Type: "PUBLIC KEY",
- Bytes: encodedPublicKey,
- })
- publicKeyString := strings.ReplaceAll(string(publicKeyBytes), "\n", "\\n")
-
- // for this test we need the client to return the public key of the requester on the 'remote' instance
- responseBodyString := fmt.Sprintf(`
- {
- "@context": [
- "https://www.w3.org/ns/activitystreams",
- "https://w3id.org/security/v1"
- ],
-
- "id": "%s",
- "type": "Person",
- "preferredUsername": "%s",
- "inbox": "%s",
-
- "publicKey": {
- "id": "%s",
- "owner": "%s",
- "publicKeyPem": "%s"
- }
- }`, requestingAccount.URI, requestingAccount.Username, requestingAccount.InboxURI, requestingAccount.PublicKeyURI, requestingAccount.URI, publicKeyString)
-
- // create a transport controller whose client will just return the response body string we specified above
- tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(func(req *http.Request) (*http.Response, error) {
- r := ioutil.NopCloser(bytes.NewReader([]byte(responseBodyString)))
- return &http.Response{
- StatusCode: 200,
- Body: r,
- }, nil
- }))
- // get this transport controller embedded right in the user module we're testing
+ tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db)
federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator)
userModule := user.New(suite.config, processor, suite.log).(*user.Module)
@@ -105,7 +65,12 @@ func (suite *UserGetTestSuite) TestGetUser() {
// setup request
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
- ctx.Request = httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:8080%s", strings.Replace(user.UsersBasePathWithUsername, ":username", targetAccount.Username, 1)), nil) // the endpoint we're hitting
+ ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.URI, nil) // the endpoint we're hitting
+ ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
+ ctx.Request.Header.Set("Date", signedRequest.DateHeader)
+
+ // we need to pass the context through signature check first to set appropriate values on it
+ suite.securityModule.SignatureCheck(ctx)
// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
@@ -116,11 +81,6 @@ func (suite *UserGetTestSuite) TestGetUser() {
},
}
- // we need these headers for the request to be validated
- ctx.Request.Header.Set("Signature", signedRequest.SignatureHeader)
- ctx.Request.Header.Set("Date", signedRequest.DateHeader)
- ctx.Request.Header.Set("Digest", signedRequest.DigestHeader)
-
// trigger the function being tested
userModule.UsersGETHandler(ctx)