Skip to content

Commit

Permalink
Unblock fix (#247)
Browse files Browse the repository at this point in the history
* start tests for inbox posts

* go mod tidy

* rename transferContext

* test block/unblock

* improve logging

* improve logging

* fix comment typo
  • Loading branch information
tsmethurst authored Sep 24, 2021
1 parent d515c9f commit c7cfbe2
Show file tree
Hide file tree
Showing 16 changed files with 362 additions and 29 deletions.
8 changes: 1 addition & 7 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -469,17 +469,11 @@ github.com/ugorji/go v1.2.6/go.mod h1:anCg0y61KIhDlPZmnH+so+RQbysYVyDko0IMgJv0Nn
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.2.6 h1:7kbGefxLoDBuYXOms4yD7223OpNMMPNPZxXk5TvFcyQ=
github.com/ugorji/go/codec v1.2.6/go.mod h1:V6TCNZ4PHqoHGFZuSG1W8nrCzzdgA2DozYxWFFpvxTw=
github.com/uptrace/bun v1.0.5/go.mod h1:aL6D9vPw8DXaTQTwGrEPtUderBYXx7ShUmPfnxnqscw=
github.com/uptrace/bun v1.0.6 h1:o9eMq5ePGBXtxbK3SIreOCRr+rIBQzvJH+/s98kYcVM=
github.com/uptrace/bun v1.0.6/go.mod h1:aL6D9vPw8DXaTQTwGrEPtUderBYXx7ShUmPfnxnqscw=
github.com/uptrace/bun v1.0.8/go.mod h1:aL6D9vPw8DXaTQTwGrEPtUderBYXx7ShUmPfnxnqscw=
github.com/uptrace/bun v1.0.9-0.20210922104131-34c982b23581 h1:W8SAI7irrKSZ3t9MzFwehUyEd6f8ajOprqSzCxHFcxo=
github.com/uptrace/bun v1.0.9-0.20210922104131-34c982b23581/go.mod h1:aL6D9vPw8DXaTQTwGrEPtUderBYXx7ShUmPfnxnqscw=
github.com/uptrace/bun/dialect/pgdialect v1.0.5 h1:mq694/aMvs7GwuTar9NIlCLQt/2u4xsF0QMP4I24yHA=
github.com/uptrace/bun/dialect/pgdialect v1.0.5/go.mod h1:MKWjO0PC20ris2oJ3dd6mI/762x24Cjwh8XmbqUhM8A=
github.com/uptrace/bun/dialect/pgdialect v1.0.9-0.20210922104131-34c982b23581 h1:3r8Td1Y3I51f9LYcC/3EuQT8zKuCh+OWiQQ8FVHK4Pg=
github.com/uptrace/bun/dialect/pgdialect v1.0.9-0.20210922104131-34c982b23581/go.mod h1:HEGRgyS68SiHcKhFa9LXcDN+KEWo1I4VplvunL0Oi4o=
github.com/uptrace/bun/dialect/sqlitedialect v1.0.5 h1:6cIj31YVJr4vvA15C2v76soXL+7WtjFdV6WraApc3r0=
github.com/uptrace/bun/dialect/sqlitedialect v1.0.5/go.mod h1:NW2Gctc9ooQXGSD4kYSac2aiF49lo8YJ3ZAr93lH2p8=
github.com/uptrace/bun/dialect/sqlitedialect v1.0.9-0.20210922104131-34c982b23581 h1:Yfbbo8EQffFLL7EEBq2yUirSg3b7NID4sgRGdNlIJa0=
github.com/uptrace/bun/dialect/sqlitedialect v1.0.9-0.20210922104131-34c982b23581/go.mod h1:v1rNdAcJdw8AgD4x4OAJFIRFA9+sANoXK7u21H9Wvkg=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
Expand Down
4 changes: 2 additions & 2 deletions internal/api/s2s/user/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ var ActivityPubAcceptHeaders = []string{
`application/ld+json; profile="https://www.w3.org/ns/activitystreams"`,
}

// populateContext transfers the signature verifier and signature from the gin context to the request context
func populateContext(c *gin.Context) context.Context {
// transferContext transfers the signature verifier and signature from the gin context to the request context
func transferContext(c *gin.Context) context.Context {
ctx := c.Request.Context()

verifier, signed := c.Get(string(util.APRequestingPublicKeyVerifier))
Expand Down
2 changes: 1 addition & 1 deletion internal/api/s2s/user/followers.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (m *Module) FollowersGETHandler(c *gin.Context) {
}
l.Tracef("negotiated format: %s", format)

ctx := populateContext(c)
ctx := transferContext(c)

followers, errWithCode := m.processor.GetFediFollowers(ctx, requestedUsername, c.Request.URL)
if errWithCode != nil {
Expand Down
2 changes: 1 addition & 1 deletion internal/api/s2s/user/following.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (m *Module) FollowingGETHandler(c *gin.Context) {
}
l.Tracef("negotiated format: %s", format)

ctx := populateContext(c)
ctx := transferContext(c)

following, errWithCode := m.processor.GetFediFollowing(ctx, requestedUsername, c.Request.URL)
if errWithCode != nil {
Expand Down
6 changes: 3 additions & 3 deletions internal/api/s2s/user/inboxpost.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,12 @@ func (m *Module) InboxPOSTHandler(c *gin.Context) {
return
}

ctx := populateContext(c)
ctx := transferContext(c)

posted, err := m.processor.InboxPost(ctx, c.Writer, c.Request)
if err != nil {
if withCode, ok := err.(gtserror.WithCode); ok {
l.Debug(withCode.Error())
l.Debugf("InboxPOSTHandler: %s", withCode.Error())
c.JSON(withCode.Code(), withCode.Safe())
return
}
Expand All @@ -55,7 +55,7 @@ func (m *Module) InboxPOSTHandler(c *gin.Context) {
}

if !posted {
l.Debugf("request could not be handled as an AP request; headers were: %+v", c.Request.Header)
l.Debugf("InboxPOSTHandler: request could not be handled as an AP request; headers were: %+v", c.Request.Header)
c.JSON(http.StatusBadRequest, gin.H{"error": "unable to process request"})
}
}
228 changes: 228 additions & 0 deletions internal/api/s2s/user/inboxpost_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,228 @@
/*
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 <http://www.gnu.org/licenses/>.
*/

package user_test

import (
"bytes"
"context"
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
"time"

"github.com/gin-gonic/gin"
"github.com/go-fed/activity/streams"
"github.com/stretchr/testify/suite"
"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user"
"github.com/superseriousbusiness/gotosocial/internal/db"
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
"github.com/superseriousbusiness/gotosocial/testrig"
)

type InboxPostTestSuite struct {
UserStandardTestSuite
}

func (suite *InboxPostTestSuite) TestPostBlock() {
blockingAccount := suite.testAccounts["remote_account_1"]
blockedAccount := suite.testAccounts["local_account_1"]
blockURI := testrig.URLMustParse("http://fossbros-anonymous.io/users/foss_satan/blocks/01FG9C441MCTW3R2W117V2PQK3")

block := streams.NewActivityStreamsBlock()

// set the actor property to the block-ing account's URI
actorProp := streams.NewActivityStreamsActorProperty()
actorIRI := testrig.URLMustParse(blockingAccount.URI)
actorProp.AppendIRI(actorIRI)
block.SetActivityStreamsActor(actorProp)

// set the ID property to the blocks's URI
idProp := streams.NewJSONLDIdProperty()
idProp.Set(blockURI)
block.SetJSONLDId(idProp)

// set the object property to the target account's URI
objectProp := streams.NewActivityStreamsObjectProperty()
targetIRI := testrig.URLMustParse(blockedAccount.URI)
objectProp.AppendIRI(targetIRI)
block.SetActivityStreamsObject(objectProp)

// set the TO property to the target account's IRI
toProp := streams.NewActivityStreamsToProperty()
toIRI := testrig.URLMustParse(blockedAccount.URI)
toProp.AppendIRI(toIRI)
block.SetActivityStreamsTo(toProp)

targetURI := testrig.URLMustParse(blockedAccount.InboxURI)

signature, digestHeader, dateHeader := testrig.GetSignatureForActivity(block, blockingAccount.PublicKeyURI, blockingAccount.PrivateKey, targetURI)
bodyI, err := streams.Serialize(block)
suite.NoError(err)

bodyJson, err := json.Marshal(bodyI)
suite.NoError(err)
body := bytes.NewReader(bodyJson)

tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db)
federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator)
userModule := user.New(suite.config, processor, suite.log).(*user.Module)

// setup request
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodPost, targetURI.String(), body) // the endpoint we're hitting
ctx.Request.Header.Set("Signature", signature)
ctx.Request.Header.Set("Date", dateHeader)
ctx.Request.Header.Set("Digest", digestHeader)
ctx.Request.Header.Set("Content-Type", "application/activity+json")

// we need to pass the context through signature check first to set appropriate values on it
suite.securityModule.SignatureCheck(ctx)

// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
Key: user.UsernameKey,
Value: blockedAccount.Username,
},
}

// trigger the function being tested
userModule.InboxPOSTHandler(ctx)

result := recorder.Result()
defer result.Body.Close()
b, err := ioutil.ReadAll(result.Body)
suite.NoError(err)
suite.Empty(b)

// there should be a block in the database now between the accounts
dbBlock, err := suite.db.GetBlock(context.Background(), blockingAccount.ID, blockedAccount.ID)
suite.NoError(err)
suite.NotNil(dbBlock)
suite.WithinDuration(time.Now(), dbBlock.CreatedAt, 30*time.Second)
suite.WithinDuration(time.Now(), dbBlock.UpdatedAt, 30*time.Second)
suite.Equal("http://fossbros-anonymous.io/users/foss_satan/blocks/01FG9C441MCTW3R2W117V2PQK3", dbBlock.URI)
}

