feat: add session-based authentication to the web interface and API routes
Build and Push / build (nucleus, amd64, linux) (push) Successful in 16s

This commit is contained in:
2026-07-17 01:33:26 +01:00
parent b5a7cfc776
commit 8a35a5b791
9 changed files with 245 additions and 10 deletions
+131
View File
@@ -0,0 +1,131 @@
package api
import (
"crypto/rand"
"encoding/hex"
"encoding/json"
"net/http"
"os"
"sync"
"time"
)
var (
currentSession string
sessionMu sync.Mutex
)
func generateSession() string {
b := make([]byte, 32)
rand.Read(b)
return hex.EncodeToString(b)
}
func AuthMiddleware(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user := os.Getenv("WEB_USER")
pass := os.Getenv("WEB_PASS")
if user == "" || pass == "" {
next(w, r)
return
}
cookie, err := r.Cookie("nucleus_session")
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
sessionMu.Lock()
valid := currentSession != "" && currentSession == cookie.Value
sessionMu.Unlock()
if !valid {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next(w, r)
}
}
func LoginHandler(w http.ResponseWriter, r *http.Request) {
user := os.Getenv("WEB_USER")
pass := os.Getenv("WEB_PASS")
if user == "" || pass == "" {
w.WriteHeader(http.StatusOK)
return
}
var creds struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := json.NewDecoder(r.Body).Decode(&creds); err != nil {
http.Error(w, "Bad Request", http.StatusBadRequest)
return
}
if creds.Username != user || creds.Password != pass {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
sessionMu.Lock()
currentSession = generateSession()
token := currentSession
sessionMu.Unlock()
http.SetCookie(w, &http.Cookie{
Name: "nucleus_session",
Value: token,
Path: "/",
Expires: time.Now().Add(24 * time.Hour),
HttpOnly: true,
})
w.WriteHeader(http.StatusOK)
}
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
sessionMu.Lock()
currentSession = ""
sessionMu.Unlock()
http.SetCookie(w, &http.Cookie{
Name: "nucleus_session",
Value: "",
Path: "/",
Expires: time.Now().Add(-1 * time.Hour),
HttpOnly: true,
})
w.WriteHeader(http.StatusOK)
}
func StatusHandler(w http.ResponseWriter, r *http.Request) {
user := os.Getenv("WEB_USER")
pass := os.Getenv("WEB_PASS")
if user == "" || pass == "" {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(`{"auth_required": false, "authenticated": true}`))
return
}
w.Header().Set("Content-Type", "application/json")
cookie, err := r.Cookie("nucleus_session")
if err != nil {
w.Write([]byte(`{"auth_required": true, "authenticated": false}`))
return
}
sessionMu.Lock()
valid := currentSession != "" && currentSession == cookie.Value
sessionMu.Unlock()
if valid {
w.Write([]byte(`{"auth_required": true, "authenticated": true}`))
} else {
w.Write([]byte(`{"auth_required": true, "authenticated": false}`))
}
}
+10 -6
View File
@@ -12,12 +12,16 @@ import (
)
func RegisterRoutes(mux *http.ServeMux) {
mux.HandleFunc("GET /api/targets", getTargets)
mux.HandleFunc("POST /api/targets", createTarget)
mux.HandleFunc("DELETE /api/targets/{id}", deleteTarget)
mux.HandleFunc("POST /api/targets/{id}/scan", triggerScan)
mux.HandleFunc("GET /api/scans", getScans)
mux.HandleFunc("GET /api/scans/{id}/findings", getFindings)
mux.HandleFunc("POST /api/auth/login", LoginHandler)
mux.HandleFunc("POST /api/auth/logout", LogoutHandler)
mux.HandleFunc("GET /api/auth/status", StatusHandler)
mux.HandleFunc("GET /api/targets", AuthMiddleware(getTargets))
mux.HandleFunc("POST /api/targets", AuthMiddleware(createTarget))
mux.HandleFunc("DELETE /api/targets/{id}", AuthMiddleware(deleteTarget))
mux.HandleFunc("POST /api/targets/{id}/scan", AuthMiddleware(triggerScan))
mux.HandleFunc("GET /api/scans", AuthMiddleware(getScans))
mux.HandleFunc("GET /api/scans/{id}/findings", AuthMiddleware(getFindings))
}
func getTargets(w http.ResponseWriter, r *http.Request) {