Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

api: Disable body limit middleware for admin endpoints. #5486

Merged
merged 6 commits into from
Jun 22, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 18 additions & 14 deletions daemon/algod/api/server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ const (
apiV1Tag = "/v1"
// TokenHeader is the header where we put the token.
TokenHeader = "X-Algo-API-Token"
// maxRequestBodyBytes is the maximum request body size that we allow in our APIs.
maxRequestBodyBytes = "10MB"
// MaxRequestBodyBytes is the maximum request body size that we allow in our APIs.
MaxRequestBodyBytes = "10MB"
)

// wrapCtx passes a common context to each request without a global variable.
Expand All @@ -79,8 +79,13 @@ func NewRouter(logger logging.Logger, node APINodeInterface, shutdown <-chan str
if err := tokens.ValidateAPIToken(adminAPIToken); err != nil {
logger.Errorf("Invalid adminAPIToken was passed to NewRouter ('%s'): %v", adminAPIToken, err)
}
adminAuthenticator := middlewares.MakeAuth(TokenHeader, []string{adminAPIToken})
apiAuthenticator := middlewares.MakeAuth(TokenHeader, []string{adminAPIToken, apiToken})
adminMiddleware := []echo.MiddlewareFunc{
middlewares.MakeAuth(TokenHeader, []string{adminAPIToken}),
}
publicMiddleware := []echo.MiddlewareFunc{
middleware.BodyLimit(MaxRequestBodyBytes),
middlewares.MakeAuth(TokenHeader, []string{adminAPIToken, apiToken}),
}

e := echo.New()

Expand All @@ -93,7 +98,6 @@ func NewRouter(logger logging.Logger, node APINodeInterface, shutdown <-chan str
e.Use(
middlewares.MakeLogger(logger),
middlewares.MakeCORS(TokenHeader),
middleware.BodyLimit(maxRequestBodyBytes),
)

// Request Context
Expand All @@ -104,32 +108,32 @@ func NewRouter(logger logging.Logger, node APINodeInterface, shutdown <-chan str
// Route pprof requests to DefaultServeMux.
// The auth middleware removes /urlAuth/:token so that it can be routed correctly.
if node.Config().EnableProfiler {
e.GET("/debug/pprof/*", echo.WrapHandler(http.DefaultServeMux), adminAuthenticator)
e.GET(fmt.Sprintf("%s/debug/pprof/*", middlewares.URLAuthPrefix), echo.WrapHandler(http.DefaultServeMux), adminAuthenticator)
e.GET("/debug/pprof/*", echo.WrapHandler(http.DefaultServeMux), adminMiddleware...)
e.GET(fmt.Sprintf("%s/debug/pprof/*", middlewares.URLAuthPrefix), echo.WrapHandler(http.DefaultServeMux), adminMiddleware...)
}
// Registering common routes (no auth)
registerHandlers(e, "", common.Routes, ctx)

// Registering v1 routes
registerHandlers(e, apiV1Tag, routes.V1Routes, ctx, apiAuthenticator)
registerHandlers(e, apiV1Tag, routes.V1Routes, ctx, publicMiddleware...)

// Registering v2 routes
v2Handler := v2.Handlers{
Node: node,
Log: logger,
Shutdown: shutdown,
}
nppublic.RegisterHandlers(e, &v2Handler, apiAuthenticator)
npprivate.RegisterHandlers(e, &v2Handler, adminAuthenticator)
ppublic.RegisterHandlers(e, &v2Handler, apiAuthenticator)
pprivate.RegisterHandlers(e, &v2Handler, adminAuthenticator)
nppublic.RegisterHandlers(e, &v2Handler, publicMiddleware...)
npprivate.RegisterHandlers(e, &v2Handler, adminMiddleware...)
ppublic.RegisterHandlers(e, &v2Handler, publicMiddleware...)
pprivate.RegisterHandlers(e, &v2Handler, adminMiddleware...)

if node.Config().EnableFollowMode {
data.RegisterHandlers(e, &v2Handler, apiAuthenticator)
data.RegisterHandlers(e, &v2Handler, publicMiddleware...)
}

if node.Config().EnableExperimentalAPI {
experimental.RegisterHandlers(e, &v2Handler, apiAuthenticator)
experimental.RegisterHandlers(e, &v2Handler, publicMiddleware...)
}

return e
Expand Down
41 changes: 16 additions & 25 deletions daemon/algod/api/server/router_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,24 +20,16 @@ import (
"net/http/httptest"
"testing"

"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"

"github.com/algorand/go-algorand/daemon/algod/api/server/lib"
"github.com/algorand/go-algorand/daemon/algod/api/server/v1/routes"

"github.com/algorand/go-algorand/logging"
"github.com/algorand/go-algorand/test/partitiontest"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
)

type TestSuite struct {
suite.Suite
e *echo.Echo
}

func (s *TestSuite) SetupSuite() {
s.e = echo.New()
func setupRouter() *echo.Echo {
e := echo.New()
// Make a deep copy of the routes array with handlers.
v1RoutesCopy := make([]lib.Route, len(routes.V1Routes))
for _, route := range routes.V1Routes {
Expand All @@ -51,10 +43,13 @@ func (s *TestSuite) SetupSuite() {
// Make a ReqContext with an initialized logger to prevent nil dereferencing.
reqCtx := lib.ReqContext{Log: logging.NewLogger()}
// Registering v1 routes
registerHandlers(s.e, apiV1Tag, v1RoutesCopy, reqCtx)
registerHandlers(e, apiV1Tag, v1RoutesCopy, reqCtx)
return e
}

func (s *TestSuite) TestGetTransactionV1Sunset() {
func TestGetTransactionV1Sunset(t *testing.T) {
partitiontest.PartitionTest(t)

testCases := []struct {
path string
route string
Expand All @@ -67,21 +62,17 @@ func (s *TestSuite) TestGetTransactionV1Sunset() {
}

rec := httptest.NewRecorder()
ctx := s.e.NewContext(nil, rec)
e := setupRouter()
ctx := e.NewContext(nil, rec)

for _, testCase := range testCases {
s.e.Router().Find(http.MethodGet, testCase.path, ctx)
assert.Equal(s.T(), testCase.route, ctx.Path())
e.Router().Find(http.MethodGet, testCase.path, ctx)
assert.Equal(t, testCase.route, ctx.Path())

// Check that router correctly routes to the v1Sunset handler.
assert.Equal(s.T(), nil, ctx.Handler()(ctx))
assert.NotNil(s.T(), rec.Body)
assert.Equal(s.T(), http.StatusGone, rec.Code)
assert.Equal(t, nil, ctx.Handler()(ctx))
assert.NotNil(t, rec.Body)
assert.Equal(t, http.StatusGone, rec.Code)
}

}

func TestTestSuite(t *testing.T) {
partitiontest.PartitionTest(t)
suite.Run(t, new(TestSuite))
}
37 changes: 36 additions & 1 deletion daemon/algod/api/server/v2/test/handlers_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,16 @@ import (
"fmt"
"io"
"math"
"net"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"

"github.com/algorand/go-algorand/daemon/algod/api/server"
"github.com/algorand/go-algorand/ledger/eval"
"github.com/algorand/go-algorand/ledger/ledgercore"

"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
Expand Down Expand Up @@ -2203,3 +2204,37 @@ func TestDeltasForTxnGroup(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 501, rec.Code)
}

func TestRouterRequestBody(t *testing.T) {
partitiontest.PartitionTest(t)
t.Parallel()

mockLedger, _, _, _, _ := testingenv(t, 1, 1, true)
mockNode := makeMockNode(mockLedger, t.Name(), nil, cannedStatusReportGolden, false)
dummyShutdownChan := make(chan struct{})
l, err := net.Listen("tcp", ":0") // create listener so requests are buffered
e := server.NewRouter(logging.TestingLog(t), mockNode, dummyShutdownChan, "", "", l, 1000)
go e.Start(":0")
defer e.Close()

// Admin API call greater than max body bytes should succeed
assert.Equal(t, "10MB", server.MaxRequestBodyBytes)
stringReader := strings.NewReader(strings.Repeat("a", 50_000_000))
fmt.Println(e.Listener)
winder marked this conversation as resolved.
Show resolved Hide resolved
fmt.Println(e.Listener.Addr())
fmt.Println(e.Listener.Addr().String())
req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("https://%s/v2/participation", e.Listener.Addr().String()), stringReader)
assert.NoError(t, err)
rec := httptest.NewRecorder()
e.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)

// Public API call greater than max body bytes fails
assert.Equal(t, "10MB", server.MaxRequestBodyBytes)
stringReader = strings.NewReader(strings.Repeat("a", 50_000_000))
req, err = http.NewRequest(http.MethodPost, fmt.Sprintf("https://%s/v2/transactions", e.Listener.Addr().String()), stringReader)
assert.NoError(t, err)
rec = httptest.NewRecorder()
e.ServeHTTP(rec, req)
assert.Equal(t, http.StatusRequestEntityTooLarge, rec.Code)
}
2 changes: 1 addition & 1 deletion daemon/algod/api/server/v2/test/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ type mockNode struct {
}

func (m *mockNode) InstallParticipationKey(partKeyBinary []byte) (account.ParticipationID, error) {
panic("implement me")
return account.ParticipationID{}, nil
}

func (m *mockNode) ListParticipationKeys() ([]account.ParticipationRecord, error) {
Expand Down