// TestPostUnblock verifies that a remote account with a block targeting one of our instance users should be able to undo that block.
func (suite *InboxPostTestSuite) TestPostUnblock() {
blockingAccount := suite.testAccounts["remote_account_1"]
blockedAccount := suite.testAccounts["local_account_1"]

// first put a block in the database so we have something to undo
blockURI := "http://fossbros-anonymous.io/users/foss_satan/blocks/01FG9C441MCTW3R2W117V2PQK3"
dbBlockID, err := id.NewRandomULID()
suite.NoError(err)

dbBlock := &gtsmodel.Block{
ID: dbBlockID,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
URI: blockURI,
AccountID: blockingAccount.ID,
TargetAccountID: blockedAccount.ID,
}

err = suite.db.Put(context.Background(), dbBlock)
suite.NoError(err)

asBlock, err := suite.tc.BlockToAS(context.Background(), dbBlock)
suite.NoError(err)

targetAccountURI := testrig.URLMustParse(blockedAccount.URI)

// create an Undo and set the appropriate actor on it
undo := streams.NewActivityStreamsUndo()
undo.SetActivityStreamsActor(asBlock.GetActivityStreamsActor())

// Set the block as the 'object' property.
undoObject := streams.NewActivityStreamsObjectProperty()
undoObject.AppendActivityStreamsBlock(asBlock)
undo.SetActivityStreamsObject(undoObject)

// Set the To of the undo as the target of the block
undoTo := streams.NewActivityStreamsToProperty()
undoTo.AppendIRI(targetAccountURI)
undo.SetActivityStreamsTo(undoTo)

undoID := streams.NewJSONLDIdProperty()
undoID.SetIRI(testrig.URLMustParse("http://fossbros-anonymous.io/72cc96a3-f742-4daf-b9f5-3407667260c5"))
undo.SetJSONLDId(undoID)

targetURI := testrig.URLMustParse(blockedAccount.InboxURI)

signature, digestHeader, dateHeader := testrig.GetSignatureForActivity(undo, blockingAccount.PublicKeyURI, blockingAccount.PrivateKey, targetURI)
bodyI, err := streams.Serialize(undo)
suite.NoError(err)

bodyJson, err := json.Marshal(bodyI)
suite.NoError(err)
body := bytes.NewReader(bodyJson)

tc := testrig.NewTestTransportController(testrig.NewMockHTTPClient(nil), suite.db)
federator := testrig.NewTestFederator(suite.db, tc, suite.storage)
processor := testrig.NewTestProcessor(suite.db, suite.storage, federator)
userModule := user.New(suite.config, processor, suite.log).(*user.Module)

// setup request
recorder := httptest.NewRecorder()
ctx, _ := gin.CreateTestContext(recorder)
ctx.Request = httptest.NewRequest(http.MethodPost, targetURI.String(), body) // the endpoint we're hitting
ctx.Request.Header.Set("Signature", signature)
ctx.Request.Header.Set("Date", dateHeader)
ctx.Request.Header.Set("Digest", digestHeader)
ctx.Request.Header.Set("Content-Type", "application/activity+json")

// we need to pass the context through signature check first to set appropriate values on it
suite.securityModule.SignatureCheck(ctx)

// normally the router would populate these params from the path values,
// but because we're calling the function directly, we need to set them manually.
ctx.Params = gin.Params{
gin.Param{
Key: user.UsernameKey,
Value: blockedAccount.Username,
},
}

// trigger the function being tested
userModule.InboxPOSTHandler(ctx)

result := recorder.Result()
defer result.Body.Close()
b, err := ioutil.ReadAll(result.Body)
suite.NoError(err)
suite.Empty(b)
suite.Equal(http.StatusOK, result.StatusCode)

// the block should be undone
block, err := suite.db.GetBlock(context.Background(), blockingAccount.ID, blockedAccount.ID)
suite.ErrorIs(err, db.ErrNoEntries)
suite.Nil(block)
}

