From 88d18536e91f01b69399b5689eb324c8d6b400a5 Mon Sep 17 00:00:00 2001 From: nullun Date: Sun, 16 Jun 2024 15:04:37 +0100 Subject: [PATCH 01/10] Allow Private Network Access requests on preflight --- daemon/algod/api/server/lib/middlewares/cors.go | 12 ++++++++++++ daemon/algod/api/server/router.go | 1 + 2 files changed, 13 insertions(+) diff --git a/daemon/algod/api/server/lib/middlewares/cors.go b/daemon/algod/api/server/lib/middlewares/cors.go index 89e88dabb6..914e75cf47 100644 --- a/daemon/algod/api/server/lib/middlewares/cors.go +++ b/daemon/algod/api/server/lib/middlewares/cors.go @@ -31,3 +31,15 @@ func MakeCORS(tokenHeader string) echo.MiddlewareFunc { AllowMethods: []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete, http.MethodOptions}, }) } + +func MakePNA() echo.MiddlewareFunc { + return func(next echo.HandlerFunc) echo.HandlerFunc { + return func(ctx echo.Context) error { + req := ctx.Request() + if req.Method == http.MethodOptions && req.Header.Get("Access-Control-Request-Private-Network") == "true" { + ctx.Response().Header().Set("Access-Control-Allow-Private-Network", "true") + } + return next(ctx) + } + } +} diff --git a/daemon/algod/api/server/router.go b/daemon/algod/api/server/router.go index 0b02bb8566..f1a0bb0401 100644 --- a/daemon/algod/api/server/router.go +++ b/daemon/algod/api/server/router.go @@ -107,6 +107,7 @@ func NewRouter(logger logging.Logger, node APINodeInterface, shutdown <-chan str middleware.RemoveTrailingSlash()) e.Use( middlewares.MakeLogger(logger), + middlewares.MakePNA(), middlewares.MakeCORS(TokenHeader), ) From e22248b90a35ed575ba9179974417138df92e72a Mon Sep 17 00:00:00 2001 From: nullun Date: Mon, 17 Jun 2024 13:47:14 +0100 Subject: [PATCH 02/10] Allow PNA preflight requests on KMD --- daemon/kmd/api/api.go | 1 + daemon/kmd/api/cors.go | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/daemon/kmd/api/api.go b/daemon/kmd/api/api.go index 084b6f882b..2a8a0155d2 100644 --- a/daemon/kmd/api/api.go +++ b/daemon/kmd/api/api.go @@ -141,6 +141,7 @@ func Handler(sm *session.Manager, log logging.Logger, allowedOrigins []string, a rootRouter := mux.NewRouter() // Send the appropriate CORS headers + rootRouter.Use(AllowPNA()) rootRouter.Use(corsMiddleware(allowedOrigins)) // Handle OPTIONS requests diff --git a/daemon/kmd/api/cors.go b/daemon/kmd/api/cors.go index 6ff8e38453..953ab998da 100644 --- a/daemon/kmd/api/cors.go +++ b/daemon/kmd/api/cors.go @@ -56,3 +56,15 @@ func corsMiddleware(allowedOrigins []string) func(http.Handler) http.Handler { }) } } + +func AllowPNA() func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodOptions && r.Header.Get("Access-Control-Request-Private-Network") == "true" { + w.Header().Set("Access-Control-Allow-Private-Network", "true") + } + + next.ServeHTTP(w, r) + }) + } +} From 7f7301c75a02669f58ee6a14f1c540e5c777cd06 Mon Sep 17 00:00:00 2001 From: nullun Date: Fri, 26 Jul 2024 10:50:55 +0100 Subject: [PATCH 03/10] Add config option and initial tests --- config/localTemplate.go | 3 +++ config/local_defaults.go | 1 + daemon/algod/api/server/router.go | 7 ++++++- installer/config.json.example | 1 + test/e2e-go/cli/goal/expect/corsTest.exp | 4 ++++ test/e2e-go/cli/goal/expect/goalExpectCommon.exp | 14 ++++++++++++++ test/testdata/configs/config-v34.json | 1 + 7 files changed, 30 insertions(+), 1 deletion(-) diff --git a/config/localTemplate.go b/config/localTemplate.go index 9583a194cd..9d6a9688bd 100644 --- a/config/localTemplate.go +++ b/config/localTemplate.go @@ -167,6 +167,9 @@ type Local struct { // EndpointAddress configures the address the node listens to for REST API calls. Specify an IP and port or just port. For example, 127.0.0.1:0 will listen on a random port on the localhost (preferring 8080). EndpointAddress string `version[0]:"127.0.0.1:0"` + // Respond to Private Network Access preflight requests sent to the node. Useful when a public website is trying to access a node that's hosted on a local network. + EnablePrivateNetworkAccessHeader bool `version[34]:"false"` + // RestReadTimeoutSeconds is passed to the API servers rest http.Server implementation. RestReadTimeoutSeconds int `version[4]:"15"` diff --git a/config/local_defaults.go b/config/local_defaults.go index 57457531be..4c2c16a359 100644 --- a/config/local_defaults.go +++ b/config/local_defaults.go @@ -77,6 +77,7 @@ var defaultLocal = Local{ EnableP2P: false, EnableP2PHybridMode: false, EnablePingHandler: true, + EnablePrivateNetworkAccessHeader: false, EnableProcessBlockStats: false, EnableProfiler: false, EnableRequestLogger: false, diff --git a/daemon/algod/api/server/router.go b/daemon/algod/api/server/router.go index f1a0bb0401..555f2813a1 100644 --- a/daemon/algod/api/server/router.go +++ b/daemon/algod/api/server/router.go @@ -107,7 +107,12 @@ func NewRouter(logger logging.Logger, node APINodeInterface, shutdown <-chan str middleware.RemoveTrailingSlash()) e.Use( middlewares.MakeLogger(logger), - middlewares.MakePNA(), + ) + // Optional middleware for Private Network Access Header (PNA). Must come before CORS middleware. + if node.Config().EnablePrivateNetworkAccessHeader { + e.Use(middlewares.MakePNA()) + } + e.Use( middlewares.MakeCORS(TokenHeader), ) diff --git a/installer/config.json.example b/installer/config.json.example index 3a9714bbfb..d68aec24eb 100644 --- a/installer/config.json.example +++ b/installer/config.json.example @@ -56,6 +56,7 @@ "EnableP2P": false, "EnableP2PHybridMode": false, "EnablePingHandler": true, + "EnablePrivateNetworkAccessHeader": false, "EnableProcessBlockStats": false, "EnableProfiler": false, "EnableRequestLogger": false, diff --git a/test/e2e-go/cli/goal/expect/corsTest.exp b/test/e2e-go/cli/goal/expect/corsTest.exp index 7691b740fa..0ba9fa4cc0 100755 --- a/test/e2e-go/cli/goal/expect/corsTest.exp +++ b/test/e2e-go/cli/goal/expect/corsTest.exp @@ -31,6 +31,10 @@ if { [catch { set ALGOD_NET_ADDRESS [::AlgorandGoal::GetAlgodNetworkAddress $TEST_PRIMARY_NODE_DIR] ::AlgorandGoal::CheckNetworkAddressForCors $ALGOD_NET_ADDRESS + # Hit algod with a private network access preflight request and look for 200 OK + set ALGOD_NET_ADDRESS [::AlgorandGoal::GetAlgodNetworkAddress $TEST_PRIMARY_NODE_DIR] + ::AlgorandGoal::CheckNetworkAddressForPNA $ALGOD_NET_ADDRESS + # Start kmd, then do the same CORS check as algod exec goal kmd start -t 180 -d $TEST_PRIMARY_NODE_DIR set KMD_NET_ADDRESS [::AlgorandGoal::GetKMDNetworkAddress $TEST_PRIMARY_NODE_DIR] diff --git a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp index 4728f445df..4033a24d6a 100644 --- a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp +++ b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp @@ -817,6 +817,20 @@ proc ::AlgorandGoal::CheckNetworkAddressForCors { NET_ADDRESS } { } } +# Use curl to check if a network address supports private network access +proc ::AlgorandGoal::CheckNetworkAddressForPNA { NET_ADDRESS } { + if { [ catch { + spawn curl -X OPTIONS -H "Access-Control-Request-Private-Network: true" --head $NET_ADDRESS + expect { + timeout { close; ::AlgorandGoal::Abort "Timeout failure in CheckNetworkAddressForPNA" } + "Access-Control-Allow-Private-Network" { puts "success" ; close } + close + } + } EXCEPTION ] } { + ::AlgorandGoal::Abort "ERROR in CheckNetworkAddressForPNA: $EXCEPTION" + } +} + # Show the Ledger Supply proc ::AlgorandGoal::GetLedgerSupply { TEST_PRIMARY_NODE_DIR } { if { [ catch { diff --git a/test/testdata/configs/config-v34.json b/test/testdata/configs/config-v34.json index 3a9714bbfb..d68aec24eb 100644 --- a/test/testdata/configs/config-v34.json +++ b/test/testdata/configs/config-v34.json @@ -56,6 +56,7 @@ "EnableP2P": false, "EnableP2PHybridMode": false, "EnablePingHandler": true, + "EnablePrivateNetworkAccessHeader": false, "EnableProcessBlockStats": false, "EnableProfiler": false, "EnableRequestLogger": false, From d26fc33c1c967832557e63a6b03cda8715255def Mon Sep 17 00:00:00 2001 From: nullun Date: Tue, 30 Jul 2024 16:48:40 +0100 Subject: [PATCH 04/10] Add config option for KMD --- daemon/kmd/api/api.go | 6 ++++-- daemon/kmd/config/config.go | 11 ++++++----- daemon/kmd/kmd.go | 15 ++++++++------- daemon/kmd/server/server.go | 17 +++++++++-------- 4 files changed, 27 insertions(+), 22 deletions(-) diff --git a/daemon/kmd/api/api.go b/daemon/kmd/api/api.go index 2a8a0155d2..c33d5aa451 100644 --- a/daemon/kmd/api/api.go +++ b/daemon/kmd/api/api.go @@ -137,11 +137,13 @@ func SwaggerHandler(w http.ResponseWriter, r *http.Request) { // Handler returns the root mux router for the kmd API. It sets up handlers on // subrouters specific to each API version. -func Handler(sm *session.Manager, log logging.Logger, allowedOrigins []string, apiToken string, reqCB func()) *mux.Router { +func Handler(sm *session.Manager, log logging.Logger, allowedOrigins []string, apiToken string, pnaHeader bool, reqCB func()) *mux.Router { rootRouter := mux.NewRouter() // Send the appropriate CORS headers - rootRouter.Use(AllowPNA()) + if pnaHeader { + rootRouter.Use(AllowPNA()) + } rootRouter.Use(corsMiddleware(allowedOrigins)) // Handle OPTIONS requests diff --git a/daemon/kmd/config/config.go b/daemon/kmd/config/config.go index 5dce6ba660..4eb947634b 100644 --- a/daemon/kmd/config/config.go +++ b/daemon/kmd/config/config.go @@ -35,11 +35,12 @@ const ( // KMDConfig contains global configuration information for kmd type KMDConfig struct { - DataDir string `json:"-"` - DriverConfig DriverConfig `json:"drivers"` - SessionLifetimeSecs uint64 `json:"session_lifetime_secs"` - Address string `json:"address"` - AllowedOrigins []string `json:"allowed_origins"` + DataDir string `json:"-"` + DriverConfig DriverConfig `json:"drivers"` + SessionLifetimeSecs uint64 `json:"session_lifetime_secs"` + Address string `json:"address"` + AllowedOrigins []string `json:"allowed_origins"` + EnablePrivateNetworkAccessHeader bool `json:"enable_private_network_access_header"` } // DriverConfig contains config info specific to each wallet driver diff --git a/daemon/kmd/kmd.go b/daemon/kmd/kmd.go index 3d3ce3c92d..9f51eb7053 100644 --- a/daemon/kmd/kmd.go +++ b/daemon/kmd/kmd.go @@ -65,13 +65,14 @@ func Start(startConfig StartConfig) (died chan error, sock string, err error) { // Configure the wallet API server serverCfg := server.WalletServerConfig{ - APIToken: apiToken, - DataDir: startConfig.DataDir, - Address: kmdCfg.Address, - AllowedOrigins: kmdCfg.AllowedOrigins, - SessionManager: session.MakeManager(kmdCfg), - Log: startConfig.Log, - Timeout: startConfig.Timeout, + APIToken: apiToken, + DataDir: startConfig.DataDir, + Address: kmdCfg.Address, + AllowedOrigins: kmdCfg.AllowedOrigins, + EnablePrivateNetworkAccessHeader: kmdCfg.EnablePrivateNetworkAccessHeader, + SessionManager: session.MakeManager(kmdCfg), + Log: startConfig.Log, + Timeout: startConfig.Timeout, } // Instantiate the wallet API server diff --git a/daemon/kmd/server/server.go b/daemon/kmd/server/server.go index 712583d47a..d167e75c98 100644 --- a/daemon/kmd/server/server.go +++ b/daemon/kmd/server/server.go @@ -50,13 +50,14 @@ const ( // WalletServerConfig is the configuration passed to MakeWalletServer type WalletServerConfig struct { - APIToken string - DataDir string - Address string - AllowedOrigins []string - SessionManager *session.Manager - Log logging.Logger - Timeout *time.Duration + APIToken string + DataDir string + Address string + AllowedOrigins []string + EnablePrivateNetworkAccessHeader bool + SessionManager *session.Manager + Log logging.Logger + Timeout *time.Duration } // WalletServer deals with serving API requests @@ -211,7 +212,7 @@ func (ws *WalletServer) start(kill chan os.Signal) (died chan error, sock string // Initialize HTTP server watchdogCB := ws.makeWatchdogCallback(kill) srv := http.Server{ - Handler: api.Handler(ws.SessionManager, ws.Log, ws.AllowedOrigins, ws.APIToken, watchdogCB), + Handler: api.Handler(ws.SessionManager, ws.Log, ws.AllowedOrigins, ws.APIToken, ws.EnablePrivateNetworkAccessHeader, watchdogCB), } // Read the kill channel and shut down the server gracefully From 609290ad163197911b469f1727d15da150c043cf Mon Sep 17 00:00:00 2001 From: nullun Date: Wed, 31 Jul 2024 16:35:27 +0100 Subject: [PATCH 05/10] gofmt and add comment to exported function --- daemon/algod/api/server/lib/middlewares/cors.go | 1 + daemon/kmd/api/cors.go | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/daemon/algod/api/server/lib/middlewares/cors.go b/daemon/algod/api/server/lib/middlewares/cors.go index 914e75cf47..c8b292703f 100644 --- a/daemon/algod/api/server/lib/middlewares/cors.go +++ b/daemon/algod/api/server/lib/middlewares/cors.go @@ -32,6 +32,7 @@ func MakeCORS(tokenHeader string) echo.MiddlewareFunc { }) } +// MakePNA constructs the Private Network Access middleware function func MakePNA() echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(ctx echo.Context) error { diff --git a/daemon/kmd/api/cors.go b/daemon/kmd/api/cors.go index 953ab998da..0384de5306 100644 --- a/daemon/kmd/api/cors.go +++ b/daemon/kmd/api/cors.go @@ -57,6 +57,7 @@ func corsMiddleware(allowedOrigins []string) func(http.Handler) http.Handler { } } +// AllowPNA constructs the Private Network Access middleware function func AllowPNA() func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { @@ -65,6 +66,6 @@ func AllowPNA() func(http.Handler) http.Handler { } next.ServeHTTP(w, r) - }) - } + }) + } } From 0aba6461428e654dfb10eb965a67a9fb702aabc8 Mon Sep 17 00:00:00 2001 From: nullun Date: Mon, 2 Sep 2024 16:45:42 +0100 Subject: [PATCH 06/10] Use a PNA specific config for Cors tests --- test/e2e-go/cli/goal/expect/corsTest.exp | 2 +- .../cli/goal/expect/goalExpectCommon.exp | 3 ++ .../nettemplates/TwoNodes50EachPNA.json | 37 +++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 test/testdata/nettemplates/TwoNodes50EachPNA.json diff --git a/test/e2e-go/cli/goal/expect/corsTest.exp b/test/e2e-go/cli/goal/expect/corsTest.exp index 0ba9fa4cc0..81a496489f 100755 --- a/test/e2e-go/cli/goal/expect/corsTest.exp +++ b/test/e2e-go/cli/goal/expect/corsTest.exp @@ -15,7 +15,7 @@ if { [catch { set TEST_ROOT_DIR $TEST_ALGO_DIR/root set TEST_PRIMARY_NODE_DIR $TEST_ROOT_DIR/Primary/ set NETWORK_NAME test_net_expect_$TIME_STAMP - set NETWORK_TEMPLATE "$TEST_DATA_DIR/nettemplates/TwoNodes50Each.json" + set NETWORK_TEMPLATE "$TEST_DATA_DIR/nettemplates/TwoNodes50EachPNA.json" # Create network ::AlgorandGoal::CreateNetwork $NETWORK_NAME $NETWORK_TEMPLATE $TEST_ALGO_DIR $TEST_ROOT_DIR diff --git a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp index 4033a24d6a..54eda50240 100644 --- a/test/e2e-go/cli/goal/expect/goalExpectCommon.exp +++ b/test/e2e-go/cli/goal/expect/goalExpectCommon.exp @@ -824,6 +824,9 @@ proc ::AlgorandGoal::CheckNetworkAddressForPNA { NET_ADDRESS } { expect { timeout { close; ::AlgorandGoal::Abort "Timeout failure in CheckNetworkAddressForPNA" } "Access-Control-Allow-Private-Network" { puts "success" ; close } + eof { + return -code error "EOF without Access-Control-Allow-Private-Network" + } close } } EXCEPTION ] } { diff --git a/test/testdata/nettemplates/TwoNodes50EachPNA.json b/test/testdata/nettemplates/TwoNodes50EachPNA.json new file mode 100644 index 0000000000..2777bab9d2 --- /dev/null +++ b/test/testdata/nettemplates/TwoNodes50EachPNA.json @@ -0,0 +1,37 @@ +{ + "Genesis": { + "NetworkName": "tbd", + "LastPartKeyRound": 3000, + "Wallets": [ + { + "Name": "Wallet1", + "Stake": 50, + "Online": true + }, + { + "Name": "Wallet2", + "Stake": 50, + "Online": true + } + ] + }, + "Nodes": [ + { + "Name": "Primary", + "IsRelay": true, + "ConfigJSONOverride": "{\"EnablePrivateNetworkAccessHeader\":true}", + "Wallets": [ + { "Name": "Wallet1", + "ParticipationOnly": false } + ] + }, + { + "Name": "Node", + "ConfigJSONOverride": "{\"EnablePrivateNetworkAccessHeader\":true}", + "Wallets": [ + { "Name": "Wallet2", + "ParticipationOnly": false } + ] + } + ] +} From b0321e8f278e983790678a7037b03fb5057ab8f5 Mon Sep 17 00:00:00 2001 From: nullun Date: Tue, 3 Sep 2024 09:39:26 +0100 Subject: [PATCH 07/10] Shortened KMD's PNA config name --- daemon/kmd/config/config.go | 12 ++++++------ daemon/kmd/kmd.go | 16 ++++++++-------- daemon/kmd/server/server.go | 18 +++++++++--------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/daemon/kmd/config/config.go b/daemon/kmd/config/config.go index 4eb947634b..468db8ec03 100644 --- a/daemon/kmd/config/config.go +++ b/daemon/kmd/config/config.go @@ -35,12 +35,12 @@ const ( // KMDConfig contains global configuration information for kmd type KMDConfig struct { - DataDir string `json:"-"` - DriverConfig DriverConfig `json:"drivers"` - SessionLifetimeSecs uint64 `json:"session_lifetime_secs"` - Address string `json:"address"` - AllowedOrigins []string `json:"allowed_origins"` - EnablePrivateNetworkAccessHeader bool `json:"enable_private_network_access_header"` + DataDir string `json:"-"` + DriverConfig DriverConfig `json:"drivers"` + SessionLifetimeSecs uint64 `json:"session_lifetime_secs"` + Address string `json:"address"` + AllowedOrigins []string `json:"allowed_origins"` + AllowHeaderPNA bool `json:"allow_header_pna"` } // DriverConfig contains config info specific to each wallet driver diff --git a/daemon/kmd/kmd.go b/daemon/kmd/kmd.go index 9f51eb7053..220dd8da5b 100644 --- a/daemon/kmd/kmd.go +++ b/daemon/kmd/kmd.go @@ -65,14 +65,14 @@ func Start(startConfig StartConfig) (died chan error, sock string, err error) { // Configure the wallet API server serverCfg := server.WalletServerConfig{ - APIToken: apiToken, - DataDir: startConfig.DataDir, - Address: kmdCfg.Address, - AllowedOrigins: kmdCfg.AllowedOrigins, - EnablePrivateNetworkAccessHeader: kmdCfg.EnablePrivateNetworkAccessHeader, - SessionManager: session.MakeManager(kmdCfg), - Log: startConfig.Log, - Timeout: startConfig.Timeout, + APIToken: apiToken, + DataDir: startConfig.DataDir, + Address: kmdCfg.Address, + AllowedOrigins: kmdCfg.AllowedOrigins, + AllowHeaderPNA: kmdCfg.AllowHeaderPNA, + SessionManager: session.MakeManager(kmdCfg), + Log: startConfig.Log, + Timeout: startConfig.Timeout, } // Instantiate the wallet API server diff --git a/daemon/kmd/server/server.go b/daemon/kmd/server/server.go index d167e75c98..58638f0bee 100644 --- a/daemon/kmd/server/server.go +++ b/daemon/kmd/server/server.go @@ -50,14 +50,14 @@ const ( // WalletServerConfig is the configuration passed to MakeWalletServer type WalletServerConfig struct { - APIToken string - DataDir string - Address string - AllowedOrigins []string - EnablePrivateNetworkAccessHeader bool - SessionManager *session.Manager - Log logging.Logger - Timeout *time.Duration + APIToken string + DataDir string + Address string + AllowedOrigins []string + AllowHeaderPNA bool + SessionManager *session.Manager + Log logging.Logger + Timeout *time.Duration } // WalletServer deals with serving API requests @@ -212,7 +212,7 @@ func (ws *WalletServer) start(kill chan os.Signal) (died chan error, sock string // Initialize HTTP server watchdogCB := ws.makeWatchdogCallback(kill) srv := http.Server{ - Handler: api.Handler(ws.SessionManager, ws.Log, ws.AllowedOrigins, ws.APIToken, ws.EnablePrivateNetworkAccessHeader, watchdogCB), + Handler: api.Handler(ws.SessionManager, ws.Log, ws.AllowedOrigins, ws.APIToken, ws.AllowHeaderPNA, watchdogCB), } // Read the kill channel and shut down the server gracefully From da187c2a5135359a0b820f03a66659d826041a0d Mon Sep 17 00:00:00 2001 From: nullun Date: Tue, 17 Sep 2024 13:56:31 +0100 Subject: [PATCH 08/10] Tests added for CORS/PNA --- .../api/server/lib/middlewares/cors_test.go | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 daemon/algod/api/server/lib/middlewares/cors_test.go diff --git a/daemon/algod/api/server/lib/middlewares/cors_test.go b/daemon/algod/api/server/lib/middlewares/cors_test.go new file mode 100644 index 0000000000..68765a7ec7 --- /dev/null +++ b/daemon/algod/api/server/lib/middlewares/cors_test.go @@ -0,0 +1,129 @@ +package middlewares + +import ( + "net/http" + "net/http/httptest" + "testing" + + "github.com/labstack/echo/v4" + "github.com/stretchr/testify/assert" +) + +func TestMakeCORS(t *testing.T) { + e := echo.New() + tokenHeader := "X-Algo-API-Token" + corsMiddleware := MakeCORS(tokenHeader) + + testCases := []struct { + name string + method string + headers map[string]string + expectedStatus int + expectedHeaders map[string]string + }{ + { + name: "OPTIONS request", + method: http.MethodOptions, + headers: map[string]string{ + "Origin": "http://algorand.com", + "Access-Control-Request-Headers": "Content-Type," + tokenHeader, + }, + expectedStatus: http.StatusNoContent, + expectedHeaders: map[string]string{ + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Methods": "GET,POST,PUT,DELETE,OPTIONS", + "Access-Control-Allow-Headers": tokenHeader + ",Content-Type", + }, + }, + { + name: "GET request", + method: http.MethodGet, + headers: map[string]string{ + "Origin": "http://algorand.com", + }, + expectedStatus: http.StatusOK, + expectedHeaders: map[string]string{ + "Access-Control-Allow-Origin": "*", + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + req := httptest.NewRequest(tc.method, "/health", nil) + for key, value := range tc.headers { + req.Header.Set(key, value) + } + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + + handler := corsMiddleware(func(c echo.Context) error { + return c.NoContent(http.StatusOK) + }) + + err := handler(c) + + assert.NoError(t, err) + assert.Equal(t, tc.expectedStatus, rec.Code) + for key, value := range tc.expectedHeaders { + assert.Equal(t, value, rec.Header().Get(key)) + } + }) + } +} + +func TestMakePNA(t *testing.T) { + e := echo.New() + pnaMiddleware := MakePNA() + + testCases := []struct { + name string + method string + headers map[string]string + expectedStatusCode int + expectedHeader string + }{ + { + name: "OPTIONS request with PNA header", + method: http.MethodOptions, + headers: map[string]string{"Access-Control-Request-Private-Network": "true"}, + expectedStatusCode: http.StatusOK, + expectedHeader: "true", + }, + { + name: "OPTIONS request without PNA header", + method: http.MethodOptions, + headers: map[string]string{}, + expectedStatusCode: http.StatusOK, + expectedHeader: "", + }, + { + name: "GET request", + method: http.MethodGet, + headers: map[string]string{}, + expectedStatusCode: http.StatusOK, + expectedHeader: "", + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + req := httptest.NewRequest(tc.method, "/", nil) + for key, value := range tc.headers { + req.Header.Set(key, value) + } + rec := httptest.NewRecorder() + c := e.NewContext(req, rec) + + handler := pnaMiddleware(func(c echo.Context) error { + return c.NoContent(http.StatusOK) + }) + + err := handler(c) + + assert.NoError(t, err) + assert.Equal(t, tc.expectedStatusCode, rec.Code) + assert.Equal(t, tc.expectedHeader, rec.Header().Get("Access-Control-Allow-Private-Network")) + }) + } +} From 9be9309786d9cbdf749ed7710d24399f1d9d0301 Mon Sep 17 00:00:00 2001 From: nullun Date: Tue, 17 Sep 2024 14:22:15 +0100 Subject: [PATCH 09/10] added license headed --- .../api/server/lib/middlewares/cors_test.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/daemon/algod/api/server/lib/middlewares/cors_test.go b/daemon/algod/api/server/lib/middlewares/cors_test.go index 68765a7ec7..307e16a203 100644 --- a/daemon/algod/api/server/lib/middlewares/cors_test.go +++ b/daemon/algod/api/server/lib/middlewares/cors_test.go @@ -1,3 +1,19 @@ +// Copyright (C) 2019-2024 Algorand, Inc. +// This file is part of go-algorand +// +// go-algorand 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. +// +// go-algorand 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 go-algorand. If not, see . + package middlewares import ( From 53d2216a6df29e8b92a24d4595c63a2e8ecfd163 Mon Sep 17 00:00:00 2001 From: cce <51567+cce@users.noreply.github.com> Date: Thu, 19 Sep 2024 10:54:53 -0400 Subject: [PATCH 10/10] fix partitiontest linter warnings --- daemon/algod/api/server/lib/middlewares/cors_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/daemon/algod/api/server/lib/middlewares/cors_test.go b/daemon/algod/api/server/lib/middlewares/cors_test.go index 307e16a203..032596cfb2 100644 --- a/daemon/algod/api/server/lib/middlewares/cors_test.go +++ b/daemon/algod/api/server/lib/middlewares/cors_test.go @@ -21,11 +21,13 @@ import ( "net/http/httptest" "testing" + "github.com/algorand/go-algorand/test/partitiontest" "github.com/labstack/echo/v4" "github.com/stretchr/testify/assert" ) func TestMakeCORS(t *testing.T) { + partitiontest.PartitionTest(t) e := echo.New() tokenHeader := "X-Algo-API-Token" corsMiddleware := MakeCORS(tokenHeader) @@ -89,6 +91,7 @@ func TestMakeCORS(t *testing.T) { } func TestMakePNA(t *testing.T) { + partitiontest.PartitionTest(t) e := echo.New() pnaMiddleware := MakePNA()