diff --git a/go.mod b/go.mod index 6dad8390f..9fd6c85bd 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/FZambia/eagle v0.1.0 github.com/FZambia/statik v0.1.2-0.20180217151304-b9f012bb2a1b github.com/centrifugal/centrifuge v0.33.5-0.20241026095149-106d43f21426 - github.com/centrifugal/protocol v0.13.5-0.20241030063749-f2e17f373602 + github.com/centrifugal/protocol v0.13.5-0.20241030080628-ab8a125839c1 github.com/cristalhq/jwt/v5 v5.4.0 github.com/go-viper/mapstructure/v2 v2.1.0 github.com/gobwas/glob v0.2.3 diff --git a/go.sum b/go.sum index 557a05f47..8d3bd83df 100644 --- a/go.sum +++ b/go.sum @@ -8,8 +8,8 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/centrifugal/centrifuge v0.33.5-0.20241026095149-106d43f21426 h1:g5zZaCr/BybYgq8Nqrnrvqvb3jGGO/Dloil3cFGzzbg= github.com/centrifugal/centrifuge v0.33.5-0.20241026095149-106d43f21426/go.mod h1:Ck+7H3eVwoeyabKcj3L55oSunaORIOGPAIVB5xrQyGU= -github.com/centrifugal/protocol v0.13.5-0.20241030063749-f2e17f373602 h1:GdltAOXAFqR9i/q/DWbGzWFXv6x5ff/oPgZFuwlIZFQ= -github.com/centrifugal/protocol v0.13.5-0.20241030063749-f2e17f373602/go.mod h1:7V5vI30VcoxJe4UD87xi7bOsvI0bmEhvbQuMjrFM2L4= +github.com/centrifugal/protocol v0.13.5-0.20241030080628-ab8a125839c1 h1:itkwGpNchf1X8djN+S90koPptcnxzRGh2atzdw6ZZtc= +github.com/centrifugal/protocol v0.13.5-0.20241030080628-ab8a125839c1/go.mod h1:7V5vI30VcoxJe4UD87xi7bOsvI0bmEhvbQuMjrFM2L4= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= diff --git a/internal/admin/handlers.go b/internal/admin/handlers.go index 3abb783ac..d3edb6b9d 100644 --- a/internal/admin/handlers.go +++ b/internal/admin/handlers.go @@ -60,7 +60,6 @@ func (s *Handler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { } func (s *Handler) adminSecureTokenAuth(h http.Handler) http.Handler { - secret := s.config.Secret insecure := s.config.Insecure diff --git a/internal/admin/handlers_test.go b/internal/admin/handlers_test.go new file mode 100644 index 000000000..4e873de1a --- /dev/null +++ b/internal/admin/handlers_test.go @@ -0,0 +1,166 @@ +package admin + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + + "github.com/centrifugal/centrifuge" + "github.com/stretchr/testify/require" +) + +// TestNewHandler ensures NewHandler sets up routes and proxy handling correctly. +func TestNewHandler(t *testing.T) { + node := ¢rifuge.Node{} + cfg := Config{HandlerPrefix: "/prefix", WebProxyAddress: "", WebPath: ""} + handler := NewHandler(node, nil, cfg) + require.NotNil(t, handler) + require.NotNil(t, handler.mux) + + // Validate settings route is registered. + req := httptest.NewRequest("GET", "/prefix/admin/settings", nil) + resp := httptest.NewRecorder() + handler.ServeHTTP(resp, req) + require.Equal(t, http.StatusOK, resp.Code) +} + +// TestSettingsHandler checks the settingsHandler returns correct settings. +func TestSettingsHandler(t *testing.T) { + config := Config{Insecure: true} + handler := &Handler{config: config} + req := httptest.NewRequest("GET", "/admin/settings", nil) + resp := httptest.NewRecorder() + + handler.settingsHandler(resp, req) + require.Equal(t, http.StatusOK, resp.Code) + + var response map[string]any + err := json.NewDecoder(resp.Body).Decode(&response) + require.NoError(t, err) + require.Equal(t, "oss", response["edition"]) + require.Equal(t, config.Insecure, response["insecure"]) +} + +// TestAuthHandler_NoPasswordOrSecret tests authHandler error when password or secret is missing. +func TestAuthHandler_NoPasswordOrSecret(t *testing.T) { + config := Config{Password: "", Secret: ""} + handler := &Handler{config: config} + req := httptest.NewRequest("POST", "/admin/auth", nil) + resp := httptest.NewRecorder() + + handler.authHandler(resp, req) + require.Equal(t, http.StatusBadRequest, resp.Code) +} + +// TestAuthHandler_ValidPassword tests authHandler token generation with valid password. +func TestAuthHandler_ValidPassword(t *testing.T) { + config := Config{Password: "test-password", Secret: "test-secret"} + handler := &Handler{config: config} + form := url.Values{} + form.Add("password", "test-password") + req := httptest.NewRequest("POST", "/admin/auth", strings.NewReader(form.Encode())) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + resp := httptest.NewRecorder() + + handler.authHandler(resp, req) + require.Equal(t, http.StatusOK, resp.Code) + + var response map[string]string + err := json.NewDecoder(resp.Body).Decode(&response) + require.NoError(t, err) + require.NotEmpty(t, response["token"]) +} + +// TestAuthHandler_InvalidPassword tests authHandler rejection with invalid password. +func TestAuthHandler_InvalidPassword(t *testing.T) { + config := Config{Password: "test-password", Secret: "test-secret"} + handler := &Handler{config: config} + form := url.Values{} + form.Add("password", "wrong-password") + req := httptest.NewRequest("POST", "/admin/auth", strings.NewReader(form.Encode())) + req.Header.Add("Content-Type", "application/x-www-form-urlencoded") + resp := httptest.NewRecorder() + + handler.authHandler(resp, req) + require.Equal(t, http.StatusBadRequest, resp.Code) +} + +// TestAdminSecureTokenAuth_InsecureMode tests adminSecureTokenAuth allows request in insecure mode. +func TestAdminSecureTokenAuth_InsecureMode(t *testing.T) { + config := Config{Insecure: true} + handler := &Handler{config: config} + + // Mocked handler that should be invoked + finalHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + authHandler := handler.adminSecureTokenAuth(finalHandler) + req := httptest.NewRequest("GET", "/admin/api", nil) + resp := httptest.NewRecorder() + + authHandler.ServeHTTP(resp, req) + require.Equal(t, http.StatusOK, resp.Code) +} + +// TestAdminSecureTokenAuth_MissingToken tests adminSecureTokenAuth rejection when token is missing. +func TestAdminSecureTokenAuth_MissingToken(t *testing.T) { + config := Config{Secret: "test-secret"} + handler := &Handler{config: config} + + // Mocked handler that should not be invoked + finalHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + authHandler := handler.adminSecureTokenAuth(finalHandler) + req := httptest.NewRequest("GET", "/admin/api", nil) + resp := httptest.NewRecorder() + + authHandler.ServeHTTP(resp, req) + require.Equal(t, http.StatusUnauthorized, resp.Code) +} + +// TestAdminSecureTokenAuth_ValidToken tests adminSecureTokenAuth with valid token. +func TestAdminSecureTokenAuth_ValidToken(t *testing.T) { + config := Config{Secret: "test-secret"} + handler := &Handler{config: config} + + // Mocked handler that should be invoked + finalHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + }) + + // Generate a valid token + token, err := generateSecureAdminToken("test-secret") + require.NoError(t, err) + + authHandler := handler.adminSecureTokenAuth(finalHandler) + req := httptest.NewRequest("GET", "/admin/api?token="+token, nil) + resp := httptest.NewRecorder() + + authHandler.ServeHTTP(resp, req) + require.Equal(t, http.StatusOK, resp.Code) +} + +// TestAdminSecureTokenAuth_InvalidToken tests adminSecureTokenAuth rejection with invalid token. +func TestAdminSecureTokenAuth_InvalidToken(t *testing.T) { + config := Config{Secret: "test-secret"} + handler := &Handler{config: config} + + // Mocked handler that should not be invoked. + finalHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + require.Fail(t, "final handler should not be invoked") + w.WriteHeader(http.StatusOK) + }) + + authHandler := handler.adminSecureTokenAuth(finalHandler) + req := httptest.NewRequest("GET", "/admin/api?token=invalid-token", nil) + resp := httptest.NewRecorder() + + authHandler.ServeHTTP(resp, req) + require.Equal(t, http.StatusUnauthorized, resp.Code) +} diff --git a/internal/wt/handler.go b/internal/wt/handler.go index cee678085..08577161a 100644 --- a/internal/wt/handler.go +++ b/internal/wt/handler.go @@ -67,10 +67,10 @@ func (s *Handler) ServeHTTP(rw http.ResponseWriter, r *http.Request) { var decoder protocol.StreamCommandDecoder if protoType == centrifuge.ProtocolTypeJSON { - decoder = protocol.GetStreamCommandDecoder(protocol.TypeJSON, stream, s.config.MessageSizeLimit) + decoder = protocol.GetStreamCommandDecoderLimited(protocol.TypeJSON, stream, int64(s.config.MessageSizeLimit)) defer protocol.PutStreamCommandDecoder(protocol.TypeJSON, decoder) } else { - decoder = protocol.NewProtobufStreamCommandDecoder(stream, s.config.MessageSizeLimit) + decoder = protocol.NewProtobufStreamCommandDecoder(stream, int64(s.config.MessageSizeLimit)) defer protocol.PutStreamCommandDecoder(protocol.TypeProtobuf, decoder) }