208 lines
5.0 KiB
Go
208 lines
5.0 KiB
Go
|
|
package handlers
|
||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"net/http"
|
||
|
|
"time"
|
||
|
|
|
||
|
|
"crowdsec-dashy/internal/crowdsec"
|
||
|
|
)
|
||
|
|
|
||
|
|
// HubHandler manages the hub page (collections, parsers, scenarios, postoverflows).
|
||
|
|
type HubHandler struct {
|
||
|
|
deps Deps
|
||
|
|
}
|
||
|
|
|
||
|
|
func NewHubHandler(deps Deps) *HubHandler {
|
||
|
|
return &HubHandler{deps: deps}
|
||
|
|
}
|
||
|
|
|
||
|
|
// HubData is passed to the hub template.
|
||
|
|
type HubData struct {
|
||
|
|
PageData
|
||
|
|
Tab string
|
||
|
|
Items []crowdsec.HubItem // current tab's items
|
||
|
|
}
|
||
|
|
|
||
|
|
// List renders the hub page for the active tab.
|
||
|
|
func (h *HubHandler) List(w http.ResponseWriter, r *http.Request) {
|
||
|
|
pd := NewPageData(r, "Hub", h.deps.CLIAvailable, h.deps.PollInterval)
|
||
|
|
if f := readFlash(r); f.Message != "" {
|
||
|
|
pd.Flash = f
|
||
|
|
}
|
||
|
|
|
||
|
|
tab := sanitizeTab(r.URL.Query().Get("tab"))
|
||
|
|
data := HubData{PageData: pd, Tab: tab}
|
||
|
|
|
||
|
|
if h.deps.CLIAvailable {
|
||
|
|
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
var err error
|
||
|
|
switch tab {
|
||
|
|
case "collections":
|
||
|
|
data.Items, err = h.deps.CLI.ListCollections(ctx)
|
||
|
|
case "parsers":
|
||
|
|
data.Items, err = h.deps.CLI.ListParsers(ctx)
|
||
|
|
case "scenarios":
|
||
|
|
data.Items, err = h.deps.CLI.ListScenarios(ctx)
|
||
|
|
case "postoverflows":
|
||
|
|
data.Items, err = h.deps.CLI.ListPostoverflows(ctx)
|
||
|
|
}
|
||
|
|
if err != nil {
|
||
|
|
data.PageData.Flash = FlashMessage{Type: "error", Message: fmt.Sprintf("cscli error: %v", err)}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
h.deps.Renderer.Render(w, "hub", data)
|
||
|
|
}
|
||
|
|
|
||
|
|
// Install installs a hub item.
|
||
|
|
func (h *HubHandler) Install(w http.ResponseWriter, r *http.Request) {
|
||
|
|
r.Body = http.MaxBytesReader(w, r.Body, 4096)
|
||
|
|
if err := r.ParseForm(); err != nil {
|
||
|
|
flashRedirect(w, r, "/hub", "error", "invalid form data")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if !checkCSRF(r) {
|
||
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if !h.deps.CLIAvailable {
|
||
|
|
flashRedirect(w, r, "/hub", "error", "cscli not available")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
kind := r.FormValue("kind")
|
||
|
|
name := r.FormValue("name")
|
||
|
|
tab := sanitizeTab(r.FormValue("tab"))
|
||
|
|
|
||
|
|
if err := validateHubKind(kind); err != nil {
|
||
|
|
flashRedirect(w, r, hubURL(tab), "error", err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(r.Context(), 120*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
var err error
|
||
|
|
switch kind {
|
||
|
|
case "collections":
|
||
|
|
err = h.deps.CLI.InstallCollection(ctx, name)
|
||
|
|
case "parsers":
|
||
|
|
err = h.deps.CLI.InstallParser(ctx, name)
|
||
|
|
case "scenarios":
|
||
|
|
err = h.deps.CLI.InstallScenario(ctx, name)
|
||
|
|
default:
|
||
|
|
flashRedirect(w, r, hubURL(tab), "error", "unsupported hub type")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
flashRedirect(w, r, hubURL(tab), "error", fmt.Sprintf("install failed: %v", err))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
flashRedirect(w, r, hubURL(tab), "success", fmt.Sprintf("Installed %s", name))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Remove uninstalls a hub item.
|
||
|
|
func (h *HubHandler) Remove(w http.ResponseWriter, r *http.Request) {
|
||
|
|
r.Body = http.MaxBytesReader(w, r.Body, 4096)
|
||
|
|
if err := r.ParseForm(); err != nil {
|
||
|
|
flashRedirect(w, r, "/hub", "error", "invalid form data")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if !checkCSRF(r) {
|
||
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if !h.deps.CLIAvailable {
|
||
|
|
flashRedirect(w, r, "/hub", "error", "cscli not available")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
kind := r.FormValue("kind")
|
||
|
|
name := r.FormValue("name")
|
||
|
|
tab := sanitizeTab(r.FormValue("tab"))
|
||
|
|
|
||
|
|
if err := validateHubKind(kind); err != nil {
|
||
|
|
flashRedirect(w, r, hubURL(tab), "error", err.Error())
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(r.Context(), 120*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
var err error
|
||
|
|
switch kind {
|
||
|
|
case "collections":
|
||
|
|
err = h.deps.CLI.RemoveCollection(ctx, name)
|
||
|
|
case "parsers":
|
||
|
|
err = h.deps.CLI.RemoveParser(ctx, name)
|
||
|
|
case "scenarios":
|
||
|
|
err = h.deps.CLI.RemoveScenario(ctx, name)
|
||
|
|
default:
|
||
|
|
flashRedirect(w, r, hubURL(tab), "error", "unsupported hub type")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
flashRedirect(w, r, hubURL(tab), "error", fmt.Sprintf("remove failed: %v", err))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
flashRedirect(w, r, hubURL(tab), "success", fmt.Sprintf("Removed %s", name))
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update runs cscli hub update + upgrade.
|
||
|
|
func (h *HubHandler) Update(w http.ResponseWriter, r *http.Request) {
|
||
|
|
r.Body = http.MaxBytesReader(w, r.Body, 1024)
|
||
|
|
if err := r.ParseForm(); err != nil {
|
||
|
|
flashRedirect(w, r, "/hub", "error", "invalid form data")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
if !checkCSRF(r) {
|
||
|
|
http.Error(w, "forbidden", http.StatusForbidden)
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if !h.deps.CLIAvailable {
|
||
|
|
flashRedirect(w, r, "/hub", "error", "cscli not available")
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
ctx, cancel := context.WithTimeout(r.Context(), 180*time.Second)
|
||
|
|
defer cancel()
|
||
|
|
|
||
|
|
if err := h.deps.CLI.HubUpdate(ctx); err != nil {
|
||
|
|
flashRedirect(w, r, "/hub", "error", fmt.Sprintf("hub update failed: %v", err))
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
flashRedirect(w, r, "/hub", "success", "Hub updated and upgraded successfully")
|
||
|
|
}
|
||
|
|
|
||
|
|
func sanitizeTab(tab string) string {
|
||
|
|
switch tab {
|
||
|
|
case "collections", "parsers", "scenarios", "postoverflows":
|
||
|
|
return tab
|
||
|
|
}
|
||
|
|
return "collections"
|
||
|
|
}
|
||
|
|
|
||
|
|
func hubURL(tab string) string {
|
||
|
|
return "/hub?tab=" + tab
|
||
|
|
}
|
||
|
|
|
||
|
|
func validateHubKind(kind string) error {
|
||
|
|
switch kind {
|
||
|
|
case "collections", "parsers", "scenarios", "postoverflows":
|
||
|
|
return nil
|
||
|
|
}
|
||
|
|
return fmt.Errorf("invalid hub kind: %q", kind)
|
||
|
|
}
|