feat(18-01): implement chi server, auth middleware, overview handler
- Server struct with chi router, embedded template parsing, static file serving - AuthMiddleware supports Basic and Bearer token with constant-time comparison - Overview handler renders stats from providers/recon/storage when available - Nil-safe: works with zero config (shows zeroes, no DB required) - All 7 tests pass
This commit is contained in:
52
pkg/web/auth.go
Normal file
52
pkg/web/auth.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AuthMiddleware returns HTTP middleware that enforces authentication when
|
||||
// at least one of user/pass or token is configured. If all auth fields are
|
||||
// empty, the middleware is a no-op passthrough.
|
||||
//
|
||||
// Supported schemes:
|
||||
// - Bearer <token> — matches the configured token
|
||||
// - Basic <base64> — matches the configured user:pass
|
||||
func AuthMiddleware(user, pass, token string) func(http.Handler) http.Handler {
|
||||
authEnabled := user != "" || token != ""
|
||||
|
||||
return func(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if !authEnabled {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
auth := r.Header.Get("Authorization")
|
||||
|
||||
// Check bearer token first.
|
||||
if token != "" && strings.HasPrefix(auth, "Bearer ") {
|
||||
provided := strings.TrimPrefix(auth, "Bearer ")
|
||||
if subtle.ConstantTimeCompare([]byte(provided), []byte(token)) == 1 {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Check basic auth.
|
||||
if user != "" {
|
||||
u, p, ok := r.BasicAuth()
|
||||
if ok &&
|
||||
subtle.ConstantTimeCompare([]byte(u), []byte(user)) == 1 &&
|
||||
subtle.ConstantTimeCompare([]byte(p), []byte(pass)) == 1 {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="keyhunter"`)
|
||||
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user