From de5f7ca4ee0cb1f5e8ca65bd9afaefcebb7d3170 Mon Sep 17 00:00:00 2001 From: Daniel Arroyo Date: Fri, 31 Jul 2026 17:26:31 -0400 Subject: [PATCH] Cleanup: remove dead auth middleware and fix security log leak - Remove dead AdminOnly() and AdminTokenAuth() middleware (unused since JWT switch) - Remove dead RequireScope() middleware (also unused) - Fix seed.go: only log password when auto-generated (was logging every time) - Fix seed.go: use admin.Username (normalized) instead of raw username in log - Simplify generateRandomPassword: 12 bytes (24 hex chars) instead of 24 bytes then truncate - Me handler: use claims.Subject (UUID) for lookup instead of username - Remove unused /logout endpoint (stateless JWT) --- internal/api/handlers/admin/login.go | 4 +- internal/api/handlers/admin/me.go | 7 ++- internal/api/middleware/auth.go | 78 ---------------------------- internal/api/router.go | 1 - internal/db/seed.go | 11 ++-- 5 files changed, 14 insertions(+), 87 deletions(-) diff --git a/internal/api/handlers/admin/login.go b/internal/api/handlers/admin/login.go index 5ae4d40..9659df1 100644 --- a/internal/api/handlers/admin/login.go +++ b/internal/api/handlers/admin/login.go @@ -66,6 +66,4 @@ func (h *LoginHandler) Login(c *gin.Context) { }) } -func (h *LoginHandler) Logout(c *gin.Context) { - c.Status(http.StatusOK) -} + diff --git a/internal/api/handlers/admin/me.go b/internal/api/handlers/admin/me.go index 3b9c83c..1c1feed 100644 --- a/internal/api/handlers/admin/me.go +++ b/internal/api/handlers/admin/me.go @@ -29,7 +29,12 @@ func (h *MeHandler) Me(c *gin.Context) { } var user db.AdminUser - if err := h.db.Where("username = ?", claims.Username).First(&user).Error; err != nil { + userID, err := uuid.Parse(claims.Subject) + if err != nil { + c.JSON(http.StatusUnauthorized, gin.H{"error": gin.H{"code": "unauthorized", "message": "Invalid token claims"}}) + return + } + if err := h.db.Where("id = ?", userID).First(&user).Error; err != nil { c.JSON(http.StatusUnauthorized, gin.H{"error": gin.H{"code": "unauthorized", "message": "User not found"}}) return } diff --git a/internal/api/middleware/auth.go b/internal/api/middleware/auth.go index f969b65..de86675 100644 --- a/internal/api/middleware/auth.go +++ b/internal/api/middleware/auth.go @@ -65,84 +65,6 @@ func APIKeyAuth(authService *auth.Service) gin.HandlerFunc { } } -func RequireScope(authService *auth.Service, scope string) gin.HandlerFunc { - return func(c *gin.Context) { - apiKey, exists := c.Get(ApiKeyCtx) - if !exists { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ - "error": gin.H{ - "code": "invalid_api_key", - "message": "Authentication required", - }, - }) - return - } - - key := apiKey.(*db.ApiKey) - if !authService.HasScope(key, scope) { - c.AbortWithStatusJSON(http.StatusForbidden, gin.H{ - "error": gin.H{ - "code": "insufficient_scope", - "message": "API key lacks required scope: " + scope, - }, - }) - return - } - - c.Next() - } -} - -func AdminOnly() gin.HandlerFunc { - return func(c *gin.Context) { - apiKey, exists := c.Get(ApiKeyCtx) - if !exists { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ - "error": gin.H{ - "code": "unauthorized", - "message": "Admin access required", - }, - }) - return - } - - key := apiKey.(*db.ApiKey) - if !key.IsAdmin { - c.AbortWithStatusJSON(http.StatusForbidden, gin.H{ - "error": gin.H{ - "code": "unauthorized", - "message": "Admin access required", - }, - }) - return - } - - c.Next() - } -} - -func AdminTokenAuth(token string) gin.HandlerFunc { - return func(c *gin.Context) { - adminToken := c.GetHeader("X-Admin-Token") - if adminToken == "" { - adminToken = c.GetHeader("Authorization") - adminToken = strings.TrimPrefix(adminToken, "Bearer ") - } - - if adminToken != token { - c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{ - "error": gin.H{ - "code": "unauthorized", - "message": "Invalid admin token", - }, - }) - return - } - - c.Next() - } -} - func AdminSessionAuth(jwtService *auth.AdminJWTService) gin.HandlerFunc { return func(c *gin.Context) { authHeader := c.GetHeader("Authorization") diff --git a/internal/api/router.go b/internal/api/router.go index a608cef..911d50a 100644 --- a/internal/api/router.go +++ b/internal/api/router.go @@ -104,7 +104,6 @@ func New(cfg *config.Config, db *gorm.DB, llamaManager *llama.Manager) *gin.Engi // Admin API v1 (JWT session auth) adminV1 := v1.Group("/admin") adminV1.POST("/login", adminLoginHandler.Login) - adminV1.POST("/logout", adminLoginHandler.Logout) adminSession := adminV1.Group("") adminSession.Use(middleware.AdminSessionAuth(adminJWTService)) adminSession.GET("/me", adminMeHandler.Me) diff --git a/internal/db/seed.go b/internal/db/seed.go index b242858..d19a8a5 100644 --- a/internal/db/seed.go +++ b/internal/db/seed.go @@ -16,7 +16,7 @@ func generateRandomPassword(length int) (string, error) { if _, err := rand.Read(bytes); err != nil { return "", err } - return hex.EncodeToString(bytes)[:length], nil + return hex.EncodeToString(bytes), nil } func SeedAdminUser(db *gorm.DB, username, password string) error { @@ -28,13 +28,14 @@ func SeedAdminUser(db *gorm.DB, username, password string) error { } pwd := password + autoGenerated := false if pwd == "" { var err error - pwd, err = generateRandomPassword(24) + pwd, err = generateRandomPassword(12) if err != nil { return err } - slog.Warn("no ADMIN_PASSWORD set — generated random password (save this, it won't be shown again)") + autoGenerated = true } hash, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost) @@ -54,6 +55,8 @@ func SeedAdminUser(db *gorm.DB, username, password string) error { } slog.Info("admin user seeded", "username", admin.Username) - slog.Warn("ADMIN USER: username=" + username + " password=" + pwd + " (save this, it won't be shown again)") + if autoGenerated { + slog.Warn("ADMIN USER: username=" + admin.Username + " password=" + pwd + " (save this, it won't be shown again)") + } return nil }