func TestInboxPostTestSuite(t *testing.T) {
suite.Run(t, &InboxPostTestSuite{})
}
20 changes: 19 additions & 1 deletion internal/api/s2s/user/publickeyget.go
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

package user

import (
Expand Down Expand Up @@ -33,7 +51,7 @@ func (m *Module) PublicKeyGETHandler(c *gin.Context) {
}
l.Tracef("negotiated format: %s", format)

ctx := populateContext(c)
ctx := transferContext(c)

user, errWithCode := m.processor.GetFediUser(ctx, requestedUsername, c.Request.URL)
if errWithCode != nil {
Expand Down
20 changes: 19 additions & 1 deletion internal/api/s2s/user/repliesget.go
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

package user

import (
Expand Down Expand Up @@ -121,7 +139,7 @@ func (m *Module) StatusRepliesGETHandler(c *gin.Context) {
}
l.Tracef("negotiated format: %s", format)

ctx := populateContext(c)
ctx := transferContext(c)

replies, errWithCode := m.processor.GetFediStatusReplies(ctx, requestedUsername, requestedStatusID, page, onlyOtherAccounts, minID, c.Request.URL)
if errWithCode != nil {
Expand Down
18 changes: 18 additions & 0 deletions internal/api/s2s/user/repliesget_test.go
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.
*/

package user_test

import (
Expand Down
Loading

0 comments on commit c7cfbe2

Please sign in to comment.