- 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
53 lines
1.4 KiB
Go
53 lines
1.4 KiB
Go
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)
|
|
})
|
|
}
|
|
}
|