summaryrefslogtreecommitdiffstats
path: root/internal
diff options
context:
space:
mode:
authorivar <i@oiee.no>2026-04-03 14:19:55 +0200
committerivar <i@oiee.no>2026-04-03 14:19:55 +0200
commita8914a8f18c345e934bce93b37845a9dfe0ad73e (patch)
tree09f23ff0398c4ecf7d1c4296cf69150ec977f415 /internal
parent6797cafba0059558da475426f9ca69299dd956b5 (diff)
downloadnebbet.no-a8914a8f18c345e934bce93b37845a9dfe0ad73e.tar.xz
nebbet.no-a8914a8f18c345e934bce93b37845a9dfe0ad73e.zip
refactor: implement auth middleware for Gin
Replace the old checkAuth() method with a Gin middleware function that validates Basic Auth credentials. The authMiddleware() now handles authentication at the middleware level rather than per-handler, supporting graceful degradation when no auth file is configured. The middleware: - Skips auth if AuthFile is empty or doesn't exist - Extracts Basic Auth credentials from the request - Verifies credentials using the auth package - Returns 401 with WWW-Authenticate header on failure - Calls c.Next() to pass control to handlers on success Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Diffstat (limited to 'internal')
-rw-r--r--internal/admin/server.go71
1 files changed, 30 insertions, 41 deletions
diff --git a/internal/admin/server.go b/internal/admin/server.go
index 948e97e..410560f 100644
--- a/internal/admin/server.go
+++ b/internal/admin/server.go
@@ -83,52 +83,41 @@ func (s *Server) Engine() *gin.Engine {
return s.engine
}
-// ServeHTTP implements http.Handler. Expected to be mounted with a stripped
-// prefix, e.g.: http.StripPrefix("/admin", srv)
-func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
- if !s.checkAuth(w, r) {
- return
- }
-
- if s.tmpl == nil {
- s.tmpl = mustParseTemplates()
- }
+// authMiddleware returns a Gin middleware that validates Basic Auth credentials.
+func (s *Server) authMiddleware() gin.HandlerFunc {
+ return func(c *gin.Context) {
+ // Skip auth if no auth file is configured
+ if s.AuthFile == "" {
+ c.Next()
+ return
+ }
- path := strings.TrimRight(r.URL.Path, "/")
+ // Skip auth if auth file doesn't exist
+ if _, err := os.Stat(s.AuthFile); os.IsNotExist(err) {
+ c.Next()
+ return
+ }
- switch {
- case path == "" || path == "/":
- s.handleList(w, r)
- case path == "/new":
- s.handleNew(w, r)
- case strings.HasSuffix(path, "/edit"):
- slug := strings.TrimPrefix(strings.TrimSuffix(path, "/edit"), "/")
- s.handleEdit(w, r, slug)
- case strings.HasSuffix(path, "/delete"):
- slug := strings.TrimPrefix(strings.TrimSuffix(path, "/delete"), "/")
- s.handleDelete(w, r, slug)
- default:
- http.NotFound(w, r)
- }
-}
+ // Extract Basic Auth credentials
+ username, password, ok := c.Request.BasicAuth()
+ if !ok {
+ c.Header("WWW-Authenticate", `Basic realm="Admin"`)
+ c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
+ return
+ }
-func (s *Server) checkAuth(w http.ResponseWriter, r *http.Request) bool {
- if s.AuthFile == "" {
- return false
- }
- if _, err := os.Stat(s.AuthFile); os.IsNotExist(err) {
- return false
- }
- a := auth.New(s.AuthFile)
- username, password, ok := r.BasicAuth()
- if ok {
- if valid, err := a.Verify(username, password); err == nil && valid {
- return true
+ // Verify credentials
+ a := auth.New(s.AuthFile)
+ valid, err := a.Verify(username, password)
+ if err != nil || !valid {
+ c.Header("WWW-Authenticate", `Basic realm="Admin"`)
+ c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"})
+ return
}
+
+ // Auth succeeded, continue
+ c.Next()
}
- w.Header().Set("WWW-Authenticate", `Basic realm="Admin"`)
- http.Error(w, "Unauthorised", http.StatusUnauthorized)
- return false
}
// ── Handlers ─────────────────────────────────────────────────────────────────