2
Files

135 lines
4.8 KiB
Go

package opnsense
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"sync"
"testing"
"time"
"git.dern.ovh/infrastructure/caddy-opnsense-blocker/internal/config"
)
func TestClientCreatesAliasAndBlocksAndUnblocksIPs(t *testing.T) {
t.Parallel()
type state struct {
mu sync.Mutex
aliasUUID string
aliasExists bool
ips map[string]struct{}
}
backendState := &state{ips: map[string]struct{}{}}
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
username, password, ok := r.BasicAuth()
if !ok || username != "key" || password != "secret" {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
w.Header().Set("Content-Type", "application/json")
backendState.mu.Lock()
defer backendState.mu.Unlock()
switch {
case r.Method == http.MethodGet && r.URL.Path == "/api/firewall/alias/get_alias_u_u_i_d/blocked-ips":
if backendState.aliasExists {
_ = json.NewEncoder(w).Encode(map[string]any{"uuid": backendState.aliasUUID})
} else {
_ = json.NewEncoder(w).Encode(map[string]any{"uuid": ""})
}
case r.Method == http.MethodPost && r.URL.Path == "/api/firewall/alias/add_item":
backendState.aliasExists = true
backendState.aliasUUID = "uuid-1"
_ = json.NewEncoder(w).Encode(map[string]any{"status": "ok"})
case r.Method == http.MethodPost && r.URL.Path == "/api/firewall/alias/set_item/uuid-1":
_ = json.NewEncoder(w).Encode(map[string]any{"status": "ok"})
case r.Method == http.MethodPost && r.URL.Path == "/api/firewall/alias/reconfigure":
_ = json.NewEncoder(w).Encode(map[string]any{"status": "ok"})
case r.Method == http.MethodGet && r.URL.Path == "/api/firewall/alias_util/list/blocked-ips":
rows := make([]map[string]string, 0, len(backendState.ips))
for ip := range backendState.ips {
rows = append(rows, map[string]string{"ip": ip})
}
_ = json.NewEncoder(w).Encode(map[string]any{"rows": rows})
case r.Method == http.MethodPost && r.URL.Path == "/api/firewall/alias_util/add/blocked-ips":
var payload map[string]string
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
backendState.ips[payload["address"]] = struct{}{}
_ = json.NewEncoder(w).Encode(map[string]any{"status": "done"})
case r.Method == http.MethodPost && r.URL.Path == "/api/firewall/alias_util/delete/blocked-ips":
var payload map[string]string
if err := json.NewDecoder(r.Body).Decode(&payload); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
delete(backendState.ips, payload["address"])
_ = json.NewEncoder(w).Encode(map[string]any{"status": "done"})
default:
http.Error(w, "not found", http.StatusNotFound)
}
}))
defer server.Close()
client := NewClient(config.OPNsenseConfig{
Enabled: true,
BaseURL: server.URL,
APIKey: "key",
APISecret: "secret",
EnsureAlias: true,
Timeout: config.Duration{Duration: time.Second},
Alias: config.AliasConfig{
Name: "blocked-ips",
Type: "host",
},
APIPaths: config.APIPathsConfig{
AliasGetUUID: "/api/firewall/alias/get_alias_u_u_i_d/{alias}",
AliasAddItem: "/api/firewall/alias/add_item",
AliasSetItem: "/api/firewall/alias/set_item/{uuid}",
AliasReconfig: "/api/firewall/alias/reconfigure",
AliasUtilList: "/api/firewall/alias_util/list/{alias}",
AliasUtilAdd: "/api/firewall/alias_util/add/{alias}",
AliasUtilDelete: "/api/firewall/alias_util/delete/{alias}",
},
})
ctx := context.Background()
if result, err := client.AddIPIfMissing(ctx, "203.0.113.10"); err != nil || result != "added" {
t.Fatalf("unexpected add result: result=%q err=%v", result, err)
}
if result, err := client.AddIPIfMissing(ctx, "203.0.113.10"); err != nil || result != "already_present" {
t.Fatalf("unexpected add replay result: result=%q err=%v", result, err)
}
present, err := client.IsIPPresent(ctx, "203.0.113.10")
if err != nil {
t.Fatalf("is ip present: %v", err)
}
if !present {
t.Fatalf("expected IP to be present in alias")
}
if result, err := client.RemoveIPIfPresent(ctx, "203.0.113.10"); err != nil || result != "removed" {
t.Fatalf("unexpected remove result: result=%q err=%v", result, err)
}
if result, err := client.RemoveIPIfPresent(ctx, "203.0.113.10"); err != nil || result != "already_absent" {
t.Fatalf("unexpected remove replay result: result=%q err=%v", result, err)
}
backendState.mu.Lock()
defer backendState.mu.Unlock()
if !backendState.aliasExists || backendState.aliasUUID == "" {
t.Fatalf("expected alias to exist after first add")
}
if len(backendState.ips) != 0 {
t.Fatalf("expected alias to be empty after remove, got %v", backendState.ips)
}
if strings.TrimSpace(backendState.aliasUUID) == "" {
t.Fatalf("expected alias uuid to be populated")
}
}