118 lines
3.0 KiB
Go
118 lines
3.0 KiB
Go
|
|
package handlers
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"net/http"
|
||
|
|
"regexp"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"crowdsec-dashy/internal/crowdsec"
|
||
|
|
)
|
||
|
|
|
||
|
|
// MachinesHandler manages the machines page and its POST actions.
|
||
|
|
type MachinesHandler struct {
|
||
|
|
deps Deps
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewMachinesHandler(deps Deps) *MachinesHandler {
|
||
|
|
return &MachinesHandler{deps: deps}
|
||
|
|
}
|
||
|
|
|
||
|
|
// MachinesData is passed to the machines template.
|
||
|
|
type MachinesData struct {
|
||
|
|
PageData
|
||
|
|
Machines []crowdsec.Machine
|
||
|
|
}
|
||
|
|
|
||
|
|
var validMachineID = regexp.MustCompile(`^[a-zA-Z0-9_.\-]{1,128}$`)
|
||
|
|
|
||
|
|
// List renders the machines list.
|
||
|
|
func (h *MachinesHandler) List(w http.ResponseWriter, r *http.Request) {
|
||
|
|
pd := NewPageData(r, "Machines", h.deps.CLIAvailable, h.deps.PollInterval)
|
||
|
|
if f := readFlash(r); f.Message != "" {
|
||
|
|
pd.Flash = f
|
||
|
|
}
|
||
|
|
|
||
|
|
var machines []crowdsec.Machine
|
||
|
|
if h.deps.CLIAvailable {
|
||
|
|
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
var err error
|
||
|
|
machines, err = h.deps.CLI.ListMachines(ctx)
|
||
|
|
if err != nil {
|
||
|
|
pd.Flash = FlashMessage{Type: "error", Message: fmt.Sprintf("cscli error: %v", err)}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
h.deps.Renderer.Render(w, "machines", MachinesData{PageData: pd, Machines: machines})
|
||
|
|
}
|
||
|
|
|
||
|
|
// Delete removes a machine by ID.
|
||
|
|
func (h *MachinesHandler) Delete(w http.ResponseWriter, r *http.Request) {
|
||
|
|
r.Body = http.MaxBytesReader(w, r.Body, 4096)
|
||
|
|
if err := r.ParseForm(); err != nil {
|
||
|
|
flashRedirect(w, r, "/machines", "error", "invalid form data")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if !checkCSRF(r) {
|
||
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if !h.deps.CLIAvailable {
|
||
|
|
flashRedirect(w, r, "/machines", "error", "cscli not available")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
id := r.FormValue("id")
|
||
|
|
if !validMachineID.MatchString(id) {
|
||
|
|
flashRedirect(w, r, "/machines", "error", "invalid machine id")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
if err := h.deps.CLI.DeleteMachine(ctx, id); err != nil {
|
||
|
|
flashRedirect(w, r, "/machines", "error", fmt.Sprintf("failed to delete machine: %v", err))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
flashRedirect(w, r, "/machines", "success", fmt.Sprintf("Machine %q deleted", id))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Validate approves a pending machine registration.
|
||
|
|
func (h *MachinesHandler) Validate(w http.ResponseWriter, r *http.Request) {
|
||
|
|
r.Body = http.MaxBytesReader(w, r.Body, 4096)
|
||
|
|
if err := r.ParseForm(); err != nil {
|
||
|
|
flashRedirect(w, r, "/machines", "error", "invalid form data")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if !checkCSRF(r) {
|
||
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if !h.deps.CLIAvailable {
|
||
|
|
flashRedirect(w, r, "/machines", "error", "cscli not available")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
id := r.FormValue("id")
|
||
|
|
if !validMachineID.MatchString(id) {
|
||
|
|
flashRedirect(w, r, "/machines", "error", "invalid machine id")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
if err := h.deps.CLI.ValidateMachine(ctx, id); err != nil {
|
||
|
|
flashRedirect(w, r, "/machines", "error", fmt.Sprintf("failed to validate machine: %v", err))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
flashRedirect(w, r, "/machines", "success", fmt.Sprintf("Machine %q validated", id))
|
||
|
|
}
|