mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-28 11:22:25 -05:00
[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:
parent
be6d80c020
commit
e65bda768c
6 changed files with 120 additions and 4 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,3 +72,12 @@ func mapGet(m map[string]any, keys ...string) (any, bool) {
|
|||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func InstanceActor() string {
|
||||
a := GetHost()
|
||||
if a == "" {
|
||||
a = GetAccountDomain()
|
||||
}
|
||||
|
||||
return a
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"net/url"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/ap"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/db"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/gtserror"
|
||||
"code.superseriousbusiness.org/gotosocial/internal/uris"
|
||||
|
|
@ -46,6 +47,15 @@ func (p *Processor) UserGet(ctx context.Context, requestedUsername string, reque
|
|||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
|
||||
if requestedUsername == config.InstanceActor() && uris.IsInstanceActorPath(requestURL) {
|
||||
accountable, err := p.converter.AccountToAS(ctx, receiver)
|
||||
if err != nil {
|
||||
err := gtserror.Newf("error converting account: %w", err)
|
||||
return nil, gtserror.NewErrorInternalError(err)
|
||||
}
|
||||
return data(accountable)
|
||||
}
|
||||
|
||||
if uris.IsPublicKeyPath(requestURL) {
|
||||
// If request is on a public key path, we don't need to
|
||||
// authenticate this request. However, we'll only serve
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ package uris
|
|||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"code.superseriousbusiness.org/gotosocial/internal/config"
|
||||
|
|
@ -325,6 +326,11 @@ func IsPublicKeyPath(id *url.URL) bool {
|
|||
return regexes.PublicKeyPath.MatchString(id.Path)
|
||||
}
|
||||
|
||||
// IsInstanceActorPath returns true if the given URL corresponds to /users/instance_actor
|
||||
func IsInstanceActorPath(u *url.URL) bool {
|
||||
return u.Path == path.Join("/", "users", config.InstanceActor())
|
||||
}
|
||||
|
||||
// IsBlockPath returns true if the given URL path corresponds to eg /users/example_username/blocks/SOME_ULID_OF_A_BLOCK
|
||||
func IsBlockPath(id *url.URL) bool {
|
||||
return regexes.BlockPath.MatchString(id.Path)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue