From ba46493a7b37e2e312d944e04555e82d6acb4185 Mon Sep 17 00:00:00 2001 From: Stavros Date: Wed, 3 Sep 2025 15:30:24 +0300 Subject: [PATCH] tests: add proxy controller tests --- internal/controller/proxy_controller.go | 9 + internal/controller/proxy_controller_test.go | 164 +++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 internal/controller/proxy_controller_test.go diff --git a/internal/controller/proxy_controller.go b/internal/controller/proxy_controller.go index 88eeb4d..dbf13b9 100644 --- a/internal/controller/proxy_controller.go +++ b/internal/controller/proxy_controller.go @@ -55,6 +55,15 @@ func (controller *ProxyController) proxyHandler(c *gin.Context) { return } + if req.Proxy != "nginx" && req.Proxy != "traefik" && req.Proxy != "caddy" { + log.Warn().Str("proxy", req.Proxy).Msg("Invalid proxy") + c.JSON(400, gin.H{ + "status": 400, + "message": "Bad Request", + }) + return + } + isBrowser := strings.Contains(c.Request.Header.Get("Accept"), "text/html") if isBrowser { diff --git a/internal/controller/proxy_controller_test.go b/internal/controller/proxy_controller_test.go new file mode 100644 index 0000000..718eb0d --- /dev/null +++ b/internal/controller/proxy_controller_test.go @@ -0,0 +1,164 @@ +package controller_test + +import ( + "net/http/httptest" + "testing" + "tinyauth/internal/config" + "tinyauth/internal/controller" + "tinyauth/internal/service" + + "github.com/gin-gonic/gin" + "gotest.tools/v3/assert" +) + +func setupProxyController(t *testing.T, middlewares *[]gin.HandlerFunc) (*gin.Engine, *httptest.ResponseRecorder, *service.AuthService) { + // Setup + gin.SetMode(gin.TestMode) + router := gin.Default() + + if middlewares != nil { + for _, m := range *middlewares { + router.Use(m) + } + } + + group := router.Group("/api") + recorder := httptest.NewRecorder() + + // Database + databaseService := service.NewDatabaseService(service.DatabaseServiceConfig{ + DatabasePath: "/tmp/tinyauth_test.db", + }) + + assert.NilError(t, databaseService.Init()) + + database := databaseService.GetDatabase() + + // Docker + dockerService := service.NewDockerService() + + assert.NilError(t, dockerService.Init()) + + // Auth service + authService := service.NewAuthService(service.AuthServiceConfig{ + Users: []config.User{ + { + Username: "testuser", + Password: "$2a$10$ne6z693sTgzT3ePoQ05PgOecUHnBjM7sSNj6M.l5CLUP.f6NyCnt.", // test + }, + }, + OauthWhitelist: "", + SessionExpiry: 3600, + SecureCookie: false, + RootDomain: "localhost", + LoginTimeout: 300, + LoginMaxRetries: 3, + SessionCookieName: "tinyauth-session", + }, dockerService, nil, database) + + // Controller + ctrl := controller.NewProxyController(controller.ProxyControllerConfig{ + AppURL: "http://localhost:8080", + }, group, dockerService, authService) + ctrl.SetupRoutes() + + return router, recorder, authService +} + +func TestProxyHandler(t *testing.T) { + // Setup + router, recorder, authService := setupProxyController(t, nil) + + // Test invalid proxy + req := httptest.NewRequest("GET", "/api/auth/invalidproxy", nil) + router.ServeHTTP(recorder, req) + + assert.Equal(t, 400, recorder.Code) + + // Test logged out user (traefik/caddy) + recorder = httptest.NewRecorder() + req = httptest.NewRequest("GET", "/api/auth/traefik", nil) + req.Header.Set("X-Forwarded-Proto", "https") + req.Header.Set("X-Forwarded-Host", "example.com") + req.Header.Set("X-Forwarded-Uri", "/somepath") + req.Header.Set("Accept", "text/html") + router.ServeHTTP(recorder, req) + + assert.Equal(t, 307, recorder.Code) + assert.Equal(t, "http://localhost:8080/login?redirect_uri=https%3A%2F%2Fexample.com%2Fsomepath", recorder.Header().Get("Location")) + + // Test logged out user (nginx) + recorder = httptest.NewRecorder() + req = httptest.NewRequest("GET", "/api/auth/nginx", nil) + router.ServeHTTP(recorder, req) + + assert.Equal(t, 401, recorder.Code) + + // Test logged in user + c := gin.CreateTestContextOnly(recorder, router) + + err := authService.CreateSessionCookie(c, &config.SessionCookie{ + Username: "testuser", + Name: "testuser", + Email: "testuser@example.com", + Provider: "username", + TotpPending: false, + OAuthGroups: "", + }) + + assert.NilError(t, err) + + cookie := c.Writer.Header().Get("Set-Cookie") + + router, recorder, _ = setupProxyController(t, &[]gin.HandlerFunc{ + func(c *gin.Context) { + c.Set("context", &config.UserContext{ + Username: "testuser", + Name: "testuser", + Email: "testuser@example.com", + IsLoggedIn: true, + OAuth: false, + Provider: "username", + TotpPending: false, + OAuthGroups: "", + TotpEnabled: false, + }) + c.Next() + }, + }) + + req = httptest.NewRequest("GET", "/api/auth/traefik", nil) + req.Header.Set("Cookie", cookie) + req.Header.Set("Accept", "text/html") + router.ServeHTTP(recorder, req) + + assert.Equal(t, 200, recorder.Code) + + assert.Equal(t, "testuser", recorder.Header().Get("Remote-User")) + assert.Equal(t, "testuser", recorder.Header().Get("Remote-Name")) + assert.Equal(t, "testuser@example.com", recorder.Header().Get("Remote-Email")) + + // Ensure basic auth is disabled for TOTP enabled users + router, recorder, _ = setupProxyController(t, &[]gin.HandlerFunc{ + func(c *gin.Context) { + c.Set("context", &config.UserContext{ + Username: "testuser", + Name: "testuser", + Email: "testuser@example.com", + IsLoggedIn: true, + OAuth: false, + Provider: "basic", + TotpPending: false, + OAuthGroups: "", + TotpEnabled: true, + }) + c.Next() + }, + }) + + req = httptest.NewRequest("GET", "/api/auth/traefik", nil) + req.SetBasicAuth("testuser", "test") + router.ServeHTTP(recorder, req) + + assert.Equal(t, 401, recorder.Code) +}