[chore] No sigs for retrieving instance actor

As a possible path towards resolving #1186, this removes the signature
check on the instance actor. Also adds a test to check authentication
for a user other than the instance actor to ensure we don't poke a big
hole into that check.
This commit is contained in:
Daenney 2025-06-03 19:11:07 +02:00
commit e65bda768c
6 changed files with 120 additions and 4 deletions

View file

@ -21,6 +21,7 @@ import (
"code.superseriousbusiness.org/gotosocial/internal/api/activitypub/emoji"
"code.superseriousbusiness.org/gotosocial/internal/api/activitypub/publickey"
"code.superseriousbusiness.org/gotosocial/internal/api/activitypub/users"
"code.superseriousbusiness.org/gotosocial/internal/config"
"code.superseriousbusiness.org/gotosocial/internal/db"
"code.superseriousbusiness.org/gotosocial/internal/middleware"
"code.superseriousbusiness.org/gotosocial/internal/processing"
@ -40,14 +41,22 @@ func (a *ActivityPub) Route(r *router.Router, m ...gin.HandlerFunc) {
emojiGroup := r.AttachGroup("emoji")
usersGroup := r.AttachGroup("users")
emojiGroup.Use(m...)
usersGroup.Use(m...)
// attach shared, non-global middlewares to both of these groups
ccMiddleware := middleware.CacheControl(middleware.CacheControlConfig{
Directives: []string{"no-store"},
})
emojiGroup.Use(m...)
usersGroup.Use(m...)
emojiGroup.Use(a.signatureCheckMiddleware, ccMiddleware)
usersGroup.Use(a.signatureCheckMiddleware, ccMiddleware)
emojiGroup.Use(ccMiddleware, a.signatureCheckMiddleware)
usersGroup.Use(ccMiddleware)
// hook the instance actor route first so we don't require auth
usersGroup.GET(config.InstanceActor(), a.users.InstanceActorGETHandler)
// add signature checking to any other users routes
usersGroup.Use(a.signatureCheckMiddleware)
a.emoji.Route(emojiGroup.Handle)
a.users.Route(usersGroup.Handle)

View file

@ -23,6 +23,7 @@ import (
"strings"
apiutil "code.superseriousbusiness.org/gotosocial/internal/api/util"
"code.superseriousbusiness.org/gotosocial/internal/config"
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
"github.com/gin-gonic/gin"
)
@ -65,3 +66,19 @@ func (m *Module) UsersGETHandler(c *gin.Context) {
apiutil.JSONType(c, http.StatusOK, contentType, resp)
}
func (m *Module) InstanceActorGETHandler(c *gin.Context) {
contentType, err := apiutil.NegotiateAccept(c, apiutil.ActivityPubHeaders...)
if err != nil {
apiutil.ErrorHandler(c, gtserror.NewErrorNotAcceptable(err, err.Error()), m.processor.InstanceGetV1)
return
}
resp, errWithCode := m.processor.Fedi().UserGet(c.Request.Context(), config.InstanceActor(), c.Request.URL)
if errWithCode != nil {
apiutil.ErrorHandler(c, errWithCode, m.processor.InstanceGetV1)
return
}
apiutil.JSONType(c, http.StatusOK, contentType, resp)
}

View file

@ -19,6 +19,7 @@ package users_test
import (
"encoding/json"
"io"
"io/ioutil"
"net/http"
"net/http/httptest"
@ -158,6 +159,70 @@ func (suite *UserGetTestSuite) TestGetUserPublicKeyDeleted() {
suite.EqualValues(targetAccount.Username, a.Username)
}
func (suite *UserGetTestSuite) TestGetUserAuth() {
targetAccount := suite.testAccounts["local_account_1"]
// setup request
recorder := httptest.NewRecorder()
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.URI, nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/activity+json")
// we need to pass the context through signature check first to set appropriate values on it
suite.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: users.UsernameKey,
Value: targetAccount.Username,
},
}
// trigger the function being tested
suite.userModule.UsersGETHandler(ctx)
// check response
suite.EqualValues(http.StatusUnauthorized, recorder.Code)
}
func (suite *UserGetTestSuite) TestInstanceActor() {
targetAccount := suite.testAccounts["instance_account"]
// setup request
recorder := httptest.NewRecorder()
ctx, _ := testrig.CreateGinTestContext(recorder, nil)
ctx.Request = httptest.NewRequest(http.MethodGet, targetAccount.URI, nil) // the endpoint we're hitting
ctx.Request.Header.Set("accept", "application/activity+json")
// trigger the function being tested
suite.userModule.InstanceActorGETHandler(ctx)
// check response
suite.EqualValues(http.StatusOK, recorder.Code)
result := recorder.Result()
defer result.Body.Close()
b, err := io.ReadAll(result.Body)
suite.NoError(err)
// should be a Service
m := make(map[string]interface{})
err = json.Unmarshal(b, &m)
suite.NoError(err)
t, err := streams.ToType(suite.T().Context(), m)
suite.NoError(err)
// ensure it's a Service
svc, ok := t.(vocab.ActivityStreamsService)
suite.True(ok)
suite.Equal(targetAccount.Username, svc.GetActivityStreamsPreferredUsername().GetIRI().String())
}
func TestUserGetTestSuite(t *testing.T) {
suite.Run(t, new(UserGetTestSuite))
}