package middleware import ( "bytes" "net/http" "net/http/httptest" "testing" "time" "github.com/alicebob/miniredis/v2" "github.com/gin-gonic/gin" goredis "github.com/redis/go-redis/v9" "github.com/stretchr/testify/require" "go.uber.org/zap" "go.uber.org/zap/zapcore" sharedauth "github.com/geo-platform/tenant-api/internal/shared/auth" ) func TestLoggerIncludesDetailedAuthFailureContext(t *testing.T) { t.Parallel() gin.SetMode(gin.TestMode) var logBuffer bytes.Buffer encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.TimeKey = "" logger := zap.New(zapcore.NewCore( zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&logBuffer), zapcore.DebugLevel, )) mr := miniredis.RunT(t) rdb := goredis.NewClient(&goredis.Options{Addr: mr.Addr()}) mgr := sharedauth.NewManager("logger-test-secret", -1*time.Minute, 7*24*time.Hour) sessions := sharedauth.NewSessionStore(rdb) pair, err := mgr.Issue(7, 42, 420, "editor") require.NoError(t, err) router := gin.New() router.Use(RequestID()) router.Use(Logger(logger)) router.Use(sharedauth.Middleware(mgr, sessions)) router.GET("/test", func(c *gin.Context) { c.Status(http.StatusOK) }) req := httptest.NewRequest(http.MethodGet, "/test?tab=cards", nil) req.Header.Set("Authorization", "Bearer "+pair.AccessToken) req.Header.Set("User-Agent", "geo-rankly-test/1.0") resp := httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusUnauthorized, resp.Code) logLine := logBuffer.String() t.Log(logLine) require.Contains(t, logLine, `"msg":"request"`) require.Contains(t, logLine, `"status":401`) require.Contains(t, logLine, `"route":"/test"`) require.Contains(t, logLine, `"query":"tab=cards"`) require.Contains(t, logLine, `"user_agent":"geo-rankly-test/1.0"`) require.Contains(t, logLine, `"app_message":"invalid_access_token"`) require.Contains(t, logLine, `"auth_header_present":true`) require.Contains(t, logLine, `"auth_scheme":"Bearer"`) require.Contains(t, logLine, `"auth_failure_stage":"parse_access_token"`) require.Contains(t, logLine, `"auth_failure_reason":"expired"`) require.Contains(t, logLine, `"auth_parse_error":"token has invalid claims: token is expired"`) require.Contains(t, logLine, `"auth_token_fingerprint":"`) } func TestLoggerUsesForwardedClientIPFromTrustedProxy(t *testing.T) { t.Parallel() gin.SetMode(gin.TestMode) var logBuffer bytes.Buffer encoderConfig := zap.NewProductionEncoderConfig() encoderConfig.TimeKey = "" logger := zap.New(zapcore.NewCore( zapcore.NewJSONEncoder(encoderConfig), zapcore.AddSync(&logBuffer), zapcore.DebugLevel, )) router := gin.New() require.NoError(t, router.SetTrustedProxies([]string{"10.42.0.0/16"})) router.Use(RequestID()) router.Use(Logger(logger)) router.GET("/test", func(c *gin.Context) { c.Status(http.StatusOK) }) req := httptest.NewRequest(http.MethodGet, "/test", nil) req.RemoteAddr = "10.42.0.128:51234" req.Header.Set("X-Forwarded-For", "106.14.89.27") resp := httptest.NewRecorder() router.ServeHTTP(resp, req) require.Equal(t, http.StatusOK, resp.Code) require.Contains(t, logBuffer.String(), `"client_ip":"106.14.89.27"`) }