function update
This commit is contained in:
@@ -2,6 +2,8 @@ package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -20,8 +22,8 @@ func NewAllowlistHandler(deps Deps) *AllowlistHandler {
|
||||
|
||||
type AllowlistData struct {
|
||||
PageData
|
||||
Lists []crowdsec.Allowlist
|
||||
FetchErr string
|
||||
Lists []crowdsec.Allowlist
|
||||
FetchErr string
|
||||
}
|
||||
|
||||
func (h *AllowlistHandler) List(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -54,8 +56,38 @@ func (h *AllowlistHandler) List(w http.ResponseWriter, r *http.Request) {
|
||||
})
|
||||
}
|
||||
|
||||
// CreateList creates a new named allowlist.
|
||||
func (h *AllowlistHandler) CreateList(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, 1024)
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if !checkCSRF(r) {
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(r.FormValue("name"))
|
||||
if name == "" {
|
||||
flashRedirect(w, r, "/allowlist", "error", "list name is required")
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := h.deps.CLI.CreateAllowlist(ctx, name); err != nil {
|
||||
flashRedirect(w, r, "/allowlist", "error", "create failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
flashRedirect(w, r, "/allowlist", "success", "Allowlist "+name+" created")
|
||||
}
|
||||
|
||||
// AddEntry adds one or more IPs/CIDRs to an allowlist. Auto-creates the list if missing.
|
||||
func (h *AllowlistHandler) AddEntry(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, 4096)
|
||||
r.Body = http.MaxBytesReader(w, r.Body, 16384)
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "bad request", http.StatusBadRequest)
|
||||
return
|
||||
@@ -66,22 +98,65 @@ func (h *AllowlistHandler) AddEntry(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
listName := strings.TrimSpace(r.FormValue("list"))
|
||||
value := strings.TrimSpace(r.FormValue("value"))
|
||||
raw := r.FormValue("value")
|
||||
|
||||
if listName == "" || value == "" {
|
||||
flashRedirect(w, r, "/allowlist", "error", "list name and value are required")
|
||||
if listName == "" {
|
||||
flashRedirect(w, r, "/allowlist", "error", "list name is required")
|
||||
return
|
||||
}
|
||||
|
||||
comment := strings.TrimSpace(r.FormValue("comment"))
|
||||
values, err := parseAllowlistValues(raw)
|
||||
if err != nil {
|
||||
flashRedirect(w, r, "/allowlist", "error", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
// Auto-create the list — CreateAllowlist is a no-op if it already exists.
|
||||
if err := h.deps.CLI.CreateAllowlist(ctx, listName); err != nil {
|
||||
flashRedirect(w, r, "/allowlist", "error", "create list failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if err := h.deps.CLI.AddAllowlistEntries(ctx, listName, comment, values); err != nil {
|
||||
flashRedirect(w, r, "/allowlist", "error", "add failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("%d entr%s added to %s", len(values), pluralY(len(values)), listName)
|
||||
flashRedirect(w, r, "/allowlist", "success", msg)
|
||||
}
|
||||
|
||||
// DeleteList removes an entire allowlist.
|
||||
func (h *AllowlistHandler) DeleteList(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, 1024)
|
||||
if err := r.ParseForm(); err != nil {
|
||||
http.Error(w, "bad request", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
if !checkCSRF(r) {
|
||||
http.Error(w, "forbidden", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(r.FormValue("name"))
|
||||
if name == "" {
|
||||
flashRedirect(w, r, "/allowlist", "error", "list name is required")
|
||||
return
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(r.Context(), 15*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if err := h.deps.CLI.AddAllowlistEntry(ctx, listName, value); err != nil {
|
||||
flashRedirect(w, r, "/allowlist", "error", "add failed: "+err.Error())
|
||||
if err := h.deps.CLI.DeleteAllowlist(ctx, name); err != nil {
|
||||
flashRedirect(w, r, "/allowlist", "error", "delete failed: "+err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
flashRedirect(w, r, "/allowlist", "success", value+" added to "+listName)
|
||||
flashRedirect(w, r, "/allowlist", "success", "Allowlist "+name+" deleted")
|
||||
}
|
||||
|
||||
func (h *AllowlistHandler) RemoveEntry(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -113,3 +188,35 @@ func (h *AllowlistHandler) RemoveEntry(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
flashRedirect(w, r, "/allowlist", "success", value+" removed from "+listName)
|
||||
}
|
||||
|
||||
// parseAllowlistValues splits a raw multi-value string (newline/comma/space delimited)
|
||||
// and validates each entry as an IP address or CIDR range.
|
||||
func parseAllowlistValues(raw string) ([]string, error) {
|
||||
fields := strings.FieldsFunc(raw, func(r rune) bool {
|
||||
return r == ',' || r == '\n' || r == '\r' || r == ' ' || r == '\t'
|
||||
})
|
||||
var out []string
|
||||
for _, f := range fields {
|
||||
f = strings.TrimSpace(f)
|
||||
if f == "" {
|
||||
continue
|
||||
}
|
||||
if net.ParseIP(f) == nil {
|
||||
if _, _, err := net.ParseCIDR(f); err != nil {
|
||||
return nil, fmt.Errorf("invalid IP or CIDR: %q", f)
|
||||
}
|
||||
}
|
||||
out = append(out, f)
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return nil, fmt.Errorf("no valid IP addresses or CIDR ranges provided")
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func pluralY(n int) string {
|
||||
if n == 1 {
|
||||
return "y"
|
||||
}
|
||||
return "ies"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user